quramy / prisma-fabbrica Goto Github PK
View Code? Open in Web Editor NEWPrisma generator to define model factory
License: MIT License
Prisma generator to define model factory
License: MIT License
The following methods are useful to create has-many relation.
createList
buildList
Describe the bug
A clear and concise description of what the bug is.
To Reproduce
Steps to reproduce the behavior:
Expected behavior
definePostFactory
should accept UserFactory for author field.
Your Schema
model User {
id Int @id
posts Post[]
}
model Post {
id Int @id
author User? @relation(fields: [authorId], references: [id])
authorId Int?
}
Environment (please complete the following information):
@prisma/client
version [e.g. 4.2]Additional context
Add any other context about the problem here.
Please put gist URL including generated .js and .d.ts if you can 🙏
Describe the bug
If defining nullable column in schema.prisma, fabbrica generator fails to transpile.
To Reproduce
Rewrite example-prj's schema.prisma in this repository as like followed by example.
generator client {
provider = "prisma-client-js"
previewFeatures = ["interactiveTransactions"]
}
generator fabbrica {
provider = "prisma-fabbrica"
}
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
model User {
id String @id
name String?
posts Post[]
}
model Post {
id String @id
title String
author User @relation(fields: [authorId], references: [id])
authorId String
}
and npx prisma migrate dev
Expected behavior
Generate fabbrica codes.
Your Schema
generator client {
provider = "prisma-client-js"
previewFeatures = ["interactiveTransactions"]
}
generator fabbrica {
provider = "prisma-fabbrica"
}
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
model User {
id String @id
name String?
posts Post[]
}
model Post {
id String @id
title String
author User @relation(fields: [authorId], references: [id])
authorId String
}
Environment (please complete the following information):
@prisma/client
version [4.6.1]Additional context
Describe the bug
The generated index.ts
doesn't import all enums
from schema.prisma
. In my case, it contains most but a few, and it looks like only enums
that are set on just optional
fields are not imported, despite being assigned correctly, which causes the app to break due to missing imports.
i.e Here the enum is set, but it's not imported
To Reproduce
schema.prisma
, and set it to an optional
field. *Don't use the same enum
elsewhere (especially in a required field).Expected behavior
The enums
should be imported
Describe the bug
#55
The above issue you responded to.
I am still experiencing the same problem with the schema.prisma described in the issue.
Sorry for the lack of explanation.
対応いただいた上記issueですが、
issueに記載したschema.prismaで現在も同様の現象が発生しています。
説明不足で申し訳ございません。
$ npx prisma generate
Prisma schema loaded from prisma/schema.prisma
Error:
✔ Generated Prisma Client (4.6.1 | library) to ./../../node_modules/@prisma/client in 58ms
Cannot read properties of undefined (reading 'name')
To Reproduce
Define composite unique key with @@unique
&&
Occurs when Primary Key does not exist for a model.
If @@id and @@id are added, it will not occur.
@@uniqueで複合ユニークキーを定義
&&
Primary Keyが存在しないmodelの場合に発生。
@id, @@idを追加すれば発生しない。
Expected behavior
Successful fabbrica conversion
正常にfabbricaの変換が行われる
Your Schema
// Paste schema.prisma file.
model Company {
id String @id
name String
companyEmployees CompanyEmployee[]
}
model Employee {
id String @id
name String
companyEmployees CompanyEmployee?
}
model CompanyEmployee {
company Company @relation(fields: [companyId], references: [id])
companyId String
employee Employee @relation(fields: [employeeId], references: [id])
employeeId String @unique
@@unique([companyId, employeeId])
}
Environment (please complete the following information):
Additional context
Add any other context about the problem here.
Please put gist URL including generated .js and .d.ts if you can 🙏
Provide to way to define association with defined other factory like this:
const PostFactory = definePostFactory({
defaultData: {
author: UserFactory,
}
})
Hello again! Can you share some advice on using the XyzFactoryInterface
so that it has an awareness of traits? The type takes a generic, TOptions
, but it looks like it wants something elaborate and I can't pin it down and don't see examples in the tests.
Thank you!
Hi 👋
First of all, thank you very much for your work. After your answer, we decided to use your lib on our on production application and it saved us a lot of time!
Describe the bug
As mentioned in the documentation, I create a UserProfile
which has a relation with the table User
. When I use the factory to create a UserProfile
, the return object is not similar to one from the DB so I can't call my relation like userProfile.user
.
To Reproduce
Create a relation like this:
export const UserProfileFactory = defineUserProfileFactory({
defaultData: () => ({
id: chance.guid(), // chance is a lib similar to faker
firstName: chance.first(),
user: UserFactory,
}),
});
In my test, I do : const recipientProfile = await UserProfileFactory.create();
.
My recipientProfile
doesn't have a user
property but a userId
which means if I want to do recipientProfile.user
I can't and I have to do a prisma call to get my User
db object.
Expected behavior
My factory returns a DB object and so I can easily use my relation through my new created object.
Your Schema
model User {
id String @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
email String @unique
userProfiles UserProfile[]
}
model UserProfile {
id String @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
userId String @db.Uuid
user User @relation(fields: [userId], references: [id])
firstName String?
}
Environment (please complete the following information):
@prisma/client
version: 4.13.0Thanks again for your hard work!
Describe the bug
When running prisma generate
with a schema with a type of Unsupported("tsvector)
, fabbrica generation fails
To Reproduce
Steps to reproduce the behavior:
Use the schema below, run prisma generate
.
Error in console: Not found ExampleCreateInput
Expected behavior
That generation succeeds
Your Schema
generator client {
provider = "prisma-client-js"
output = "dist/client"
}
generator fabbrica {
provider = "prisma-fabbrica"
noTranspile = true
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model Example {
id Int @unique
searchString String
searchStringVector Unsupported("tsvector")
@@index([searchStringVector], type: Gin)
}
Environment:
@prisma/client
version: 5.4.2This is a feature request.
The way Prisma works, and the way prisma-factory works, they generate a package in node_modules
rather then generating source files.
This makes it easy to import files without having to think about where the files are or whether they need to be committed into the repo.
Ideally, you could run prisma generate
and then write:
import { defineUserFactory } from "prisma-fabricca/generated"
and it would just work.
Describe the bug
Thanks for the library. I am bumping into some problem about the precedence, or re-using the association / params. How can I pass in reference of relationship?
To Reproduce
Steps to reproduce the behavior:
Expected behavior
A clear and concise description of what you expected to happen.
Your Schema
import { faker } from '@faker-js/faker';
import { Workspace } from '@prisma/client';
import {
defineUserFactory,
defineUserIdentifierFactory,
defineWorkspaceMemberFactory,
defineWorkspacePlayerFactory,
} from 'src/core/__generated__/fabbrica';
import { WorkspaceFactory } from 'src/database/seeds/modules/workspace';
import { prisma } from 'src/database/seeds/prisma-instance';
export const userIdentifierFactory = defineUserIdentifierFactory({
defaultData: async () => ({
type: 'EMAIL',
value: faker.internet.email(),
verifiedAt: faker.date.recent(),
user: userFactory,
workspace: WorkspaceFactory,
}),
});
export const workspaceMemberFactory = defineWorkspaceMemberFactory({
defaultData: async () => ({
user: userFactory,
workspace: WorkspaceFactory,
allowImpersonation: true,
}),
});
export const workspacePlayerFactory = defineWorkspacePlayerFactory({
defaultData: async () => ({
user: userFactory,
workspace: WorkspaceFactory,
allowImpersonation: true,
}),
});
export const userFactory = defineUserFactory({
defaultData: async () => ({
firstName: faker.person.firstName(),
lastName: faker.person.lastName(),
passwordHash: '$2a$10$fSBMDkZacBh6rVjMcucBx.G0gzPp9Tt97eGRQT1zclTtdbJRIbt0q',
isDisabled: false,
locale: 'en',
lastSeen: faker.date.recent(),
avatarUrl: faker.image.avatar(),
metadata: {},
}),
traits: {
isPlayer: {
data: async () => ({
workspacePlayer: {
connect: await workspacePlayerFactory.create(),
},
userIdentifiers: {
connect: await userIdentifierFactory.create(),
},
}),
},
},
});
export async function defaultAdminsSeed() {
const defaultWorkspace = await prisma.workspace.findFirst({ where: { domainName: 'localhost:3000' } });
const defaultAdmin = await userFactory.create({
userIdentifiers: {
connect: await userIdentifierFactory.create({
type: 'EMAIL',
value: '[email protected]',
workspace: {
connect: {
id: defaultWorkspace!.id,
},
},
}),
},
workspaceMember: {
connect: await workspaceMemberFactory.create({
role: 'SUPERADMIN',
allowImpersonation: false,
workspace: {
connect: {
id: defaultWorkspace!.id,
},
},
}),
},
});
console.log({ defaultAdmin });
}
export async function randomPlayersSeed() {
const otherWorkspaces = await prisma.workspace.findMany({
where: { NOT: { domainName: 'localhost:3000' } },
});
const players = await userFactory.use('isPlayer').createList(10);
// await userFactory.create({
// userIdentifiers: {
// connect: await userIdentifierFactory.create({
// workspace: {
// connect: {
// id: workspace!.id,
// },
// },
// }),
// },
// workspacePlayer: {
// connect: await workspacePlayerFactory.create({
// allowImpersonation: true,
// workspace: {
// connect: {
// id: workspace!.id,
// },
// },
// }),
// },
// });
}
// Paste schema.prisma file.
Environment (please complete the following information):
@prisma/client
version [e.g. 4.2]Additional context
Add any other context about the problem here.
Please put gist URL including generated .js and .d.ts if you can 🙏
Running prisma 4.12, I have an error when calling a factory create function.
Error: No prisma client
❯ Module.getClient node_modules/.pnpm/@[email protected][email protected]/node_modules/@quramy/prisma-fabbrica/lib/clientHolder.js:16:15
❯ Object.create packages/core/src/db/prisma/client/factories/index.ts:1852:22
1850| const create = async (inputData: Partial<Prisma.VoiceCreateInput> = {}) => {
1851| const data = await build(inputData).then(screen);
1852| return await getClient<PrismaClient>().voice.create({ data });
| ^
1853| };
1854| const createList = (inputData: number | readonly Partial<Prisma.VoiceCreateInput>[]) => Promise.all(normalizeList(inputData).map(data => create(data)));
❯ packages/test/functions/fixText.test.ts:24:19
Here's my factory implementation
generator factories {
provider = "prisma-fabbrica"
output = "./client/factories"
noTranspile = true
}
Feature like https://github.com/thoughtbot/factory_bot/blob/master/GETTING_STARTED.md#default-callbacks
const PostFactory = definePostFactory({
defaultData: {
author: UserFactory,
},
onAfterCreate: async (created) => {
await something(created);
},
});
onAfterBuild
, onBeforeCreate
, onAfterCreate
is an optional callback functionDescribe the bug
Even the field having the atttribute like @db.VarChar(10)
that constrains the length of the value, fabbrica may generate values too long for the contraint.
To Reproduce
Steps to reproduce the behavior:
I will prepare it at later...
Expected behavior
A clear and concise description of what you expected to happen.
Cut the value generated with the limit.
Your Schema
model Sample {
name String @db.VarChar(10)
}
Environment (please complete the following information):
@prisma/client
version: 5.15.0Additional context
Add any other context about the problem here.
Please put gist URL including generated .js and .d.ts if you can 🙏
i will prepaere it at leter...
Prisma.<Model>CreateInput
in v4 is not assignable to Prisma.<Model>CreateInput
in v5 because this change
For details, prisma-fabbrica generates enumerable field TypeScript code as the following:
model FieldTypePatternModel {
enumerableInt Int[]
}
/* generated type */
type FieldTypePatternModelFactoryDefineInput = {
enumerableInt?: Prisma.FieldTypePatternModelCreateenumerableIntInput | Prisma.Enumerable<number>;
}
The type Prisma.FieldTypePatternModelCreateenumerableIntInput | Prisma.Enumerable<number>
can be assigned Prisma.FieldTypePatternModelCreateInput["enumerableInt"]
in Primsa client v4, but can not be assigned to the same field in Prisma client v5.
In Prisma v5, Prisma.FieldTypePatternModelCreateInput["enumerableInt"]
is defined as Prisma.FieldTypePatternModelCreateenumerableIntInput | Array<number>
.
Change prisma-fabbrica's code generation as the following:
/* before */
type FieldTypePatternModelFactoryDefineInput = {
enumerableInt?: Prisma.FieldTypePatternModelCreateenumerableIntInput | Prisma.Enumerable<number>;
}
/* after */
type FieldTypePatternModelFactoryDefineInput = {
enumerableInt?: Prisma.FieldTypePatternModelCreateenumerableIntInput | Array<number>;
}
And this change should be published as major version up because this change can break codes of users of prisma-fabbrica.
Describe the bug
We enabled verbatimModuleSyntax in our project, because we want to be more strict about our imports to prevent import elision and issues down the road with other tools (like SWC).
Since our generated fabbrica code lives in our source directory, we have the issue that tsc
now complains about a type import that is not marked as a type import.
src/server/prisma/generated/fabbrica/index.ts:25:21 - error TS1484: 'ModelWithFields' is a type and must be imported using a type-only import when 'verbatimModuleSyntax' is enabled.
25 import { getClient, ModelWithFields, createScreener, getScalarFieldValueGenerator, Resolver, normalizeResolver, normalizeList, getSequenceCounter, } from "@quramy/prisma-fabbrica/lib/internal";
~~~~~~~~~~~~~~~
src/server/prisma/generated/fabbrica/index.ts:25:84 - error TS1484: 'Resolver' is a type and must be imported using a type-only import when 'verbatimModuleSyntax' is enabled.
25 import { getClient, ModelWithFields, createScreener, getScalarFieldValueGenerator, Resolver, normalizeResolver, normalizeList, getSequenceCounter, } from "@quramy/prisma-fabbrica/lib/internal";
~~~~~~~~
To Reproduce
Steps to reproduce the behavior:
Expected behavior
A clear and concise description of what you expected to happen.
The import of Resolver
and ModelWithFields
should be marked as a"type" import or an option should be given that enables type imports in the generated code.
Your Schema
Not relevant.
Environment (please complete the following information):
This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.
These updates have all been created already. Click a checkbox below to force a retry/rebase of any.
.github/workflows/build.yml
actions/checkout v4@692973e3d937129bcbf40652eb9f2f61becf3332
actions/setup-node v4
.github/workflows/publish.yml
actions/checkout v4@692973e3d937129bcbf40652eb9f2f61becf3332
actions/setup-node v4
actions/github-script v7
package.json
markdown-toc ^1.2.0
@prisma/client 5.17.0
@types/node ^20.0.0
husky 9.1.1
prettier 3.3.3
pretty-quick 4.0.0
prisma 5.17.0
rimraf ^6.0.0
typescript 5.5.4
packages/artifact-testing/package.json
@prisma/internals 5.17.0
@types/jest 29.5.12
jest 29.7.0
ts-jest 29.2.3
typescript *
packages/prisma-fabbrica/package.json
@prisma/generator-helper ^5.0.0
@prisma/internals ^5.0.0
short-uuid 5.2.0
talt 2.4.4
@types/jest 29.5.12
jest 29.7.0
ts-jest 29.2.3
typescript *
@prisma/client ^5.0.0
typescript ^3.0.0 || ^4.0.0 || ^5.0.0
Hi @Quramy ,
I've just discovered this library that looks awesome 😍 !! (please keep working on it ;) )
It looks like you have just release the first stable version but I am a bit worried about using it on a real world application.
What are your plans for this project ?
Do you plan on maintaining it on the long run ?
Do you plan on maintaining this project alone ? Do you plan on involving maintainers (people or companies) ?
Do you know how many companies use it in real world project ?
Do you plan on respecting SemVer (particularly respecting the major version update when introducing breaking changes) ?
Have you tested it with other users before releasing the first stable version ?
Sorry for all those annoying question, but I'm pretty sure that if you plan maintaining this project on the long run it will become a few hundreds/thousands stars project !
In any case, thank you for your work on this project 🙂
Feature request
Would that be possible to conditionally resign from persistence and simulate best-effort models' response from a create
related API?
In another words could we derive Account
type from AccountCreateInput
without persisting to Prisma
Really nice library!
Thanks for building such a great library!
We have multiple databases on the same application and would like to initialize prisma-fabbrica multiple times with different PrismaClient to connect them.
But since fabbrica currently stores PrismaClient instance as a singleton, every initialization overrides previous one.
Any chance to support multiple databases?
Like transient attributes in factory_bot, I'd like to provide feature to define and use arbitrary parameters in fabbrica.
See #244 .
Signature of this feature should satisfy the followings:
Add HOF withExtraParameters
to defineModelFactory fn:
declare function withExtraParameters<TExtraParams>(defaultExtraParams: TExtraParams) => (options: UserFactoryOptions) => UserFactoryInterface<TExtraParams, UserFactoryOptions>;
defineModelFactory works not only function but also object which provides the HOF.
import { defineUserFactory } from "./__generated__/fabbrica";
export async function seed() {
const UserFactory = defineUserFactory.withExtraParameters({ loginCount: 0 })({
defaultData: ({ seq, loginCount }) => {
console.log(seq, loginCount);
return {};
},
traits: {
withLoginRecords: {
data: ({ loginCount }) => {
console.log(loginCount);
return {};
},
},
},
});
await UserFactory.build({ loginCount: 100 });
await UserFactory.build(); // UserFactory provides default value defined `withExtraParameters`(i.e. 0) as loginCount
await UserFactory.use("withLoginRecords").build({ loginCount: 100 });
}
If you want fully example, see https://github.com/Quramy/prisma-fabbrica/blob/feature/transient_params/packages/artifact-testing/fixtures/callback/transients.ts
Factory guarantees extra parameters existence because of default value. So developer can refer the extra parameters at implementation of defaultData
or traits. ( Inspired from createContext
in React) .
And default parameters object also tells to factory what kind of type for extra parameters via type inference.
The major reason is to infer types of the extra parameters and to provide the inferred type to factory definition. I also attempted the following pattern, but I can't achieve it.
const UserFactory = defineUserFactory({
defaultExtraParams: { loginCount: 0 },
defaultData: ({ seq, loginCount }) => {
console.log(seq, loginCount);
return {};
},
})
extraParameters
?I think a different name would be fine. For example, transientFields
or contextParameters
.
Hello! First, I want to thank you so much for this library. I was so happy to find it and even happier to discover how well it's working and being maintained. I gave you and the project a shout out on Twitter, I hope you get more recognition for your work.
I'm curious to know what (if any) patterns exist for providing data to a factory for use in the building of its relations. For instance, image that objects in a project are scoped to an Organization
. We want to ensure that as a chain of objects are created, they all get the same organizationId
key associated with them. What I want to do is something like:
const organization = await OrganizationFactory.create();
const department = DepartmentFactory.provide({ organizationId: organization.organizationId }).use('withTeams').create();
In this example, withTeams
might create a team, each team might create two offices, each office might create three desks. I each to use the organizationId
if it is available.
Right now, I'm doing this manually. I'm using more narrowly scoped factories without traits then gluing them together. But there's a lot of boilerplate that I think the factory could provide. Does a pattern exist for this already, or is it something you'd be open to adding?
Currently, the library only identifies strings with attribute type, @id
via the IsId
parameter to be assigned a UUID as a scalar default
export const scalarFieldValueGenerator: StrictScalarFieldValueGenerator = {
Boolean: () => true,
String: ({ fieldName, isId, isUnique }) => {
if (isId || isUnique) {
return short.generate();
}
return `${fieldName} field`;
},
Int: ({ isId, isUnique, seq }) => {
if (isId || isUnique) {
return seq;
}
return 10;
},
Float: () => 3.14,
BigInt: () => BigInt(12_345_678),
Decimal: () => {
throw new Error("Not support Decimal field generation");
},
DateTime: () => new Date(),
Bytes: () => Buffer.from([0x80, 0xff]),
Json: () => ({
key: "value",
}),
};
However, schemas that make use of the attribute @db.Uuid
are treated a regular strings, which is a mismatched handling.
Describe the bug
After upgrading from 2.0.2 to 2.1.0, a function that I use to seed my database fails with the error Error: No prisma client
. I confirmed that initialize
is called prior to this. Even moving the initialize
call immediately before UserFactory.create()
does not resolve it.
To Reproduce
Steps to reproduce the behavior:
Expected behavior
It should create database records without crashing :-)
Your Schema
Sorry, I can't provide this.
Environment (please complete the following information):
@prisma/client
version - 5.8.1Additional context
There might be some important things to know.
globalSetup
file, globalDbSetup.ts
. That file is this:import { dbConnectionHelper } from './testDbHelper';
let dbBootstrapped = false;
export default async function setup({ provide }: { provide: (key: string, value: unknown) => void }) {
if (dbBootstrapped) {
throw new Error('We already setup the database once!');
}
dbBootstrapped = true;
dbConnectionHelper.setup();
// This is going to cleanup the database by truncating it. It ensures we always start with a clean slate.
await dbConnectionHelper.cleanup();
const userId = await dbConnectionHelper.bootstrap();
if (!userId) {
throw new Error('ERROR: no user was created!');
}
provide('userId', userId);
}
The dbConnectionHelper
is a singleton object that is responsible for the connection. It looks like this:
import { getPrismaClient } from '@/lib/getPrismaClient';
import { initialize } from './__generated__/fabbrica';
import { UserFactory } from './factories/UserFactory';
class DbConnectionHelper {
connection?: ReturnType<typeof getPrismaClient>;
userId?: string;
constructor() {
if (!process.env.VITEST) {
throw new Error('ERROR: This class is not for use outside of testing!');
}
}
getUserId() {
if (!this.userId) {
throw new Error('ERROR: no user id has been set!');
}
return this.userId;
}
setup() {
this.connection = getPrismaClient();
initialize({ prisma: this.connection });
}
async bootstrap() {
if (this.userId) {
return this.userId;
}
const user = await this.seedDatabase();
this.userId = user.id;
return this.userId;
}
async cleanup() {
if (!this.connection) {
throw new Error('ERROR: no connection has been established!');
}
if (!process.env.VITEST) {
throw new Error('ERROR: This method is not for use outside of testing!');
}
console.log('TRUNCATING DATABASE TABLES');
const tableNames = await this.getTableNames();
try {
for await (const tableName of tableNames) {
await this.connection.$queryRawUnsafe(`TRUNCATE TABLE "${tableName}" CASCADE`);
}
} catch (e) {
console.log('CLEANUP FAILED:');
console.log(e);
}
}
async getTableNames() {
if (!this.connection) {
throw new Error('ERROR: no connection has been established!');
}
if (!process.env.VITEST) {
throw new Error('ERROR: This method is not for use outside of testing!');
}
const tables = await this.connection.$queryRaw`SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'public'
`;
return (tables as { table_name: string }[])
.map((table) => table.table_name)
.filter((name) => name !== '_prisma_migrations' && !name.startsWith('pg_'));
}
async seedDatabase() {
if (!this.connection) {
throw new Error('ERROR: no connection has been established!');
}
if (!process.env.VITEST) {
throw new Error('ERROR: This method is not for use outside of testing!');
}
const user = await UserFactory.create();
// Some more setup logic happens here. We create some related objects that are expected to exist.
return user;
}
}
export const dbConnectionHelper = new DbConnectionHelper();
The failure occurs within seedDatabase
as soon as we call UserFactory.create()
.
getPrismaClient()
returns this.Finally, sorry for not responding about the WIP traits! I'll review those now.
The change in #257 appears to have broken transactional tests with jest-prisma. Some factory generations seem to be picking up something other than the jestPrisma client that wraps them in a transaction.
Describe the bug
ユニークキーを持った not null の string カラムで
'TypeError: Cannot read properties of undefined (reading 'generate')'
が発生しテスト実行時エラーになる
To Reproduce
後述するschema.prismaを用意し、テストを実行で確認できます。
Expected behavior
実行時エラーが発生しないこと
Your Schema
// Paste schema.prisma file.
model Users {
id BigInt @id @default(autoincrement())
name String
email String @unique
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
}
factory 定義
const usersFactory = defineUsersFactory({
defaultData: async ({ seq }) => {
return {
name: 'test user',
email: `test_${seq}@example.com`,
};
},
});
beforeEach(async () => {
await usersFactory.create();
});
ログ
TypeError: Cannot read properties of undefined (reading 'generate')
220 | return {
221 | name: getScalarFieldValueGenerator().String({ modelName: "Users", fieldName: "name", isId: false, isUnique: false, seq }),
> 222 | email: getScalarFieldValueGenerator().String({ modelName: "Users", fieldName: "email", isId: false, isUnique: true, seq })
| ^
223 | };
224 | }
225 |
at Object.String (node_modules/.pnpm/@[email protected]_@[email protected][email protected][email protected]/node_modules/@quramy/prisma-fabbrica/lib/scalar/gen.js:12:41)
at autoGenerateUsersScalarsOrEnums (fabbrica/index.ts:222:47)
at build (fabbrica/index.ts:233:40)
at Object.create (fabbrica/index.ts:253:32)
Environment (please complete the following information):
@prisma/client
version [5.2.0]Additional context
When the @unique attribute exists in the model definition, a TypeError: Cannot read properties of undefined (reading 'generate') error occurs.
Describe the bug
The error TypeError: Cannot read properties of undefined (reading 'generate') occurs when using a string column with a unique constraint and not null, causing a test execution error.
To Reproduce
You can prepare the schema.prisma mentioned below and execute the test to confirm.
Expected behavior
No runtime error should occur.
Additional context
Hi @Quramy
I stumbled upon another factory library: https://prisma-extensions.io/.
They use Prisma extension to have a really clean API without the need to generate new files.
Here is what their API look like:
import { PrismaClient } from '@prisma/client';
import { factoryExtension } from '@prisma-extensions/factory';
const prisma = new PrismaClient();
const db = prisma.$extends(factoryExtension);
const UserFactory = db.user.$factory({
firstName: 'Tom',
lastName: 'Milewski',
})
UserFactory.attributes();
UserFactory.create();
Except if the generator provide really awesome features which are not possible with prisma client, I think Prisma Fabbrica should head into this direction.
I know it would introduce breaking changes and would require a v2 but I think it would be better on the long run.
First, Prisma seems to favor extensions to generator because they are more composable. Besides, generated files can slow build tasks and produce weird problems due to stale files.
What do you think ?
PS: Unfortunately the code of the library isn't released publicly yet.
First of all, really like the look of this library. Hoping to switch to it from our current solution.
However having an issue with creating reusable factory templates and exporting them (e.g. for overriding defaults across several different test files). For example:
export const factory = defineUserFactory({
defaultData: async () => ({
displayName: faker.name.fullName().slice(0, 30),
email: faker.internet.email().slice(0, 100),
}),
});
Current fails to compile with Exported variable 'factory' has or is using name 'UserFactoryInterface' from external module "<dir>/src/factories/__generated__/fabbrica/index" but cannot be named.
. This would be solvable by being able to import UserFactoryInterface
, however the generated factories don't export these interfaces.
Is it possible to either:
defineXFactory
, so they can be imported into another file?Thanks for your time!
Awesome library! Will this work with prisma 5.0.0
?
/**
*
* Define factory for {@link User} model.
*
* @example
* ```ts
* const UserFactory = defineUserFactory();
*
* await UserFactory.create();
* ```
*
*/
export function defineUserFactory(args: UserFactoryDefineOptions = {}) {
return defineUserFactoryInternal(args);
}
When the setting noTransplie
is set to true
, part of the import path is escaped \\
on Windows vs on Linux which uses /
.
Example:
Windows
import type { User } from "./..\\..\\prisma-client\\dist\\client";
Linux
import type { User } from "./../../prisma-client/dist/client";
This results in issues with version control when the code is complied across operating systems.
After the change to make factoryFor
a private symbol, exporting a generated factory results in a type error:
Exported variable 'FooFactory' has or is using name 'factoryFor' from external module "test/factories/fabbrica/index" but cannot be named.
The error occurs on this line:
export const FooFactory = defineFooFactory();
Something like this:
UserFactory.create({
posts: {
create: [await PostFactory.buildCreateInput()]
}
})
tsc never emits diagnostics for the above code , but the runtime error occurs because the object resolved by buildCreateInput
includes { author: { ... } }
property.
Model Factory 's .create()
methods screens the input data before passing to Prisma client's raw .crate
function.
The screening procedure should the following:
expect(screen({
id: "",
name: "",
posts: {
create: [{ id: "", author: { ... } }],
connect: [{ id: "" }],
}
}).toEqual({
id: "",
name: "",
posts: {
create: [{ id: "" }], // The author property is removed
connect: [[ id: "" }],
}
})
Describe the bug
If specifying NODE_ENV="development"
and run prisma-fabbrica generators then type tests aren't passed, it stops generating code though it continues with other values.
Now, maybe due to Node TypeLiteral did not pass test 'isEntityName'
warning, anytime it stops now when setting NODE_ENV="development"
.
To Reproduce
Set env variables NODE_ENV="development" and run npm run migrate:test:ci
in example-prj directory.
Expected behavior
There are two choices.
Node TypeLiteral did not pass test 'isEntityName'
Your Schema
Maybe any schema I tested if warning occurs.
Environment (please complete the following information):
@prisma/client
version [4.6.1]Additional context
Describe the bug
プライマリキーを持たないmodelの場合、npx prisma generate実行時に
Cannot read properties of undefined (reading 'name')
が発生し、generateに失敗してしまいます。
To Reproduce
後述するschema.prismaを用意し、npx prisma generateを実行で確認できます。
CompanyEmployeeに、@id、もしくは@@idでPKを設定すれば、発生しないことを確認しています。
Expected behavior
generateの正常終了
Your Schema
// Paste schema.prisma file.
model Company {
id String @id
name String
companyEmployees CompanyEmployee[]
}
model Employee {
id String @id
name String
companyEmployees CompanyEmployee?
}
model CompanyEmployee {
company Company @relation(fields: [companyId], references: [id])
companyId String
employee Employee @relation(fields: [employeeId], references: [id])
employeeId String @unique
@@unique([companyId, employeeId])
}
Environment (please complete the following information):
@prisma/client
version [4.6.1]Additional context
93247ec
コードは最新のものから、特に変更を加えず確認しています。
export const UserFactory = defineUserFactory({
defaultData: async ({ seq }) => ({
id: `user-${seq}`,
})
})
await userFactory.create() // insert user with id = user-0
await userFactory.create() // insert user with id = user-1
await userFactory.create() // insert user with id = user-2
// :
Hi, thank you for a great library!
This library has made me very comfortable writing prisma tests, but I have one request.
When I try to create a record with required Decimal type, Not support Decimal field generation
error occurs.
It seems to be caused by getScalarFieldValueGenerator().Decimal()
being executed in autoGenerateTABLE_NAMEOrEnums
function even if the target Decimal column is explicitly given a value.
There seems to be no escape hatch that prevents auto generate Decimal scalar data.
In the case of a required Decimal column, could you please add a change like that a scalar data is not automatically generated and must be explicitly given a value , for example?
Allow to customize rules defined https://github.com/Quramy/prisma-fabbrica/blob/main/packages/prisma-fabbrica/src/scalar/gen.ts .
registerScalarFieldValueGenerator({
String: ({ fieldName }) => {
if (fieldName.endsWith('email')) {
return randomeEmail()
}
return fieldName;
},
})
Describe the bug
src/__generated__/fabbrica/index.ts:22:16 - error TS2742: The inferred type of 'initialize' cannot be named without a reference to '../../../node_modules/@quramy/prisma-fabbrica/lib/initialize.js'. This is likely not portable. A
type annotation is necessary.
22 export const { initialize } = initializer;
~~~~~~~~~~
Found 1 error in src/__generated__/fabbrica/index.ts:22
To Reproduce
npx tsc -p tsconfig.build.json
Expected behavior
No TypeScript errors
Your Schema
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
previewFeatures = ["tracing"]
}
generator fabbrica {
provider = "prisma-fabbrica"
noTranspile = true
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model Post {
id String @id @default(cuid())
title String
body String
isDraft Boolean
comments Comment[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt()
}
model Comment {
id String @id @default(cuid())
post Post @relation(fields: [postId], references: [id])
postId String
star Int
body String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt()
}
Environment (please complete the following information):
@prisma/client
version [e.g. 4.2]: 5.15.0Additional context
Add any other context about the problem here.
My tsconfig.json:
/* tsconfig.bulid.json */
{
"extends": "./tsconfig.json",
"compilerOptions": {
"incremental": true,
"noEmit": false,
"declaration": true,
"rootDir": "src",
"outDir": "built"
},
"include": ["src/**/*"]
}
/* tsconfig.json */
{
"compilerOptions": {
"incremental": true,
"target": "es2021",
"module": "node16",
"moduleResolution": "node16",
"noEmit": true,
"types": ["@types/jest", "@quramy/jest-prisma-node"],
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
}
}
Please put gist URL including generated .js and .d.ts if you can 🙏
const UserFactory = defineUserFactory({
defaultData: {},
});
const PostFactory = definePostFactory({
defaultData: {
author: UserFactory,
},
});
describe("factories", () => {
describe("PostFactory", () => {
it("creates record with association", async () => {
const user = await UserFactory.createForConnect({ name: "quramy" });
await PostFactory.create({ author: { connect: user } });
await PostFactory.create({ author: { connect: user } });
await PostFactory.create({ author: { connect: user } });
const userWithPosts = await prisma.user.findFirst({ where: { name: "quramy" }, include: { posts: true } });
expect(userWithPosts?.posts.length).toBe(3);
});
});
});
For the above example, we needs the following generation:
diff --git a/packages/ts-compile-testing/fixtures/relations-one-to-many/__generated__/fabbrica/index.ts b/packages/ts-compile-testing/fixtures/relations-one-to-many/__generated__/fabbrica/index.ts
index d5f1c4c..5920f62 100644
--- a/packages/ts-compile-testing/fixtures/relations-one-to-many/__generated__/fabbrica/index.ts
+++ b/packages/ts-compile-testing/fixtures/relations-one-to-many/__generated__/fabbrica/index.ts
@@ -31,14 +31,22 @@ function defineUserFactoryInternal({ defaultData: defaultDataResolver }: UserFac
const data: Prisma.UserCreateInput = { ...requiredScalarData, ...defaultData, ...defaultAssociations, ...inputData };
return data;
};
+ const pickForConnect = (inputData: Prisma.UserCreateInput) => ({
+ id: inputData.id
+ });
const create = async (inputData: Partial<Prisma.UserCreateInput> = {}) => {
const data = await buildCreateInput(inputData);
return await getClient<PrismaClient>().user.create({ data });
};
+ const createForConnect = (inputData: Partial<Prisma.UserCreateInput>={}) => {
+ return create(inputData).then(pickForConnect)
+ }
return {
_factoryFor: "User" as const,
buildCreateInput,
+ pickForConnect,
create,
+ createForConnect,
};
}
export function defineUserFactory(args: UserFactoryDefineOptions = {}) {
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.