Coder Social home page Coder Social logo

prisma-fabbrica's People

Contributors

bun913 avatar dznbk avatar katsukiniwa avatar mochizukikotaro avatar quramy avatar renovate[bot] avatar sisisin avatar toshi1127 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

prisma-fabbrica's Issues

Create list shorthand

The following methods are useful to create has-many relation.

  • createList
  • buildList

Can't give factory for optional one-to-many relation

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):

  • Database kind [e.g. PostgreSQL ]
  • Node.js version [e.g. 16.0]
  • @prisma/client version [e.g. 4.2]
  • TypeScript version [e.g. 4.8.3]

Additional context
Add any other context about the problem here.

Please put gist URL including generated .js and .d.ts if you can 🙏

Unknown scalar type "Null" for ... .

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):

  • Database kind [PostgreSQL, sqlite]
  • Node.js version [v16.15.0]
  • @prisma/client version [4.6.1]
  • TypeScript version [e.g. 4.9.3]

Additional context

Enums not imported

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
image

To Reproduce

  1. Create an enum type in schema.prisma, and set it to an optional field. *Don't use the same enum elsewhere (especially in a required field).
  2. generate Prisma models
  3. Visit the generated module, and most likely you will see that enum is not imported

Expected behavior
The enums should be imported

Cannot convert when composite unique key and Primary Key are not set

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):

  • Database kind [sqlite]
  • Node.js version [18.11.0]
  • @prisma/client version [4.6.1]
  • TypeScript version [4.9.3]

Additional context
Add any other context about the problem here.

Please put gist URL including generated .js and .d.ts if you can 🙏

Define Association shorthand

Provide to way to define association with defined other factory like this:

const PostFactory = definePostFactory({
  defaultData: {
    author: UserFactory,
  }
})

FactoryInterface type with traits

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!

Factory doesn't return a db object

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):

  • Database kind: PostgreSQL
  • Node.js version: 18.14.6
  • @prisma/client version: 4.13.0
  • TypeScript version: 5.0.4

Thanks again for your hard work!

Error when using `Unsupported("tsvector")` as a type

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:

  • Database kind: PostgreSQL
  • Node.js version: 18.18.0
  • @prisma/client version: 5.4.2
  • TypeScript version: 5.2.2

Feature: Generate a package in node_modules rather than creating generated files

This 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.

How to connect in Traits?

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):

  • Database kind [e.g. PostgreSQL ]
  • Node.js version [e.g. 16.0]
  • @prisma/client version [e.g. 4.2]
  • TypeScript version [e.g. 4.8.3]

Additional context
Add any other context about the problem here.

Please put gist URL including generated .js and .d.ts if you can 🙏

No prisma client error

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
}

Ignores the `@db.VarChar()` attribute length constraint.

Describe 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):

  • Database kind: PostgreSQL
  • Node.js version: v20.9.0
  • @prisma/client version: 5.15.0
  • TypeScript version: 5.4.5

Additional 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...

Follow Prisma client v5

Problems to solve

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>.

Implementation plan

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.

verbatimModuleSyntax = true breaks a project that uses 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:

  1. Enable verbatimModuleSyntax in your tsconfig.json
  2. Run tsc

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):

  • TypeScript version: 5.1.3

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Detected dependencies

github-actions
.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
npm
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

  • Check this box to trigger a request for Renovate to run again on this repository

Use prisma-fabbrica in real world application

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 🙂

Create models without persisting

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!

Multiple databases support

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?

Design signature of arbitrary factory parameters

What

Like transient attributes in factory_bot, I'd like to provide feature to define and use arbitrary parameters in fabbrica.

Background

See #244 .

Points

Signature of this feature should satisfy the followings:

  1. Types for additional user-defined parameters are inferred whenever possible.
  2. New signature should be backward-compat.

Proposal for API signature

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

Why default parameters ?

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.

Why HOF ?

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 {};
    },
  })

Why named extraParameters ?

I think a different name would be fine. For example, transientFields or contextParameters.

Caveat

  • Developer can't define trait specified parameters. If a parameter is referred from one trait impl, it should be defined and provided the default value at the factory level

Providing a value for dependent objects

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?

Handle UUID Generation for Strings in addition to ID

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.

"Error: No prisma client" in version 2.1.0

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:

  1. Upgrade to 2.1.0
  2. Run the Prisma migrate command to rebuild the client
  3. Run tests

Expected behavior

It should create database records without crashing :-)

Your Schema

Sorry, I can't provide this.

Environment (please complete the following information):

  • Database kind - Postgres
  • Node.js version - 20.10.0
  • @prisma/client version - 5.8.1
  • TypeScript version - 5.2.2

Additional context

There might be some important things to know.

  1. I perform some database operations at the beginning of each run of my tests. I am using Vite and have a 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().

  1. I use an extende Prisma client. Calling getPrismaClient() returns this.
  2. I am using https://github.com/aiji42/vitest-environment-vprisma (@aiji42) which does some tricky things with the Prisma client. I wonder if this is related? From my debugging, it's not using the vPrisma client.
  3. The Prisma connection is active because the earlier queries to get table names return correctly. This is limited to the factories' connection.

Finally, sorry for not responding about the WIP traits! I'll review those now.

モデル定義に'@unique'属性が存在すると'TypeError: Cannot read properties of undefined (reading 'generate')' が発生する

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):

  • Database kind [mysql:8.1 ]
  • Node.js version [v18.17.1]
  • @prisma/client version [5.2.0]
  • TypeScript version [5.2.2]

Additional context

  • schema.prisma の定義で @default を設定することで回避しています。
  • prisma と prisma-fabbrica のバージョンを変更して確認したところ、発生していない様でした。
    • 確認したバージョン: "@prisma/client": "^4.12.0", "@quramy/prisma-fabbrica": "^1.0.3",

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

  • The issue is being avoided by setting @default in the schema.prisma definition.
  • When testing with different versions of Prisma and prisma-fabbrica, the issue does not occur.
  • Versions tested: "@prisma/client": "^4.12.0", "@quramy/prisma-fabbrica": "^1.0.3"

Documentation

  • Getting started
  • jest-prisma integration
  • Field auto filling
  • Belongs to field relation
  • Generator configuration
  • API Reference
    • Define factory
    • create
    • buildInputCreate
    • createForConnect

Use Prisma extension instead of generator

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.

export interfaces on defineXFactory

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:

  • Export the related interfaces for each defineXFactory, so they can be imported into another file?
  • For me to do this in a better way that doesn't require that above?

Thanks for your time!

Add TSDoc to generated functions

/**
 *
 * Define factory for {@link User} model.
 *
 * @example
 * ```ts
 * const UserFactory = defineUserFactory();
 * 
 * await UserFactory.create();
 * ```
 *
 */
export function defineUserFactory(args: UserFactoryDefineOptions = {}) {
    return defineUserFactoryInternal(args);
}

Import paths in generated code without transpilation are different across Windows and Linux

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.

Helper to define has-one / has-many relation fields

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.

Solution

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: "" }],
  }
})

NODE_ENV="development" stops generating codes if typescript's tests not passed, and Node TypeLiteral did not pass test 'isEntityName' always fails

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" .

スクリーンショット 2022-11-30 10 07 49

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.

  1. If not allowing warning, fix Node TypeLiteral did not pass test 'isEntityName'
  2. If allowing warning, skip this exception and continue to generate codes.

Your Schema

Maybe any schema I tested if warning occurs.

Environment (please complete the following information):

  • Database kind [any]
  • Node.js version [any?]
  • @prisma/client version [4.6.1]
  • TypeScript version [4.9.3]

Additional context

Failed to generate when model without PK

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):

  • Database kind [sqlite]
  • Node.js version [18.11.0]
  • @prisma/client version [4.6.1]
  • TypeScript version [4.9.3]

Additional context
93247ec
コードは最新のものから、特に変更を加えず確認しています。

Sequence counter

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
// :

Cannot create a record with a column of required Decimal type

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?

error TS2742: The inferred type of 'initialize' cannot be named without a reference

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

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):

  • Database kind [e.g. PostgreSQL ]: PostgreSQL
  • Node.js version [e.g. 16.0]: v22.3.0
  • @prisma/client version [e.g. 4.2]: 5.15.0
  • TypeScript version [e.g. 4.8.3]: 5.4.5

Additional 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 🙏

Connect and create util

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 = {}) {

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.