From 9b73f27c5688439f3d4e7c979945c58b790a386d Mon Sep 17 00:00:00 2001 From: Quramy Date: Fri, 25 Nov 2022 15:12:43 +0900 Subject: [PATCH 01/10] feat: Implement sequence counter --- .../src/__generated__/fabbrica/index.d.ts | 9 ++- .../src/__generated__/fabbrica/index.js | 31 ++++++---- .../example-prj/src/connectOrCreate.test.ts | 36 +++++++++++ examples/example-prj/src/sample.test.ts | 29 --------- examples/example-prj/src/sequence.test.ts | 28 +++++++++ packages/prisma-fabbrica/src/helpers/index.ts | 1 + .../prisma-fabbrica/src/helpers/sequence.ts | 15 +++++ .../src/helpers/valueResolver.test.ts | 29 +++++++++ .../src/helpers/valueResolver.ts | 15 +++-- packages/prisma-fabbrica/src/initialize.ts | 4 ++ packages/prisma-fabbrica/src/scalar/gen.ts | 4 +- packages/prisma-fabbrica/src/scalar/types.ts | 1 + .../src/scripts/jest-prisma/setup.ts | 3 + .../__snapshots__/getSourceFile.test.ts.snap | 25 +++++--- ...nerateModelScalarsOrEnumsFieldArgs.test.ts | 22 +++---- .../prisma-fabbrica/src/templates/index.ts | 31 +++++++--- .../__generated__/fabbrica/index.ts | 41 +++++++++---- .../fixtures/field-variation/sample.ts | 7 +++ .../__generated__/fabbrica/index.ts | 43 ++++++++----- .../__generated__/fabbrica/index.ts | 61 +++++++++++++------ .../__generated__/fabbrica/index.ts | 41 +++++++++---- .../__generated__/fabbrica/index.ts | 25 +++++--- 22 files changed, 357 insertions(+), 144 deletions(-) create mode 100644 examples/example-prj/src/connectOrCreate.test.ts create mode 100644 examples/example-prj/src/sequence.test.ts create mode 100644 packages/prisma-fabbrica/src/helpers/sequence.ts create mode 100644 packages/prisma-fabbrica/src/helpers/valueResolver.test.ts create mode 100644 packages/ts-compile-testing/fixtures/field-variation/sample.ts diff --git a/examples/example-prj/src/__generated__/fabbrica/index.d.ts b/examples/example-prj/src/__generated__/fabbrica/index.d.ts index 97ba5a25..4a99b0c4 100644 --- a/examples/example-prj/src/__generated__/fabbrica/index.d.ts +++ b/examples/example-prj/src/__generated__/fabbrica/index.d.ts @@ -2,14 +2,17 @@ import { User } from "@prisma/client"; import { Post } from "@prisma/client"; import { Prisma } from "@prisma/client"; import { Resolver } from "@quramy/prisma-fabbrica/lib/helpers"; -export { initialize } from "@quramy/prisma-fabbrica"; +export { initialize, resetSequence } from "@quramy/prisma-fabbrica"; +type BuildDataOptions = { + readonly seq: number; +}; type UserFactoryDefineInput = { id?: string; name?: string; posts?: Prisma.PostCreateNestedManyWithoutAuthorInput; }; type UserFactoryDefineOptions = { - defaultData?: Resolver; + defaultData?: Resolver; }; export declare function defineUserFactory(args?: UserFactoryDefineOptions): { _factoryFor: "User"; @@ -32,7 +35,7 @@ type PostFactoryDefineInput = { author: PostauthorFactory | Prisma.UserCreateNestedOneWithoutPostsInput; }; type PostFactoryDefineOptions = { - defaultData: Resolver; + defaultData: Resolver; }; export declare function definePostFactory(args: PostFactoryDefineOptions): { _factoryFor: "Post"; diff --git a/examples/example-prj/src/__generated__/fabbrica/index.js b/examples/example-prj/src/__generated__/fabbrica/index.js index 913f225a..f8f42db1 100644 --- a/examples/example-prj/src/__generated__/fabbrica/index.js +++ b/examples/example-prj/src/__generated__/fabbrica/index.js @@ -3,22 +3,27 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.definePostFactory = exports.defineUserFactory = exports.initialize = void 0; +exports.definePostFactory = exports.defineUserFactory = exports.resetSequence = exports.initialize = void 0; const clientHolder_1 = require("@quramy/prisma-fabbrica/lib/clientHolder"); const gen_1 = __importDefault(require("@quramy/prisma-fabbrica/lib/scalar/gen")); const helpers_1 = require("@quramy/prisma-fabbrica/lib/helpers"); var prisma_fabbrica_1 = require("@quramy/prisma-fabbrica"); Object.defineProperty(exports, "initialize", { enumerable: true, get: function () { return prisma_fabbrica_1.initialize; } }); -function autoGenerateUserScalarsOrEnums() { +Object.defineProperty(exports, "resetSequence", { enumerable: true, get: function () { return prisma_fabbrica_1.resetSequence; } }); +function autoGenerateUserScalarsOrEnums({ seq }) { return { - id: gen_1.default.String({ modelName: "User", fieldName: "id", isId: true, isUnique: false }), - name: gen_1.default.String({ modelName: "User", fieldName: "name", isId: false, isUnique: false }) + id: gen_1.default.String({ modelName: "User", fieldName: "id", isId: true, isUnique: false, seq }), + name: gen_1.default.String({ modelName: "User", fieldName: "name", isId: false, isUnique: false, seq }) }; } function defineUserFactoryInternal({ defaultData: defaultDataResolver }) { + const seqKey = {}; + const getSeq = () => (0, helpers_1.getSequenceCounter)(seqKey); const buildCreateInput = async (inputData = {}) => { - const requiredScalarData = autoGenerateUserScalarsOrEnums(); - const defaultData = await (0, helpers_1.resolveValue)(defaultDataResolver ?? {}); + const seq = getSeq(); + const requiredScalarData = autoGenerateUserScalarsOrEnums({ seq }); + const resolveValue = (0, helpers_1.normalizeResolver)(defaultDataResolver ?? {}); + const defaultData = await resolveValue({ seq }); const defaultAssociations = {}; const data = { ...requiredScalarData, ...defaultData, ...defaultAssociations, ...inputData }; return data; @@ -46,16 +51,20 @@ exports.defineUserFactory = defineUserFactory; function isPostauthorFactory(x) { return x?._factoryFor === "User"; } -function autoGeneratePostScalarsOrEnums() { +function autoGeneratePostScalarsOrEnums({ seq }) { return { - id: gen_1.default.String({ modelName: "Post", fieldName: "id", isId: true, isUnique: false }), - title: gen_1.default.String({ modelName: "Post", fieldName: "title", isId: false, isUnique: false }) + id: gen_1.default.String({ modelName: "Post", fieldName: "id", isId: true, isUnique: false, seq }), + title: gen_1.default.String({ modelName: "Post", fieldName: "title", isId: false, isUnique: false, seq }) }; } function definePostFactoryInternal({ defaultData: defaultDataResolver }) { + const seqKey = {}; + const getSeq = () => (0, helpers_1.getSequenceCounter)(seqKey); const buildCreateInput = async (inputData = {}) => { - const requiredScalarData = autoGeneratePostScalarsOrEnums(); - const defaultData = await (0, helpers_1.resolveValue)(defaultDataResolver ?? {}); + const seq = getSeq(); + const requiredScalarData = autoGeneratePostScalarsOrEnums({ seq }); + const resolveValue = (0, helpers_1.normalizeResolver)(defaultDataResolver ?? {}); + const defaultData = await resolveValue({ seq }); const defaultAssociations = { author: isPostauthorFactory(defaultData.author) ? { create: await defaultData.author.buildCreateInput() diff --git a/examples/example-prj/src/connectOrCreate.test.ts b/examples/example-prj/src/connectOrCreate.test.ts new file mode 100644 index 00000000..d94e11ce --- /dev/null +++ b/examples/example-prj/src/connectOrCreate.test.ts @@ -0,0 +1,36 @@ +import { defineUserFactory, definePostFactory } from "./__generated__/fabbrica"; + +const prisma = jestPrisma.client; + +const UserFactory = defineUserFactory(); + +const PostFactory = definePostFactory({ + defaultData: async () => ({ + author: { + connectOrCreate: { + where: { + id: "user001", + }, + create: await UserFactory.buildCreateInput({ + id: "user001", + }), + }, + }, + }), +}); + +describe("factories", () => { + describe("PostFactory", () => { + it("creates post record", async () => { + await PostFactory.create(); + await expect(prisma.user.count()).resolves.toBe(1); + }); + + it("creates related user at most one", async () => { + await PostFactory.create(); + await PostFactory.create(); + await PostFactory.create(); + await expect(prisma.user.count()).resolves.toBe(1); + }); + }); +}); diff --git a/examples/example-prj/src/sample.test.ts b/examples/example-prj/src/sample.test.ts index 1ce10580..a7737143 100644 --- a/examples/example-prj/src/sample.test.ts +++ b/examples/example-prj/src/sample.test.ts @@ -10,21 +10,6 @@ const PostFactory = definePostFactory({ }, }); -const PostFactoryAlt = definePostFactory({ - defaultData: async () => ({ - author: { - connectOrCreate: { - where: { - id: "user001", - }, - create: await UserFactory.buildCreateInput({ - id: "user001", - }), - }, - }, - }), -}); - describe("factories", () => { describe("UserFactory", () => { it("creates records without input parameters", async () => { @@ -58,18 +43,4 @@ describe("factories", () => { expect(userWithPosts?.posts.length).toBe(3); }); }); - - describe("PostFactoryAlt", () => { - it("creates post record", async () => { - await PostFactoryAlt.create(); - await expect(prisma.user.count()).resolves.toBe(1); - }); - - it("creates related user at most one", async () => { - await PostFactoryAlt.create(); - await PostFactoryAlt.create(); - await PostFactoryAlt.create(); - await expect(prisma.user.count()).resolves.toBe(1); - }); - }); }); diff --git a/examples/example-prj/src/sequence.test.ts b/examples/example-prj/src/sequence.test.ts new file mode 100644 index 00000000..49ccf733 --- /dev/null +++ b/examples/example-prj/src/sequence.test.ts @@ -0,0 +1,28 @@ +import { defineUserFactory, resetSequence } from "./__generated__/fabbrica"; + +const prisma = jestPrisma.client; + +const UserFactory = defineUserFactory({ + defaultData: async ({ seq }) => ({ + id: "user" + `${seq}`.padStart(3, "0"), + }), +}); + +describe("factories", () => { + describe("UserFactory", () => { + beforeEach(async () => { + resetSequence(); + await UserFactory.create(); + }); + + it("creates record with sequential id", async () => { + await expect(prisma.user.findUnique({ where: { id: "user000" } })).resolves.not.toBeNull(); + }); + + it("expected that the sequential id is restarted", async () => { + await UserFactory.create(); + await expect(prisma.user.findUnique({ where: { id: "user000" } })).resolves.not.toBeNull(); + await expect(prisma.user.findUnique({ where: { id: "user001" } })).resolves.not.toBeNull(); + }); + }); +}); diff --git a/packages/prisma-fabbrica/src/helpers/index.ts b/packages/prisma-fabbrica/src/helpers/index.ts index 293a25bd..79ffdbfb 100644 --- a/packages/prisma-fabbrica/src/helpers/index.ts +++ b/packages/prisma-fabbrica/src/helpers/index.ts @@ -1,3 +1,4 @@ export * from "./valueResolver"; export * from "./stringConverter"; export * from "./astShorthand"; +export * from "./sequence"; diff --git a/packages/prisma-fabbrica/src/helpers/sequence.ts b/packages/prisma-fabbrica/src/helpers/sequence.ts new file mode 100644 index 00000000..63e1b9c9 --- /dev/null +++ b/packages/prisma-fabbrica/src/helpers/sequence.ts @@ -0,0 +1,15 @@ +let sequenceCounterMap: WeakMap = new Map(); + +export function resetSequence() { + sequenceCounterMap = new Map(); +} + +export function getSequenceCounter(key: any) { + const c = sequenceCounterMap.get(key); + if (c == null) { + sequenceCounterMap.set(key, 0); + return 0; + } + sequenceCounterMap.set(key, c + 1); + return c + 1; +} diff --git a/packages/prisma-fabbrica/src/helpers/valueResolver.test.ts b/packages/prisma-fabbrica/src/helpers/valueResolver.test.ts new file mode 100644 index 00000000..94b3507b --- /dev/null +++ b/packages/prisma-fabbrica/src/helpers/valueResolver.test.ts @@ -0,0 +1,29 @@ +import { Resolver, normalizeResolver } from "./valueResolver"; + +type TestResolver = Resolver<{ value: string }, { seq: number }>; + +describe(normalizeResolver, () => { + test("with plain object", async () => { + const r: TestResolver = { + value: "hoge", + }; + const resolver = normalizeResolver<{ value: string }, { seq: number }>(r); + await expect(resolver({ seq: 1 })).resolves.toEqual({ value: "hoge" }); + }); + + test("with sync function", async () => { + const r: TestResolver = ({ seq }) => ({ + value: `${seq}`, + }); + const resolver = normalizeResolver<{ value: string }, { seq: number }>(r); + await expect(resolver({ seq: 1 })).resolves.toEqual({ value: "1" }); + }); + + test("with async function", async () => { + const r: TestResolver = async ({ seq }) => ({ + value: `${await seq}`, + }); + const resolver = normalizeResolver<{ value: string }, { seq: number }>(r); + await expect(resolver({ seq: 1 })).resolves.toEqual({ value: "1" }); + }); +}); diff --git a/packages/prisma-fabbrica/src/helpers/valueResolver.ts b/packages/prisma-fabbrica/src/helpers/valueResolver.ts index edfdea7f..0e3bac83 100644 --- a/packages/prisma-fabbrica/src/helpers/valueResolver.ts +++ b/packages/prisma-fabbrica/src/helpers/valueResolver.ts @@ -1,6 +1,13 @@ -export type Resolver> = T | (() => T) | (() => PromiseLike); +export type Resolver, S extends Record> = + | T + | ((opt: S) => T) + | ((opt: S) => PromiseLike); -export async function resolveValue>(resolver: Resolver) { - const fn = typeof resolver === "function" ? resolver : () => Promise.resolve(resolver); - return (await fn()) as T; +export function normalizeResolver, S extends Record>( + resolver: Resolver, +): (opt: S) => Promise { + return async (opt: S) => { + const fn = typeof resolver === "function" ? resolver : () => Promise.resolve(resolver); + return (await fn(opt)) as T; + }; } diff --git a/packages/prisma-fabbrica/src/initialize.ts b/packages/prisma-fabbrica/src/initialize.ts index bf4c2e85..84da06d1 100644 --- a/packages/prisma-fabbrica/src/initialize.ts +++ b/packages/prisma-fabbrica/src/initialize.ts @@ -1,4 +1,6 @@ import { resetClient, setClient, PrismaClientLike } from "./clientHolder"; +import { resetSequence } from "./helpers"; +export { resetSequence } from "./helpers"; export type InitializeOptions = { readonly prisma: PrismaClientLike | (() => PrismaClientLike); @@ -6,8 +8,10 @@ export type InitializeOptions = { export function reset() { resetClient(); + resetSequence(); } export function initialize(options: InitializeOptions) { setClient(options.prisma); + resetSequence(); } diff --git a/packages/prisma-fabbrica/src/scalar/gen.ts b/packages/prisma-fabbrica/src/scalar/gen.ts index 7930ad2c..e5b09375 100644 --- a/packages/prisma-fabbrica/src/scalar/gen.ts +++ b/packages/prisma-fabbrica/src/scalar/gen.ts @@ -9,9 +9,9 @@ const scalarFieldValueGenerator: ScalarFieldValueGenerator = { } return `${fieldName} field`; }, - Int: ({ isId, isUnique }) => { + Int: ({ isId, isUnique, seq }) => { if (isId || isUnique) { - return Date.now(); + return seq; } return 10; }, diff --git a/packages/prisma-fabbrica/src/scalar/types.ts b/packages/prisma-fabbrica/src/scalar/types.ts index cc6fdecd..65668f0b 100644 --- a/packages/prisma-fabbrica/src/scalar/types.ts +++ b/packages/prisma-fabbrica/src/scalar/types.ts @@ -3,6 +3,7 @@ export type ScalarFieldGenerateOptions = { readonly fieldName: string; readonly isUnique: boolean; readonly isId: boolean; + readonly seq: number; }; export interface ScalarFieldValueGenerator { diff --git a/packages/prisma-fabbrica/src/scripts/jest-prisma/setup.ts b/packages/prisma-fabbrica/src/scripts/jest-prisma/setup.ts index ee14f043..f8d67091 100644 --- a/packages/prisma-fabbrica/src/scripts/jest-prisma/setup.ts +++ b/packages/prisma-fabbrica/src/scripts/jest-prisma/setup.ts @@ -1,4 +1,5 @@ import { initialize } from "../../initialize"; +import { resetSequence } from "../../helpers"; beforeAll(() => { if (typeof jestPrisma === "object") { @@ -8,4 +9,6 @@ beforeAll(() => { } }); +beforeEach(() => resetSequence()); + declare var jestPrisma: any; diff --git a/packages/prisma-fabbrica/src/templates/__snapshots__/getSourceFile.test.ts.snap b/packages/prisma-fabbrica/src/templates/__snapshots__/getSourceFile.test.ts.snap index 379c7ef9..45341bf8 100644 --- a/packages/prisma-fabbrica/src/templates/__snapshots__/getSourceFile.test.ts.snap +++ b/packages/prisma-fabbrica/src/templates/__snapshots__/getSourceFile.test.ts.snap @@ -6,8 +6,11 @@ import { Prisma } from "@prisma/client"; import type { PrismaClient } from "@prisma/client"; import { getClient } from "@quramy/prisma-fabbrica/lib/clientHolder"; import scalarFieldValueGenerator from "@quramy/prisma-fabbrica/lib/scalar/gen"; -import { Resolver, resolveValue } from "@quramy/prisma-fabbrica/lib/helpers"; -export { initialize } from "@quramy/prisma-fabbrica"; +import { Resolver, normalizeResolver, getSequenceCounter } from "@quramy/prisma-fabbrica/lib/helpers"; +export { initialize, resetSequence } from "@quramy/prisma-fabbrica"; +type BuildDataOptions = { + readonly seq: number; +}; type UserScalarOrEnumFields = { id: number; name: string; @@ -17,18 +20,24 @@ type UserFactoryDefineInput = { name?: string; }; type UserFactoryDefineOptions = { - defaultData?: Resolver; + defaultData?: Resolver; }; -function autoGenerateUserScalarsOrEnums(): UserScalarOrEnumFields { +function autoGenerateUserScalarsOrEnums({ seq }: { + readonly seq: number; +}): UserScalarOrEnumFields { return { - id: scalarFieldValueGenerator.Int({ modelName: "User", fieldName: "id", isId: true, isUnique: false }), - name: scalarFieldValueGenerator.String({ modelName: "User", fieldName: "name", isId: false, isUnique: false }) + id: scalarFieldValueGenerator.Int({ modelName: "User", fieldName: "id", isId: true, isUnique: false, seq }), + name: scalarFieldValueGenerator.String({ modelName: "User", fieldName: "name", isId: false, isUnique: false, seq }) }; } function defineUserFactoryInternal({ defaultData: defaultDataResolver }: UserFactoryDefineOptions) { + const seqKey = {}; + const getSeq = () => getSequenceCounter(seqKey); const buildCreateInput = async (inputData: Partial = {}) => { - const requiredScalarData = autoGenerateUserScalarsOrEnums(); - const defaultData = await resolveValue(defaultDataResolver ?? {}); + const seq = getSeq(); + const requiredScalarData = autoGenerateUserScalarsOrEnums({ seq }); + const resolveValue = normalizeResolver(defaultDataResolver ?? {}); + const defaultData = await resolveValue({ seq }); const defaultAssociations = {}; const data: Prisma.UserCreateInput = { ...requiredScalarData, ...defaultData, ...defaultAssociations, ...inputData }; return data; diff --git a/packages/prisma-fabbrica/src/templates/autoGenerateModelScalarsOrEnumsFieldArgs.test.ts b/packages/prisma-fabbrica/src/templates/autoGenerateModelScalarsOrEnumsFieldArgs.test.ts index e8f6641f..25f1e112 100644 --- a/packages/prisma-fabbrica/src/templates/autoGenerateModelScalarsOrEnumsFieldArgs.test.ts +++ b/packages/prisma-fabbrica/src/templates/autoGenerateModelScalarsOrEnumsFieldArgs.test.ts @@ -13,7 +13,7 @@ describe(autoGenerateModelScalarsOrEnumsFieldArgs, () => { `, targetField: "id", expected: ` - scalarFieldValueGenerator.Int({ modelName: "TestModel", fieldName: "id", isId: true, isUnique: false }) + scalarFieldValueGenerator.Int({ modelName: "TestModel", fieldName: "id", isId: true, isUnique: false, seq }) `, }, { @@ -26,7 +26,7 @@ describe(autoGenerateModelScalarsOrEnumsFieldArgs, () => { `, targetField: "complexIdField", expected: ` - scalarFieldValueGenerator.Int({ modelName: "TestModel", fieldName: "complexIdField", isId: true, isUnique: false }) + scalarFieldValueGenerator.Int({ modelName: "TestModel", fieldName: "complexIdField", isId: true, isUnique: false, seq }) `, }, { @@ -38,7 +38,7 @@ describe(autoGenerateModelScalarsOrEnumsFieldArgs, () => { `, targetField: "uniqueField", expected: ` - scalarFieldValueGenerator.String({ modelName: "TestModel", fieldName: "uniqueField", isId: false, isUnique: true }) + scalarFieldValueGenerator.String({ modelName: "TestModel", fieldName: "uniqueField", isId: false, isUnique: true, seq }) `, }, { @@ -50,7 +50,7 @@ describe(autoGenerateModelScalarsOrEnumsFieldArgs, () => { `, targetField: "booleanField", expected: ` - scalarFieldValueGenerator.Boolean({ modelName: "TestModel", fieldName: "booleanField", isId: false, isUnique: false }) + scalarFieldValueGenerator.Boolean({ modelName: "TestModel", fieldName: "booleanField", isId: false, isUnique: false, seq }) `, }, { @@ -62,7 +62,7 @@ describe(autoGenerateModelScalarsOrEnumsFieldArgs, () => { `, targetField: "stringField", expected: ` - scalarFieldValueGenerator.String({ modelName: "TestModel", fieldName: "stringField", isId: false, isUnique: false }) + scalarFieldValueGenerator.String({ modelName: "TestModel", fieldName: "stringField", isId: false, isUnique: false, seq }) `, }, { @@ -74,7 +74,7 @@ describe(autoGenerateModelScalarsOrEnumsFieldArgs, () => { `, targetField: "intField", expected: ` - scalarFieldValueGenerator.Int({ modelName: "TestModel", fieldName: "intField", isId: false, isUnique: false }) + scalarFieldValueGenerator.Int({ modelName: "TestModel", fieldName: "intField", isId: false, isUnique: false, seq }) `, }, { @@ -86,7 +86,7 @@ describe(autoGenerateModelScalarsOrEnumsFieldArgs, () => { `, targetField: "floatField", expected: ` - scalarFieldValueGenerator.Float({ modelName: "TestModel", fieldName: "floatField", isId: false, isUnique: false }) + scalarFieldValueGenerator.Float({ modelName: "TestModel", fieldName: "floatField", isId: false, isUnique: false, seq }) `, }, { @@ -98,7 +98,7 @@ describe(autoGenerateModelScalarsOrEnumsFieldArgs, () => { `, targetField: "bigIntField", expected: ` - scalarFieldValueGenerator.BigInt({ modelName: "TestModel", fieldName: "bigIntField", isId: false, isUnique: false }) + scalarFieldValueGenerator.BigInt({ modelName: "TestModel", fieldName: "bigIntField", isId: false, isUnique: false, seq }) `, }, { @@ -110,7 +110,7 @@ describe(autoGenerateModelScalarsOrEnumsFieldArgs, () => { `, targetField: "dateTimeField", expected: ` - scalarFieldValueGenerator.DateTime({ modelName: "TestModel", fieldName: "dateTimeField", isId: false, isUnique: false }) + scalarFieldValueGenerator.DateTime({ modelName: "TestModel", fieldName: "dateTimeField", isId: false, isUnique: false, seq }) `, }, { @@ -122,7 +122,7 @@ describe(autoGenerateModelScalarsOrEnumsFieldArgs, () => { `, targetField: "bytesField", expected: ` - scalarFieldValueGenerator.Bytes({ modelName: "TestModel", fieldName: "bytesField", isId: false, isUnique: false }) + scalarFieldValueGenerator.Bytes({ modelName: "TestModel", fieldName: "bytesField", isId: false, isUnique: false, seq }) `, }, { @@ -134,7 +134,7 @@ describe(autoGenerateModelScalarsOrEnumsFieldArgs, () => { `, targetField: "jsonField", expected: ` - scalarFieldValueGenerator.Json({ modelName: "TestModel", fieldName: "jsonField", isId: false, isUnique: false }) + scalarFieldValueGenerator.Json({ modelName: "TestModel", fieldName: "jsonField", isId: false, isUnique: false, seq }) `, }, { diff --git a/packages/prisma-fabbrica/src/templates/index.ts b/packages/prisma-fabbrica/src/templates/index.ts index 50384519..9944e354 100644 --- a/packages/prisma-fabbrica/src/templates/index.ts +++ b/packages/prisma-fabbrica/src/templates/index.ts @@ -69,8 +69,12 @@ export const header = (prismaClientModuleSpecifier: string) => import type { PrismaClient } from ${() => ast.stringLiteral(prismaClientModuleSpecifier)}; import { getClient } from "@quramy/prisma-fabbrica/lib/clientHolder"; import scalarFieldValueGenerator from "@quramy/prisma-fabbrica/lib/scalar/gen"; - import { Resolver, resolveValue } from "@quramy/prisma-fabbrica/lib/helpers"; - export { initialize } from "@quramy/prisma-fabbrica"; + import { Resolver, normalizeResolver, getSequenceCounter } from "@quramy/prisma-fabbrica/lib/helpers"; + export { initialize, resetSequence } from "@quramy/prisma-fabbrica"; + + type BuildDataOptions = { + readonly seq: number; + }; `(); export const importStatement = (specifier: string, prismaClientModuleSpecifier: string) => @@ -186,7 +190,7 @@ export const modelFactoryDefineOptions = (modelName: string, isOpionalDefaultDat isOpionalDefaultData ? template.statement` type MODEL_FACTORY_DEFINE_OPTIONS = { - defaultData?: Resolver; + defaultData?: Resolver; }; `({ MODEL_FACTORY_DEFINE_OPTIONS: ast.identifier(`${modelName}FactoryDefineOptions`), @@ -194,7 +198,7 @@ export const modelFactoryDefineOptions = (modelName: string, isOpionalDefaultDat }) : template.statement` type MODEL_FACTORY_DEFINE_OPTIONS = { - defaultData: Resolver; + defaultData: Resolver; }; `({ MODEL_FACTORY_DEFINE_OPTIONS: ast.identifier(`${modelName}FactoryDefineOptions`), @@ -222,7 +226,7 @@ export const autoGenerateModelScalarsOrEnumsFieldArgs = ( ) => field.inputTypes[0].location === "scalar" ? template.expression` - scalarFieldValueGenerator.SCALAR_TYPE({ modelName: MODEL_NAME, fieldName: FIELD_NAME, isId: IS_ID, isUnique: IS_UNIQUE }) + scalarFieldValueGenerator.SCALAR_TYPE({ modelName: MODEL_NAME, fieldName: FIELD_NAME, isId: IS_ID, isUnique: IS_UNIQUE, seq }) `({ SCALAR_TYPE: ast.identifier(field.inputTypes[0].type as string), MODEL_NAME: ast.stringLiteral(model.name), @@ -241,7 +245,7 @@ export const autoGenerateModelScalarsOrEnums = ( enums: DMMF.SchemaEnum[], ) => template.statement` - function AUTO_GENERATE_MODEL_SCALARS_OR_ENUMS(): MODEL_SCALAR_OR_ENUM_FIELDS { + function AUTO_GENERATE_MODEL_SCALARS_OR_ENUMS({ seq }: { readonly seq: number }): MODEL_SCALAR_OR_ENUM_FIELDS { return ${() => ast.objectLiteralExpression( filterRequiredScalarOrEnumFields(inputType).map(field => @@ -260,11 +264,17 @@ export const defineModelFactoryInernal = (model: DMMF.Model, inputType: DMMF.Inp function DEFINE_MODEL_FACTORY_INERNAL({ defaultData: defaultDataResolver }: MODEL_FACTORY_DEFINE_OPTIONS) { + + const seqKey = {}; + const getSeq = () => getSequenceCounter(seqKey); + const buildCreateInput = async ( inputData: Partial = {} ) => { - const requiredScalarData = AUTO_GENERATE_MODEL_SCALARS_OR_ENUMS() - const defaultData= await resolveValue(defaultDataResolver ?? {}); + const seq = getSeq(); + const requiredScalarData = AUTO_GENERATE_MODEL_SCALARS_OR_ENUMS({ seq }) + const resolveValue = normalizeResolver(defaultDataResolver ?? {}); + const defaultData = await resolveValue({ seq }); const defaultAssociations = ${() => ast.objectLiteralExpression( filterBelongsToField(model, inputType).map(field => @@ -285,6 +295,7 @@ export const defineModelFactoryInernal = (model: DMMF.Model, inputType: DMMF.Inp const data: Prisma.MODEL_CREATE_INPUT = { ...requiredScalarData, ...defaultData, ...defaultAssociations, ...inputData}; return data; }; + const pickForConnect = (inputData: ${() => ast.typeReferenceNode(model.name)}) => ( ${() => ast.objectLiteralExpression( @@ -294,13 +305,16 @@ export const defineModelFactoryInernal = (model: DMMF.Model, inputType: DMMF.Inp true, )} ); + const create = async ( inputData: Partial = {} ) => { const data = await buildCreateInput(inputData); return await getClient().MODEL_KEY.create({ data }); }; + const createForConnect = (inputData: Partial = {}) => create(inputData).then(pickForConnect); + return { _factoryFor: ${() => ast.stringLiteral(model.name)} as const, buildCreateInput, @@ -312,6 +326,7 @@ export const defineModelFactoryInernal = (model: DMMF.Model, inputType: DMMF.Inp `({ MODEL_KEY: ast.identifier(camelize(model.name)), DEFINE_MODEL_FACTORY_INERNAL: ast.identifier(`define${model.name}FactoryInternal`), + MODEL_FACTORY_DEFINE_INPUT: ast.identifier(`${model.name}FactoryDefineInput`), MODEL_FACTORY_DEFINE_OPTIONS: ast.identifier(`${model.name}FactoryDefineOptions`), MODEL_CREATE_INPUT: ast.identifier(`${model.name}CreateInput`), AUTO_GENERATE_MODEL_SCALARS_OR_ENUMS: ast.identifier(`autoGenerate${model.name}ScalarsOrEnums`), diff --git a/packages/ts-compile-testing/fixtures/field-variation/__generated__/fabbrica/index.ts b/packages/ts-compile-testing/fixtures/field-variation/__generated__/fabbrica/index.ts index 67265b4d..07085cde 100644 --- a/packages/ts-compile-testing/fixtures/field-variation/__generated__/fabbrica/index.ts +++ b/packages/ts-compile-testing/fixtures/field-variation/__generated__/fabbrica/index.ts @@ -5,8 +5,11 @@ import { Prisma } from "./../client"; import type { PrismaClient } from "./../client"; import { getClient } from "@quramy/prisma-fabbrica/lib/clientHolder"; import scalarFieldValueGenerator from "@quramy/prisma-fabbrica/lib/scalar/gen"; -import { Resolver, resolveValue } from "@quramy/prisma-fabbrica/lib/helpers"; -export { initialize } from "@quramy/prisma-fabbrica"; +import { Resolver, normalizeResolver, getSequenceCounter } from "@quramy/prisma-fabbrica/lib/helpers"; +export { initialize, resetSequence } from "@quramy/prisma-fabbrica"; +type BuildDataOptions = { + readonly seq: number; +}; type UserScalarOrEnumFields = { id: string; role: Role; @@ -18,18 +21,24 @@ type UserFactoryDefineInput = { roles?: Prisma.UserCreaterolesInput | Prisma.Enumerable; }; type UserFactoryDefineOptions = { - defaultData?: Resolver; + defaultData?: Resolver; }; -function autoGenerateUserScalarsOrEnums(): UserScalarOrEnumFields { +function autoGenerateUserScalarsOrEnums({ seq }: { + readonly seq: number; +}): UserScalarOrEnumFields { return { - id: scalarFieldValueGenerator.String({ modelName: "User", fieldName: "id", isId: true, isUnique: false }), + id: scalarFieldValueGenerator.String({ modelName: "User", fieldName: "id", isId: true, isUnique: false, seq }), role: "USER" }; } function defineUserFactoryInternal({ defaultData: defaultDataResolver }: UserFactoryDefineOptions) { + const seqKey = {}; + const getSeq = () => getSequenceCounter(seqKey); const buildCreateInput = async (inputData: Partial = {}) => { - const requiredScalarData = autoGenerateUserScalarsOrEnums(); - const defaultData = await resolveValue(defaultDataResolver ?? {}); + const seq = getSeq(); + const requiredScalarData = autoGenerateUserScalarsOrEnums({ seq }); + const resolveValue = normalizeResolver(defaultDataResolver ?? {}); + const defaultData = await resolveValue({ seq }); const defaultAssociations = {}; const data: Prisma.UserCreateInput = { ...requiredScalarData, ...defaultData, ...defaultAssociations, ...inputData }; return data; @@ -62,18 +71,24 @@ type ComplexIdModelFactoryDefineInput = { lastName?: string; }; type ComplexIdModelFactoryDefineOptions = { - defaultData?: Resolver; + defaultData?: Resolver; }; -function autoGenerateComplexIdModelScalarsOrEnums(): ComplexIdModelScalarOrEnumFields { +function autoGenerateComplexIdModelScalarsOrEnums({ seq }: { + readonly seq: number; +}): ComplexIdModelScalarOrEnumFields { return { - firstName: scalarFieldValueGenerator.String({ modelName: "ComplexIdModel", fieldName: "firstName", isId: true, isUnique: false }), - lastName: scalarFieldValueGenerator.String({ modelName: "ComplexIdModel", fieldName: "lastName", isId: true, isUnique: false }) + firstName: scalarFieldValueGenerator.String({ modelName: "ComplexIdModel", fieldName: "firstName", isId: true, isUnique: false, seq }), + lastName: scalarFieldValueGenerator.String({ modelName: "ComplexIdModel", fieldName: "lastName", isId: true, isUnique: false, seq }) }; } function defineComplexIdModelFactoryInternal({ defaultData: defaultDataResolver }: ComplexIdModelFactoryDefineOptions) { + const seqKey = {}; + const getSeq = () => getSequenceCounter(seqKey); const buildCreateInput = async (inputData: Partial = {}) => { - const requiredScalarData = autoGenerateComplexIdModelScalarsOrEnums(); - const defaultData = await resolveValue(defaultDataResolver ?? {}); + const seq = getSeq(); + const requiredScalarData = autoGenerateComplexIdModelScalarsOrEnums({ seq }); + const resolveValue = normalizeResolver(defaultDataResolver ?? {}); + const defaultData = await resolveValue({ seq }); const defaultAssociations = {}; const data: Prisma.ComplexIdModelCreateInput = { ...requiredScalarData, ...defaultData, ...defaultAssociations, ...inputData }; return data; diff --git a/packages/ts-compile-testing/fixtures/field-variation/sample.ts b/packages/ts-compile-testing/fixtures/field-variation/sample.ts new file mode 100644 index 00000000..cd35febb --- /dev/null +++ b/packages/ts-compile-testing/fixtures/field-variation/sample.ts @@ -0,0 +1,7 @@ +import { defineUserFactory } from "./__generated__/fabbrica"; + +export const UserFactoryWithSeq = defineUserFactory({ + defaultData: async ({ seq }) => ({ + id: seq.toString(), + }), +}); diff --git a/packages/ts-compile-testing/fixtures/relations-many-to-many/__generated__/fabbrica/index.ts b/packages/ts-compile-testing/fixtures/relations-many-to-many/__generated__/fabbrica/index.ts index 75afe714..6c84c1e8 100644 --- a/packages/ts-compile-testing/fixtures/relations-many-to-many/__generated__/fabbrica/index.ts +++ b/packages/ts-compile-testing/fixtures/relations-many-to-many/__generated__/fabbrica/index.ts @@ -4,8 +4,11 @@ import { Prisma } from "./../client"; import type { PrismaClient } from "./../client"; import { getClient } from "@quramy/prisma-fabbrica/lib/clientHolder"; import scalarFieldValueGenerator from "@quramy/prisma-fabbrica/lib/scalar/gen"; -import { Resolver, resolveValue } from "@quramy/prisma-fabbrica/lib/helpers"; -export { initialize } from "@quramy/prisma-fabbrica"; +import { Resolver, normalizeResolver, getSequenceCounter } from "@quramy/prisma-fabbrica/lib/helpers"; +export { initialize, resetSequence } from "@quramy/prisma-fabbrica"; +type BuildDataOptions = { + readonly seq: number; +}; type PostScalarOrEnumFields = { id: string; title: string; @@ -16,18 +19,24 @@ type PostFactoryDefineInput = { categories?: Prisma.CategoryCreateNestedManyWithoutPostsInput; }; type PostFactoryDefineOptions = { - defaultData?: Resolver; + defaultData?: Resolver; }; -function autoGeneratePostScalarsOrEnums(): PostScalarOrEnumFields { +function autoGeneratePostScalarsOrEnums({ seq }: { + readonly seq: number; +}): PostScalarOrEnumFields { return { - id: scalarFieldValueGenerator.String({ modelName: "Post", fieldName: "id", isId: true, isUnique: false }), - title: scalarFieldValueGenerator.String({ modelName: "Post", fieldName: "title", isId: false, isUnique: false }) + id: scalarFieldValueGenerator.String({ modelName: "Post", fieldName: "id", isId: true, isUnique: false, seq }), + title: scalarFieldValueGenerator.String({ modelName: "Post", fieldName: "title", isId: false, isUnique: false, seq }) }; } function definePostFactoryInternal({ defaultData: defaultDataResolver }: PostFactoryDefineOptions) { + const seqKey = {}; + const getSeq = () => getSequenceCounter(seqKey); const buildCreateInput = async (inputData: Partial = {}) => { - const requiredScalarData = autoGeneratePostScalarsOrEnums(); - const defaultData = await resolveValue(defaultDataResolver ?? {}); + const seq = getSeq(); + const requiredScalarData = autoGeneratePostScalarsOrEnums({ seq }); + const resolveValue = normalizeResolver(defaultDataResolver ?? {}); + const defaultData = await resolveValue({ seq }); const defaultAssociations = {}; const data: Prisma.PostCreateInput = { ...requiredScalarData, ...defaultData, ...defaultAssociations, ...inputData }; return data; @@ -61,18 +70,24 @@ type CategoryFactoryDefineInput = { posts?: Prisma.PostCreateNestedManyWithoutCategoriesInput; }; type CategoryFactoryDefineOptions = { - defaultData?: Resolver; + defaultData?: Resolver; }; -function autoGenerateCategoryScalarsOrEnums(): CategoryScalarOrEnumFields { +function autoGenerateCategoryScalarsOrEnums({ seq }: { + readonly seq: number; +}): CategoryScalarOrEnumFields { return { - id: scalarFieldValueGenerator.String({ modelName: "Category", fieldName: "id", isId: true, isUnique: false }), - name: scalarFieldValueGenerator.String({ modelName: "Category", fieldName: "name", isId: false, isUnique: false }) + id: scalarFieldValueGenerator.String({ modelName: "Category", fieldName: "id", isId: true, isUnique: false, seq }), + name: scalarFieldValueGenerator.String({ modelName: "Category", fieldName: "name", isId: false, isUnique: false, seq }) }; } function defineCategoryFactoryInternal({ defaultData: defaultDataResolver }: CategoryFactoryDefineOptions) { + const seqKey = {}; + const getSeq = () => getSequenceCounter(seqKey); const buildCreateInput = async (inputData: Partial = {}) => { - const requiredScalarData = autoGenerateCategoryScalarsOrEnums(); - const defaultData = await resolveValue(defaultDataResolver ?? {}); + const seq = getSeq(); + const requiredScalarData = autoGenerateCategoryScalarsOrEnums({ seq }); + const resolveValue = normalizeResolver(defaultDataResolver ?? {}); + const defaultData = await resolveValue({ seq }); const defaultAssociations = {}; const data: Prisma.CategoryCreateInput = { ...requiredScalarData, ...defaultData, ...defaultAssociations, ...inputData }; return data; 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 f315986e..416e9b41 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 @@ -5,8 +5,11 @@ import { Prisma } from "./../client"; import type { PrismaClient } from "./../client"; import { getClient } from "@quramy/prisma-fabbrica/lib/clientHolder"; import scalarFieldValueGenerator from "@quramy/prisma-fabbrica/lib/scalar/gen"; -import { Resolver, resolveValue } from "@quramy/prisma-fabbrica/lib/helpers"; -export { initialize } from "@quramy/prisma-fabbrica"; +import { Resolver, normalizeResolver, getSequenceCounter } from "@quramy/prisma-fabbrica/lib/helpers"; +export { initialize, resetSequence } from "@quramy/prisma-fabbrica"; +type BuildDataOptions = { + readonly seq: number; +}; type UserScalarOrEnumFields = { id: string; name: string; @@ -18,18 +21,24 @@ type UserFactoryDefineInput = { reviews?: Prisma.ReviewCreateNestedManyWithoutReviewerInput; }; type UserFactoryDefineOptions = { - defaultData?: Resolver; + defaultData?: Resolver; }; -function autoGenerateUserScalarsOrEnums(): UserScalarOrEnumFields { +function autoGenerateUserScalarsOrEnums({ seq }: { + readonly seq: number; +}): UserScalarOrEnumFields { return { - id: scalarFieldValueGenerator.String({ modelName: "User", fieldName: "id", isId: true, isUnique: false }), - name: scalarFieldValueGenerator.String({ modelName: "User", fieldName: "name", isId: false, isUnique: false }) + id: scalarFieldValueGenerator.String({ modelName: "User", fieldName: "id", isId: true, isUnique: false, seq }), + name: scalarFieldValueGenerator.String({ modelName: "User", fieldName: "name", isId: false, isUnique: false, seq }) }; } function defineUserFactoryInternal({ defaultData: defaultDataResolver }: UserFactoryDefineOptions) { + const seqKey = {}; + const getSeq = () => getSequenceCounter(seqKey); const buildCreateInput = async (inputData: Partial = {}) => { - const requiredScalarData = autoGenerateUserScalarsOrEnums(); - const defaultData = await resolveValue(defaultDataResolver ?? {}); + const seq = getSeq(); + const requiredScalarData = autoGenerateUserScalarsOrEnums({ seq }); + const resolveValue = normalizeResolver(defaultDataResolver ?? {}); + const defaultData = await resolveValue({ seq }); const defaultAssociations = {}; const data: Prisma.UserCreateInput = { ...requiredScalarData, ...defaultData, ...defaultAssociations, ...inputData }; return data; @@ -68,21 +77,27 @@ type PostFactoryDefineInput = { reviews?: Prisma.ReviewCreateNestedManyWithoutPostInput; }; type PostFactoryDefineOptions = { - defaultData?: Resolver; + defaultData?: Resolver; }; function isPostauthorFactory(x: PostauthorFactory | Prisma.UserCreateNestedOneWithoutPostsInput | undefined): x is PostauthorFactory { return (x as any)?._factoryFor === "User"; } -function autoGeneratePostScalarsOrEnums(): PostScalarOrEnumFields { +function autoGeneratePostScalarsOrEnums({ seq }: { + readonly seq: number; +}): PostScalarOrEnumFields { return { - id: scalarFieldValueGenerator.String({ modelName: "Post", fieldName: "id", isId: true, isUnique: false }), - title: scalarFieldValueGenerator.String({ modelName: "Post", fieldName: "title", isId: false, isUnique: false }) + id: scalarFieldValueGenerator.String({ modelName: "Post", fieldName: "id", isId: true, isUnique: false, seq }), + title: scalarFieldValueGenerator.String({ modelName: "Post", fieldName: "title", isId: false, isUnique: false, seq }) }; } function definePostFactoryInternal({ defaultData: defaultDataResolver }: PostFactoryDefineOptions) { + const seqKey = {}; + const getSeq = () => getSequenceCounter(seqKey); const buildCreateInput = async (inputData: Partial = {}) => { - const requiredScalarData = autoGeneratePostScalarsOrEnums(); - const defaultData = await resolveValue(defaultDataResolver ?? {}); + const seq = getSeq(); + const requiredScalarData = autoGeneratePostScalarsOrEnums({ seq }); + const resolveValue = normalizeResolver(defaultDataResolver ?? {}); + const defaultData = await resolveValue({ seq }); const defaultAssociations = { author: isPostauthorFactory(defaultData.author) ? { create: await defaultData.author.buildCreateInput() @@ -129,7 +144,7 @@ type ReviewFactoryDefineInput = { reviewer: ReviewreviewerFactory | Prisma.UserCreateNestedOneWithoutReviewsInput; }; type ReviewFactoryDefineOptions = { - defaultData: Resolver; + defaultData: Resolver; }; function isReviewpostFactory(x: ReviewpostFactory | Prisma.PostCreateNestedOneWithoutReviewsInput | undefined): x is ReviewpostFactory { return (x as any)?._factoryFor === "Post"; @@ -137,16 +152,22 @@ function isReviewpostFactory(x: ReviewpostFactory | Prisma.PostCreateNestedOneWi function isReviewreviewerFactory(x: ReviewreviewerFactory | Prisma.UserCreateNestedOneWithoutReviewsInput | undefined): x is ReviewreviewerFactory { return (x as any)?._factoryFor === "User"; } -function autoGenerateReviewScalarsOrEnums(): ReviewScalarOrEnumFields { +function autoGenerateReviewScalarsOrEnums({ seq }: { + readonly seq: number; +}): ReviewScalarOrEnumFields { return { - id: scalarFieldValueGenerator.String({ modelName: "Review", fieldName: "id", isId: true, isUnique: false }), - body: scalarFieldValueGenerator.String({ modelName: "Review", fieldName: "body", isId: false, isUnique: false }) + id: scalarFieldValueGenerator.String({ modelName: "Review", fieldName: "id", isId: true, isUnique: false, seq }), + body: scalarFieldValueGenerator.String({ modelName: "Review", fieldName: "body", isId: false, isUnique: false, seq }) }; } function defineReviewFactoryInternal({ defaultData: defaultDataResolver }: ReviewFactoryDefineOptions) { + const seqKey = {}; + const getSeq = () => getSequenceCounter(seqKey); const buildCreateInput = async (inputData: Partial = {}) => { - const requiredScalarData = autoGenerateReviewScalarsOrEnums(); - const defaultData = await resolveValue(defaultDataResolver ?? {}); + const seq = getSeq(); + const requiredScalarData = autoGenerateReviewScalarsOrEnums({ seq }); + const resolveValue = normalizeResolver(defaultDataResolver ?? {}); + const defaultData = await resolveValue({ seq }); const defaultAssociations = { post: isReviewpostFactory(defaultData.post) ? { create: await defaultData.post.buildCreateInput() diff --git a/packages/ts-compile-testing/fixtures/relations-one-to-one/__generated__/fabbrica/index.ts b/packages/ts-compile-testing/fixtures/relations-one-to-one/__generated__/fabbrica/index.ts index 20374988..2c0d90e7 100644 --- a/packages/ts-compile-testing/fixtures/relations-one-to-one/__generated__/fabbrica/index.ts +++ b/packages/ts-compile-testing/fixtures/relations-one-to-one/__generated__/fabbrica/index.ts @@ -4,8 +4,11 @@ import { Prisma } from "./../client"; import type { PrismaClient } from "./../client"; import { getClient } from "@quramy/prisma-fabbrica/lib/clientHolder"; import scalarFieldValueGenerator from "@quramy/prisma-fabbrica/lib/scalar/gen"; -import { Resolver, resolveValue } from "@quramy/prisma-fabbrica/lib/helpers"; -export { initialize } from "@quramy/prisma-fabbrica"; +import { Resolver, normalizeResolver, getSequenceCounter } from "@quramy/prisma-fabbrica/lib/helpers"; +export { initialize, resetSequence } from "@quramy/prisma-fabbrica"; +type BuildDataOptions = { + readonly seq: number; +}; type UserScalarOrEnumFields = { id: string; name: string; @@ -20,21 +23,27 @@ type UserFactoryDefineInput = { profile?: UserprofileFactory | Prisma.ProfileCreateNestedOneWithoutUserInput; }; type UserFactoryDefineOptions = { - defaultData?: Resolver; + defaultData?: Resolver; }; function isUserprofileFactory(x: UserprofileFactory | Prisma.ProfileCreateNestedOneWithoutUserInput | undefined): x is UserprofileFactory { return (x as any)?._factoryFor === "Profile"; } -function autoGenerateUserScalarsOrEnums(): UserScalarOrEnumFields { +function autoGenerateUserScalarsOrEnums({ seq }: { + readonly seq: number; +}): UserScalarOrEnumFields { return { - id: scalarFieldValueGenerator.String({ modelName: "User", fieldName: "id", isId: true, isUnique: false }), - name: scalarFieldValueGenerator.String({ modelName: "User", fieldName: "name", isId: false, isUnique: false }) + id: scalarFieldValueGenerator.String({ modelName: "User", fieldName: "id", isId: true, isUnique: false, seq }), + name: scalarFieldValueGenerator.String({ modelName: "User", fieldName: "name", isId: false, isUnique: false, seq }) }; } function defineUserFactoryInternal({ defaultData: defaultDataResolver }: UserFactoryDefineOptions) { + const seqKey = {}; + const getSeq = () => getSequenceCounter(seqKey); const buildCreateInput = async (inputData: Partial = {}) => { - const requiredScalarData = autoGenerateUserScalarsOrEnums(); - const defaultData = await resolveValue(defaultDataResolver ?? {}); + const seq = getSeq(); + const requiredScalarData = autoGenerateUserScalarsOrEnums({ seq }); + const resolveValue = normalizeResolver(defaultDataResolver ?? {}); + const defaultData = await resolveValue({ seq }); const defaultAssociations = { profile: isUserprofileFactory(defaultData.profile) ? { create: await defaultData.profile.buildCreateInput() @@ -74,20 +83,26 @@ type ProfileFactoryDefineInput = { user: ProfileuserFactory | Prisma.UserCreateNestedOneWithoutProfileInput; }; type ProfileFactoryDefineOptions = { - defaultData: Resolver; + defaultData: Resolver; }; function isProfileuserFactory(x: ProfileuserFactory | Prisma.UserCreateNestedOneWithoutProfileInput | undefined): x is ProfileuserFactory { return (x as any)?._factoryFor === "User"; } -function autoGenerateProfileScalarsOrEnums(): ProfileScalarOrEnumFields { +function autoGenerateProfileScalarsOrEnums({ seq }: { + readonly seq: number; +}): ProfileScalarOrEnumFields { return { - id: scalarFieldValueGenerator.String({ modelName: "Profile", fieldName: "id", isId: true, isUnique: false }) + id: scalarFieldValueGenerator.String({ modelName: "Profile", fieldName: "id", isId: true, isUnique: false, seq }) }; } function defineProfileFactoryInternal({ defaultData: defaultDataResolver }: ProfileFactoryDefineOptions) { + const seqKey = {}; + const getSeq = () => getSequenceCounter(seqKey); const buildCreateInput = async (inputData: Partial = {}) => { - const requiredScalarData = autoGenerateProfileScalarsOrEnums(); - const defaultData = await resolveValue(defaultDataResolver ?? {}); + const seq = getSeq(); + const requiredScalarData = autoGenerateProfileScalarsOrEnums({ seq }); + const resolveValue = normalizeResolver(defaultDataResolver ?? {}); + const defaultData = await resolveValue({ seq }); const defaultAssociations = { user: isProfileuserFactory(defaultData.user) ? { create: await defaultData.user.buildCreateInput() diff --git a/packages/ts-compile-testing/fixtures/simple-model/__generated__/fabbrica/index.ts b/packages/ts-compile-testing/fixtures/simple-model/__generated__/fabbrica/index.ts index 41ecf916..13eff060 100644 --- a/packages/ts-compile-testing/fixtures/simple-model/__generated__/fabbrica/index.ts +++ b/packages/ts-compile-testing/fixtures/simple-model/__generated__/fabbrica/index.ts @@ -3,8 +3,11 @@ import { Prisma } from "./../client"; import type { PrismaClient } from "./../client"; import { getClient } from "@quramy/prisma-fabbrica/lib/clientHolder"; import scalarFieldValueGenerator from "@quramy/prisma-fabbrica/lib/scalar/gen"; -import { Resolver, resolveValue } from "@quramy/prisma-fabbrica/lib/helpers"; -export { initialize } from "@quramy/prisma-fabbrica"; +import { Resolver, normalizeResolver, getSequenceCounter } from "@quramy/prisma-fabbrica/lib/helpers"; +export { initialize, resetSequence } from "@quramy/prisma-fabbrica"; +type BuildDataOptions = { + readonly seq: number; +}; type UserScalarOrEnumFields = { id: string; name: string; @@ -14,18 +17,24 @@ type UserFactoryDefineInput = { name?: string; }; type UserFactoryDefineOptions = { - defaultData?: Resolver; + defaultData?: Resolver; }; -function autoGenerateUserScalarsOrEnums(): UserScalarOrEnumFields { +function autoGenerateUserScalarsOrEnums({ seq }: { + readonly seq: number; +}): UserScalarOrEnumFields { return { - id: scalarFieldValueGenerator.String({ modelName: "User", fieldName: "id", isId: true, isUnique: false }), - name: scalarFieldValueGenerator.String({ modelName: "User", fieldName: "name", isId: false, isUnique: false }) + id: scalarFieldValueGenerator.String({ modelName: "User", fieldName: "id", isId: true, isUnique: false, seq }), + name: scalarFieldValueGenerator.String({ modelName: "User", fieldName: "name", isId: false, isUnique: false, seq }) }; } function defineUserFactoryInternal({ defaultData: defaultDataResolver }: UserFactoryDefineOptions) { + const seqKey = {}; + const getSeq = () => getSequenceCounter(seqKey); const buildCreateInput = async (inputData: Partial = {}) => { - const requiredScalarData = autoGenerateUserScalarsOrEnums(); - const defaultData = await resolveValue(defaultDataResolver ?? {}); + const seq = getSeq(); + const requiredScalarData = autoGenerateUserScalarsOrEnums({ seq }); + const resolveValue = normalizeResolver(defaultDataResolver ?? {}); + const defaultData = await resolveValue({ seq }); const defaultAssociations = {}; const data: Prisma.UserCreateInput = { ...requiredScalarData, ...defaultData, ...defaultAssociations, ...inputData }; return data; From ec8049ebf08b57a39d8181878399c5729ace60d0 Mon Sep 17 00:00:00 2001 From: Quramy Date: Sun, 27 Nov 2022 23:45:03 +0900 Subject: [PATCH 02/10] chore: Modify jest option --- examples/example-prj/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/example-prj/package.json b/examples/example-prj/package.json index 527e7a9b..6d291a4f 100644 --- a/examples/example-prj/package.json +++ b/examples/example-prj/package.json @@ -7,8 +7,8 @@ "clean": "echo nothing to do", "migrate:test:ci": "DATABASE_URL=\"file:./test.db\" prisma migrate dev", "generate": "prisma generate", - "test": "jest", - "test:ci": "DATABASE_URL=\"file:./test.db\" jest" + "test": "DATABASE_URL=\"file:./test.db\" jest --maxWorkers=1", + "test:ci": "DATABASE_URL=\"file:./test.db\" jest --maxWorkers=1" }, "devDependencies": { "@quramy/jest-prisma-node": "1.1.2", From 75bfea591db2bf2c17bfc0cd19a714acf8a1c014 Mon Sep 17 00:00:00 2001 From: Quramy Date: Sun, 27 Nov 2022 23:45:17 +0900 Subject: [PATCH 03/10] docs: Write about seq --- README.md | 26 +++++++++++++++++++++++ examples/example-prj/src/sequence.test.ts | 2 +- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f32ddc99..6bb86a89 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,32 @@ const UserFactory = defineUserFactory({ await UserFactory.create() ``` +### Use sequence for scalar fields + +`seq` parameter provides sequential number which increments when called `.create()` . + +```ts +const UserFactory = defineUserFactory({ + defaultData: async ({ seq }) => ({ + id: `user${seq.toString().padStart(3, "0")}`, + }), +}); + +await UserFactory.create(); // Insert with id: "user000" +await UserFactory.create(); // Insert with id: "user001" +await UserFactory.create(); // Insert with id: "user002" +``` + +And the sequential number can be reset via `resetSequence` . + +```ts +/* your.testSetup.ts */ + +import { resetSequence } from "./__generated__/fabbrica"; + +beforeEach(() => resetSequence()); +``` + ### Required relation Sometimes, creating a model requires other model existence. For example, the following model `Post` belongs to other model `User`. diff --git a/examples/example-prj/src/sequence.test.ts b/examples/example-prj/src/sequence.test.ts index 49ccf733..7c663359 100644 --- a/examples/example-prj/src/sequence.test.ts +++ b/examples/example-prj/src/sequence.test.ts @@ -4,7 +4,7 @@ const prisma = jestPrisma.client; const UserFactory = defineUserFactory({ defaultData: async ({ seq }) => ({ - id: "user" + `${seq}`.padStart(3, "0"), + id: `user${seq.toString().padStart(3, "0")}`, }), }); From f91e26cbb2e9f72d1280d2b207abccc2c3bb01da Mon Sep 17 00:00:00 2001 From: Quramy Date: Sun, 27 Nov 2022 23:19:25 +0900 Subject: [PATCH 04/10] chore: Add screening function --- README.md | 1 + packages/prisma-fabbrica/src/helpers/index.ts | 1 + .../prisma-fabbrica/src/helpers/selectors.ts | 3 + .../prisma-fabbrica/src/relations/index.ts | 0 .../src/relations/screen.test.ts | 138 ++++++++++++++++++ .../prisma-fabbrica/src/relations/screen.ts | 107 ++++++++++++++ .../prisma-fabbrica/src/templates/index.ts | 6 +- 7 files changed, 251 insertions(+), 5 deletions(-) create mode 100644 packages/prisma-fabbrica/src/helpers/selectors.ts create mode 100644 packages/prisma-fabbrica/src/relations/index.ts create mode 100644 packages/prisma-fabbrica/src/relations/screen.test.ts create mode 100644 packages/prisma-fabbrica/src/relations/screen.ts diff --git a/README.md b/README.md index 6bb86a89..dda1b217 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ Prisma generator for model factories. - [Getting started](#getting-started) - [Usage of factories](#usage-of-factories) - [Field default values](#field-default-values) + - [Use sequence for scalar fields](#use-sequence-for-scalar-fields) - [Required relation](#required-relation) - [Connection helper](#connection-helper) - [Build input data only](#build-input-data-only) diff --git a/packages/prisma-fabbrica/src/helpers/index.ts b/packages/prisma-fabbrica/src/helpers/index.ts index 79ffdbfb..6d241cc2 100644 --- a/packages/prisma-fabbrica/src/helpers/index.ts +++ b/packages/prisma-fabbrica/src/helpers/index.ts @@ -2,3 +2,4 @@ export * from "./valueResolver"; export * from "./stringConverter"; export * from "./astShorthand"; export * from "./sequence"; +export * from "./selectors"; diff --git a/packages/prisma-fabbrica/src/helpers/selectors.ts b/packages/prisma-fabbrica/src/helpers/selectors.ts new file mode 100644 index 00000000..33e76ab7 --- /dev/null +++ b/packages/prisma-fabbrica/src/helpers/selectors.ts @@ -0,0 +1,3 @@ +export function byName(name: string | { readonly name: string }) { + return (x: T) => x.name === (typeof name === "string" ? name : name.name); +} diff --git a/packages/prisma-fabbrica/src/relations/index.ts b/packages/prisma-fabbrica/src/relations/index.ts new file mode 100644 index 00000000..e69de29b diff --git a/packages/prisma-fabbrica/src/relations/screen.test.ts b/packages/prisma-fabbrica/src/relations/screen.test.ts new file mode 100644 index 00000000..ef18fea2 --- /dev/null +++ b/packages/prisma-fabbrica/src/relations/screen.test.ts @@ -0,0 +1,138 @@ +import { DMMF } from "@prisma/generator-helper"; +import { getDMMF } from "@prisma/internals"; +import { createFieldDefinitions, createScreener } from "./screen"; + +describe(createScreener, () => { + let document: DMMF.Document; + let subject: (data: T) => T; + describe("For User - Post releation", () => { + beforeEach(async () => { + document = await getDMMF({ + datamodel: ` + model User { + id Int @id + posts Post[] + } + model Post { + id Int @id + authorId Int + author User @relation(fields: [authorId], references: [id]) + } + `, + }); + }); + + describe("User screen", () => { + beforeEach(() => { + subject = createScreener("User", createFieldDefinitions(document.datamodel.models)); + }); + it("screens author in posts create relation", () => { + expect( + subject({ + id: "", + posts: { + create: [{ id: "", author: {} }], + }, + }), + ).toEqual({ + id: "", + posts: { + create: [{ id: "" }], + }, + }); + }); + + it("screens author in posts connectOrCreate relation", () => { + expect( + subject({ + id: "", + posts: { + connectOrCreate: [{ where: { id: "" }, create: { id: "", author: {} } }], + }, + }), + ).toEqual({ + id: "", + posts: { + connectOrCreate: [{ where: { id: "" }, create: { id: "" } }], + }, + }); + }); + + it("does nothing for connect", () => { + expect( + subject({ + id: "", + posts: { + connect: [{ id: "", name: "" }], + }, + }), + ).toEqual({ + id: "", + posts: { + connect: [{ id: "", name: "" }], + }, + }); + }); + }); + + describe("Post screen", () => { + beforeEach(() => { + subject = createScreener("Post", createFieldDefinitions(document.datamodel.models)); + }); + + it("screening posts in user create relation", () => { + expect( + subject({ + id: "", + author: { + create: { id: "", posts: [] }, + }, + }), + ).toEqual({ + id: "", + author: { + create: { id: "" }, + }, + }); + }); + + it("screening posts in user connectOrCreate relation", () => { + expect( + subject({ + id: "", + author: { + connectOrCreate: { + where: { id: "" }, + create: { id: "", posts: [] }, + }, + }, + }), + ).toEqual({ + id: "", + author: { + connectOrCreate: { + where: { id: "" }, + create: { id: "" }, + }, + }, + }); + }); + + it("does nothing for connect", () => { + expect( + subject({ + id: "", + author: { + connect: { id: "", name: "" }, + }, + }), + ).toEqual({ + id: "", + author: { + connect: { id: "", name: "" }, + }, + }); + }); + }); + }); +}); diff --git a/packages/prisma-fabbrica/src/relations/screen.ts b/packages/prisma-fabbrica/src/relations/screen.ts new file mode 100644 index 00000000..2073d001 --- /dev/null +++ b/packages/prisma-fabbrica/src/relations/screen.ts @@ -0,0 +1,107 @@ +import { DMMF } from "@prisma/generator-helper"; +import { byName } from "../helpers"; + +export type ModelWithFields = { + name: string; + fields: { + name: string; + type: string; + relationName: string; + }[]; +}; + +function isCreateChild(def: any): def is { create: Record } { + return typeof def.create === "object" && !Array.isArray(def.create); +} + +function isCreateChildrenList(def: any): def is { create: Record[] } { + return typeof def.create === "object" && Array.isArray(def.create); +} + +function isCorCChild(def: any): def is { connectOrCreate: { where: unknown; create: Record } } { + return typeof def.connectOrCreate === "object" && !Array.isArray(def.connectOrCreate); +} + +function isCorCChildrenList( + def: any, +): def is { connectOrCreate: { where: unknown; create: Record }[] } { + return typeof def.connectOrCreate === "object" && Array.isArray(def.connectOrCreate); +} + +export function createFieldDefinitions(models: DMMF.Model[]): ModelWithFields[] { + return models.map(m => ({ + name: m.name, + fields: m.fields + .filter(f => f.kind === "object") + .map(f => ({ + name: f.name, + type: f.type, + relationName: f.relationName ?? "", + })), + })); +} + +function removeProperties(propertyNames: readonly string[], target: Record) { + for (const name of propertyNames) { + delete target[name]; + } + return target; +} + +export function createScreener(modelName: string, fieldDefinitions: readonly ModelWithFields[]) { + const screenInternal = (parentModelName: string, createInput: any): any => { + const result = Object.keys(createInput).reduce((acc, fieldName) => { + const fieldDef = fieldDefinitions.find(byName(parentModelName))?.fields.find(byName(fieldName)); + if (fieldDef) { + const nextModel = fieldDefinitions.find(byName(fieldDef.type))!; + const fieldNamesToBeRemoved = + fieldDefinitions + .find(byName(nextModel.name)) + ?.fields.filter(f => f.relationName === fieldDef.relationName) + .map(f => f.name) ?? []; + const connectionDefinition = createInput[fieldName] as + | undefined + | { + create?: unknown; + connect?: unknown; + connectOrCreate?: unknown; + }; + if (!connectionDefinition) return acc; + const modifiedConnectionDefinition = { ...connectionDefinition }; + if (isCreateChild(connectionDefinition)) { + modifiedConnectionDefinition.create = removeProperties( + fieldNamesToBeRemoved, + screenInternal(nextModel.name, connectionDefinition.create), + ); + } else if (isCreateChildrenList(connectionDefinition)) { + modifiedConnectionDefinition.create = connectionDefinition.create + .map(screenInternal.bind(null, nextModel.name)) + .map(removeProperties.bind(null, fieldNamesToBeRemoved)); + } + if (isCorCChild(connectionDefinition)) { + modifiedConnectionDefinition.connectOrCreate = { + ...connectionDefinition.connectOrCreate, + create: removeProperties( + fieldNamesToBeRemoved, + screenInternal(nextModel.name, connectionDefinition.connectOrCreate.create), + ), + }; + } else if (isCorCChildrenList(connectionDefinition)) { + modifiedConnectionDefinition.connectOrCreate = connectionDefinition.connectOrCreate.map(def => ({ + ...def, + create: removeProperties(fieldNamesToBeRemoved, screenInternal(nextModel.name, def.create)), + })); + } + return { + ...acc, + [fieldName]: modifiedConnectionDefinition, + }; + } else { + return { ...acc, [fieldName]: createInput[fieldName] }; + } + }, {} as any); + return result; + }; + const screen = (createInput: T) => screenInternal(modelName, createInput) as T; + return screen; +} diff --git a/packages/prisma-fabbrica/src/templates/index.ts b/packages/prisma-fabbrica/src/templates/index.ts index 9944e354..bde79108 100644 --- a/packages/prisma-fabbrica/src/templates/index.ts +++ b/packages/prisma-fabbrica/src/templates/index.ts @@ -1,11 +1,7 @@ import { DMMF } from "@prisma/generator-helper"; import ts from "typescript"; import { template } from "talt"; -import { camelize, ast } from "../helpers"; - -function byName(name: string | { readonly name: string }) { - return (x: T) => x.name === (typeof name === "string" ? name : name.name); -} +import { camelize, ast, byName } from "../helpers"; export function findPrsimaCreateInputTypeFromModelName(document: DMMF.Document, modelName: string) { const search = `${modelName}CreateInput`; From 1c13257b1a8977482771528cc14eb6c1d4d5ea44 Mon Sep 17 00:00:00 2001 From: Quramy Date: Mon, 28 Nov 2022 01:03:44 +0900 Subject: [PATCH 05/10] chore: Add util to create JSON literal ast --- .../src/helpers/createJSONLiteral.ts | 42 +++++++++++++++++++ packages/prisma-fabbrica/src/helpers/index.ts | 1 + 2 files changed, 43 insertions(+) create mode 100644 packages/prisma-fabbrica/src/helpers/createJSONLiteral.ts diff --git a/packages/prisma-fabbrica/src/helpers/createJSONLiteral.ts b/packages/prisma-fabbrica/src/helpers/createJSONLiteral.ts new file mode 100644 index 00000000..00e12814 --- /dev/null +++ b/packages/prisma-fabbrica/src/helpers/createJSONLiteral.ts @@ -0,0 +1,42 @@ +import ts from "typescript"; +import { ast } from "./astShorthand"; + +type Primitive = string | boolean | number; + +type JSONObjLike = { + [key: string]: Primitive | JSONObjLike | JSONObjLike[]; +}; + +type JSONLike = JSONObjLike | JSONObjLike[]; + +function createArrayLitreral(obj: ReadonlyArray): ts.ArrayLiteralExpression { + return ast.arrayLiteralExpression(obj.map(createObjectLiteral)); +} + +function createObjectLiteral(obj: JSONObjLike): ts.ObjectLiteralExpression { + return ast.objectLiteralExpression( + Object.entries(obj).map(([k, v]) => + ast.propertyAssignment( + k, + typeof v === "string" + ? ast.stringLiteral(v) + : typeof v === "number" + ? ast.numericLiteral(v) + : typeof v === "boolean" + ? v + ? ast.true() + : ast.false() + : Array.isArray(v) + ? createArrayLitreral(v) + : typeof v === "object" + ? createObjectLiteral(v) + : (null as never), + ), + ), + true, + ); +} + +export function createJSONLiteral(obj: JSONLike): ts.Expression { + return Array.isArray(obj) ? createArrayLitreral(obj) : createObjectLiteral(obj); +} diff --git a/packages/prisma-fabbrica/src/helpers/index.ts b/packages/prisma-fabbrica/src/helpers/index.ts index 6d241cc2..a6f4fa91 100644 --- a/packages/prisma-fabbrica/src/helpers/index.ts +++ b/packages/prisma-fabbrica/src/helpers/index.ts @@ -3,3 +3,4 @@ export * from "./stringConverter"; export * from "./astShorthand"; export * from "./sequence"; export * from "./selectors"; +export * from "./createJSONLiteral"; From efaa881baed87d222d09661d12c4da8ba9a4d054 Mon Sep 17 00:00:00 2001 From: Quramy Date: Mon, 28 Nov 2022 01:04:54 +0900 Subject: [PATCH 06/10] feat: Screening disturbing fields before create --- .../src/__generated__/fabbrica/index.js | 22 +++++++++- examples/example-prj/src/sample.test.ts | 10 +++++ .../prisma-fabbrica/src/relations/index.ts | 1 + .../__snapshots__/getSourceFile.test.ts.snap | 8 +++- .../prisma-fabbrica/src/templates/index.ts | 13 +++++- .../__generated__/fabbrica/index.ts | 14 +++++- .../__generated__/fabbrica/index.ts | 22 +++++++++- .../__generated__/fabbrica/index.ts | 44 +++++++++++++++++-- .../__generated__/fabbrica/index.ts | 22 +++++++++- .../__generated__/fabbrica/index.ts | 8 +++- 10 files changed, 149 insertions(+), 15 deletions(-) diff --git a/examples/example-prj/src/__generated__/fabbrica/index.js b/examples/example-prj/src/__generated__/fabbrica/index.js index f8f42db1..59750bcf 100644 --- a/examples/example-prj/src/__generated__/fabbrica/index.js +++ b/examples/example-prj/src/__generated__/fabbrica/index.js @@ -5,11 +5,27 @@ var __importDefault = (this && this.__importDefault) || function (mod) { Object.defineProperty(exports, "__esModule", { value: true }); exports.definePostFactory = exports.defineUserFactory = exports.resetSequence = exports.initialize = void 0; const clientHolder_1 = require("@quramy/prisma-fabbrica/lib/clientHolder"); +const relations_1 = require("@quramy/prisma-fabbrica/lib/relations"); const gen_1 = __importDefault(require("@quramy/prisma-fabbrica/lib/scalar/gen")); const helpers_1 = require("@quramy/prisma-fabbrica/lib/helpers"); var prisma_fabbrica_1 = require("@quramy/prisma-fabbrica"); Object.defineProperty(exports, "initialize", { enumerable: true, get: function () { return prisma_fabbrica_1.initialize; } }); Object.defineProperty(exports, "resetSequence", { enumerable: true, get: function () { return prisma_fabbrica_1.resetSequence; } }); +const modelFieldDefinitions = [{ + name: "User", + fields: [{ + name: "posts", + type: "Post", + relationName: "PostToUser" + }] + }, { + name: "Post", + fields: [{ + name: "author", + type: "User", + relationName: "PostToUser" + }] + }]; function autoGenerateUserScalarsOrEnums({ seq }) { return { id: gen_1.default.String({ modelName: "User", fieldName: "id", isId: true, isUnique: false, seq }), @@ -19,6 +35,7 @@ function autoGenerateUserScalarsOrEnums({ seq }) { function defineUserFactoryInternal({ defaultData: defaultDataResolver }) { const seqKey = {}; const getSeq = () => (0, helpers_1.getSequenceCounter)(seqKey); + const screen = (0, relations_1.createScreener)("User", modelFieldDefinitions); const buildCreateInput = async (inputData = {}) => { const seq = getSeq(); const requiredScalarData = autoGenerateUserScalarsOrEnums({ seq }); @@ -32,7 +49,7 @@ function defineUserFactoryInternal({ defaultData: defaultDataResolver }) { id: inputData.id }); const create = async (inputData = {}) => { - const data = await buildCreateInput(inputData); + const data = await buildCreateInput(inputData).then(screen); return await (0, clientHolder_1.getClient)().user.create({ data }); }; const createForConnect = (inputData = {}) => create(inputData).then(pickForConnect); @@ -60,6 +77,7 @@ function autoGeneratePostScalarsOrEnums({ seq }) { function definePostFactoryInternal({ defaultData: defaultDataResolver }) { const seqKey = {}; const getSeq = () => (0, helpers_1.getSequenceCounter)(seqKey); + const screen = (0, relations_1.createScreener)("Post", modelFieldDefinitions); const buildCreateInput = async (inputData = {}) => { const seq = getSeq(); const requiredScalarData = autoGeneratePostScalarsOrEnums({ seq }); @@ -77,7 +95,7 @@ function definePostFactoryInternal({ defaultData: defaultDataResolver }) { id: inputData.id }); const create = async (inputData = {}) => { - const data = await buildCreateInput(inputData); + const data = await buildCreateInput(inputData).then(screen); return await (0, clientHolder_1.getClient)().post.create({ data }); }; const createForConnect = (inputData = {}) => create(inputData).then(pickForConnect); diff --git a/examples/example-prj/src/sample.test.ts b/examples/example-prj/src/sample.test.ts index a7737143..a7832b57 100644 --- a/examples/example-prj/src/sample.test.ts +++ b/examples/example-prj/src/sample.test.ts @@ -23,6 +23,16 @@ describe("factories", () => { const user = await prisma.user.findUnique({ where: { id: "user001" } }); expect(user).toEqual({ id: "user001", name: "Quramy" }); }); + + it("creates record with children relation", async () => { + await UserFactory.create({ + posts: { + create: [await PostFactory.buildCreateInput()], + }, + }); + const created = await prisma.user.findFirst({ include: { posts: true } }); + expect(created?.posts.length).toBe(1); + }); }); describe("PostFactory", () => { diff --git a/packages/prisma-fabbrica/src/relations/index.ts b/packages/prisma-fabbrica/src/relations/index.ts index e69de29b..73498494 100644 --- a/packages/prisma-fabbrica/src/relations/index.ts +++ b/packages/prisma-fabbrica/src/relations/index.ts @@ -0,0 +1 @@ +export * from "./screen"; diff --git a/packages/prisma-fabbrica/src/templates/__snapshots__/getSourceFile.test.ts.snap b/packages/prisma-fabbrica/src/templates/__snapshots__/getSourceFile.test.ts.snap index 45341bf8..f3d266e0 100644 --- a/packages/prisma-fabbrica/src/templates/__snapshots__/getSourceFile.test.ts.snap +++ b/packages/prisma-fabbrica/src/templates/__snapshots__/getSourceFile.test.ts.snap @@ -5,12 +5,17 @@ exports[`getSourceFile generates TypeScript AST 1`] = ` import { Prisma } from "@prisma/client"; import type { PrismaClient } from "@prisma/client"; import { getClient } from "@quramy/prisma-fabbrica/lib/clientHolder"; +import { ModelWithFields, createScreener } from "@quramy/prisma-fabbrica/lib/relations"; import scalarFieldValueGenerator from "@quramy/prisma-fabbrica/lib/scalar/gen"; import { Resolver, normalizeResolver, getSequenceCounter } from "@quramy/prisma-fabbrica/lib/helpers"; export { initialize, resetSequence } from "@quramy/prisma-fabbrica"; type BuildDataOptions = { readonly seq: number; }; +const modelFieldDefinitions: ModelWithFields[] = [{ + name: "User", + fields: [] + }]; type UserScalarOrEnumFields = { id: number; name: string; @@ -33,6 +38,7 @@ function autoGenerateUserScalarsOrEnums({ seq }: { function defineUserFactoryInternal({ defaultData: defaultDataResolver }: UserFactoryDefineOptions) { const seqKey = {}; const getSeq = () => getSequenceCounter(seqKey); + const screen = createScreener("User", modelFieldDefinitions); const buildCreateInput = async (inputData: Partial = {}) => { const seq = getSeq(); const requiredScalarData = autoGenerateUserScalarsOrEnums({ seq }); @@ -46,7 +52,7 @@ function defineUserFactoryInternal({ defaultData: defaultDataResolver }: UserFac id: inputData.id }); const create = async (inputData: Partial = {}) => { - const data = await buildCreateInput(inputData); + const data = await buildCreateInput(inputData).then(screen); return await getClient().user.create({ data }); }; const createForConnect = (inputData: Partial = {}) => create(inputData).then(pickForConnect); diff --git a/packages/prisma-fabbrica/src/templates/index.ts b/packages/prisma-fabbrica/src/templates/index.ts index bde79108..3098457b 100644 --- a/packages/prisma-fabbrica/src/templates/index.ts +++ b/packages/prisma-fabbrica/src/templates/index.ts @@ -1,7 +1,8 @@ import { DMMF } from "@prisma/generator-helper"; import ts from "typescript"; import { template } from "talt"; -import { camelize, ast, byName } from "../helpers"; +import { camelize, ast, byName, createJSONLiteral } from "../helpers"; +import { createFieldDefinitions } from "../relations"; export function findPrsimaCreateInputTypeFromModelName(document: DMMF.Document, modelName: string) { const search = `${modelName}CreateInput`; @@ -64,6 +65,7 @@ export const header = (prismaClientModuleSpecifier: string) => import { Prisma } from ${() => ast.stringLiteral(prismaClientModuleSpecifier)}; import type { PrismaClient } from ${() => ast.stringLiteral(prismaClientModuleSpecifier)}; import { getClient } from "@quramy/prisma-fabbrica/lib/clientHolder"; + import { ModelWithFields, createScreener } from "@quramy/prisma-fabbrica/lib/relations"; import scalarFieldValueGenerator from "@quramy/prisma-fabbrica/lib/scalar/gen"; import { Resolver, normalizeResolver, getSequenceCounter } from "@quramy/prisma-fabbrica/lib/helpers"; export { initialize, resetSequence } from "@quramy/prisma-fabbrica"; @@ -78,6 +80,11 @@ export const importStatement = (specifier: string, prismaClientModuleSpecifier: import { ${() => ast.identifier(specifier)} } from ${() => ast.stringLiteral(prismaClientModuleSpecifier)}; `(); +export const modelFieldDefinitions = (models: DMMF.Model[]) => + template.statement` + const modelFieldDefinitions: ModelWithFields[] = ${() => createJSONLiteral(createFieldDefinitions(models))} +`(); + export const scalarFieldType = ( model: DMMF.Model, fieldName: string, @@ -263,6 +270,7 @@ export const defineModelFactoryInernal = (model: DMMF.Model, inputType: DMMF.Inp const seqKey = {}; const getSeq = () => getSequenceCounter(seqKey); + const screen = createScreener(${() => ast.stringLiteral(model.name)}, modelFieldDefinitions); const buildCreateInput = async ( inputData: Partial = {} @@ -305,7 +313,7 @@ export const defineModelFactoryInernal = (model: DMMF.Model, inputType: DMMF.Inp const create = async ( inputData: Partial = {} ) => { - const data = await buildCreateInput(inputData); + const data = await buildCreateInput(inputData).then(screen); return await getClient().MODEL_KEY.create({ data }); }; @@ -369,6 +377,7 @@ export function getSourceFile({ ...modelNames.map(modelName => importStatement(modelName, prismaClientModuleSpecifier)), ...enums.map(enumName => importStatement(enumName, prismaClientModuleSpecifier)), ...header(prismaClientModuleSpecifier).statements, + modelFieldDefinitions(document.datamodel.models), ...document.datamodel.models .map(model => ({ model, createInputType: findPrsimaCreateInputTypeFromModelName(document, model.name) })) .flatMap(({ model, createInputType }) => [ diff --git a/packages/ts-compile-testing/fixtures/field-variation/__generated__/fabbrica/index.ts b/packages/ts-compile-testing/fixtures/field-variation/__generated__/fabbrica/index.ts index 07085cde..1d944913 100644 --- a/packages/ts-compile-testing/fixtures/field-variation/__generated__/fabbrica/index.ts +++ b/packages/ts-compile-testing/fixtures/field-variation/__generated__/fabbrica/index.ts @@ -4,12 +4,20 @@ import { Role } from "./../client"; import { Prisma } from "./../client"; import type { PrismaClient } from "./../client"; import { getClient } from "@quramy/prisma-fabbrica/lib/clientHolder"; +import { ModelWithFields, createScreener } from "@quramy/prisma-fabbrica/lib/relations"; import scalarFieldValueGenerator from "@quramy/prisma-fabbrica/lib/scalar/gen"; import { Resolver, normalizeResolver, getSequenceCounter } from "@quramy/prisma-fabbrica/lib/helpers"; export { initialize, resetSequence } from "@quramy/prisma-fabbrica"; type BuildDataOptions = { readonly seq: number; }; +const modelFieldDefinitions: ModelWithFields[] = [{ + name: "User", + fields: [] + }, { + name: "ComplexIdModel", + fields: [] + }]; type UserScalarOrEnumFields = { id: string; role: Role; @@ -34,6 +42,7 @@ function autoGenerateUserScalarsOrEnums({ seq }: { function defineUserFactoryInternal({ defaultData: defaultDataResolver }: UserFactoryDefineOptions) { const seqKey = {}; const getSeq = () => getSequenceCounter(seqKey); + const screen = createScreener("User", modelFieldDefinitions); const buildCreateInput = async (inputData: Partial = {}) => { const seq = getSeq(); const requiredScalarData = autoGenerateUserScalarsOrEnums({ seq }); @@ -47,7 +56,7 @@ function defineUserFactoryInternal({ defaultData: defaultDataResolver }: UserFac id: inputData.id }); const create = async (inputData: Partial = {}) => { - const data = await buildCreateInput(inputData); + const data = await buildCreateInput(inputData).then(screen); return await getClient().user.create({ data }); }; const createForConnect = (inputData: Partial = {}) => create(inputData).then(pickForConnect); @@ -84,6 +93,7 @@ function autoGenerateComplexIdModelScalarsOrEnums({ seq }: { function defineComplexIdModelFactoryInternal({ defaultData: defaultDataResolver }: ComplexIdModelFactoryDefineOptions) { const seqKey = {}; const getSeq = () => getSequenceCounter(seqKey); + const screen = createScreener("ComplexIdModel", modelFieldDefinitions); const buildCreateInput = async (inputData: Partial = {}) => { const seq = getSeq(); const requiredScalarData = autoGenerateComplexIdModelScalarsOrEnums({ seq }); @@ -98,7 +108,7 @@ function defineComplexIdModelFactoryInternal({ defaultData: defaultDataResolver lastName: inputData.lastName }); const create = async (inputData: Partial = {}) => { - const data = await buildCreateInput(inputData); + const data = await buildCreateInput(inputData).then(screen); return await getClient().complexIdModel.create({ data }); }; const createForConnect = (inputData: Partial = {}) => create(inputData).then(pickForConnect); diff --git a/packages/ts-compile-testing/fixtures/relations-many-to-many/__generated__/fabbrica/index.ts b/packages/ts-compile-testing/fixtures/relations-many-to-many/__generated__/fabbrica/index.ts index 6c84c1e8..5e184126 100644 --- a/packages/ts-compile-testing/fixtures/relations-many-to-many/__generated__/fabbrica/index.ts +++ b/packages/ts-compile-testing/fixtures/relations-many-to-many/__generated__/fabbrica/index.ts @@ -3,12 +3,28 @@ import { Category } from "./../client"; import { Prisma } from "./../client"; import type { PrismaClient } from "./../client"; import { getClient } from "@quramy/prisma-fabbrica/lib/clientHolder"; +import { ModelWithFields, createScreener } from "@quramy/prisma-fabbrica/lib/relations"; import scalarFieldValueGenerator from "@quramy/prisma-fabbrica/lib/scalar/gen"; import { Resolver, normalizeResolver, getSequenceCounter } from "@quramy/prisma-fabbrica/lib/helpers"; export { initialize, resetSequence } from "@quramy/prisma-fabbrica"; type BuildDataOptions = { readonly seq: number; }; +const modelFieldDefinitions: ModelWithFields[] = [{ + name: "Post", + fields: [{ + name: "categories", + type: "Category", + relationName: "CategoryToPost" + }] + }, { + name: "Category", + fields: [{ + name: "posts", + type: "Post", + relationName: "CategoryToPost" + }] + }]; type PostScalarOrEnumFields = { id: string; title: string; @@ -32,6 +48,7 @@ function autoGeneratePostScalarsOrEnums({ seq }: { function definePostFactoryInternal({ defaultData: defaultDataResolver }: PostFactoryDefineOptions) { const seqKey = {}; const getSeq = () => getSequenceCounter(seqKey); + const screen = createScreener("Post", modelFieldDefinitions); const buildCreateInput = async (inputData: Partial = {}) => { const seq = getSeq(); const requiredScalarData = autoGeneratePostScalarsOrEnums({ seq }); @@ -45,7 +62,7 @@ function definePostFactoryInternal({ defaultData: defaultDataResolver }: PostFac id: inputData.id }); const create = async (inputData: Partial = {}) => { - const data = await buildCreateInput(inputData); + const data = await buildCreateInput(inputData).then(screen); return await getClient().post.create({ data }); }; const createForConnect = (inputData: Partial = {}) => create(inputData).then(pickForConnect); @@ -83,6 +100,7 @@ function autoGenerateCategoryScalarsOrEnums({ seq }: { function defineCategoryFactoryInternal({ defaultData: defaultDataResolver }: CategoryFactoryDefineOptions) { const seqKey = {}; const getSeq = () => getSequenceCounter(seqKey); + const screen = createScreener("Category", modelFieldDefinitions); const buildCreateInput = async (inputData: Partial = {}) => { const seq = getSeq(); const requiredScalarData = autoGenerateCategoryScalarsOrEnums({ seq }); @@ -96,7 +114,7 @@ function defineCategoryFactoryInternal({ defaultData: defaultDataResolver }: Cat id: inputData.id }); const create = async (inputData: Partial = {}) => { - const data = await buildCreateInput(inputData); + const data = await buildCreateInput(inputData).then(screen); return await getClient().category.create({ data }); }; const createForConnect = (inputData: Partial = {}) => create(inputData).then(pickForConnect); 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 416e9b41..2ae79b4e 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 @@ -4,12 +4,47 @@ import { Review } from "./../client"; import { Prisma } from "./../client"; import type { PrismaClient } from "./../client"; import { getClient } from "@quramy/prisma-fabbrica/lib/clientHolder"; +import { ModelWithFields, createScreener } from "@quramy/prisma-fabbrica/lib/relations"; import scalarFieldValueGenerator from "@quramy/prisma-fabbrica/lib/scalar/gen"; import { Resolver, normalizeResolver, getSequenceCounter } from "@quramy/prisma-fabbrica/lib/helpers"; export { initialize, resetSequence } from "@quramy/prisma-fabbrica"; type BuildDataOptions = { readonly seq: number; }; +const modelFieldDefinitions: ModelWithFields[] = [{ + name: "User", + fields: [{ + name: "posts", + type: "Post", + relationName: "PostToUser" + }, { + name: "reviews", + type: "Review", + relationName: "ReviewToUser" + }] + }, { + name: "Post", + fields: [{ + name: "author", + type: "User", + relationName: "PostToUser" + }, { + name: "reviews", + type: "Review", + relationName: "PostToReview" + }] + }, { + name: "Review", + fields: [{ + name: "post", + type: "Post", + relationName: "PostToReview" + }, { + name: "reviewer", + type: "User", + relationName: "ReviewToUser" + }] + }]; type UserScalarOrEnumFields = { id: string; name: string; @@ -34,6 +69,7 @@ function autoGenerateUserScalarsOrEnums({ seq }: { function defineUserFactoryInternal({ defaultData: defaultDataResolver }: UserFactoryDefineOptions) { const seqKey = {}; const getSeq = () => getSequenceCounter(seqKey); + const screen = createScreener("User", modelFieldDefinitions); const buildCreateInput = async (inputData: Partial = {}) => { const seq = getSeq(); const requiredScalarData = autoGenerateUserScalarsOrEnums({ seq }); @@ -47,7 +83,7 @@ function defineUserFactoryInternal({ defaultData: defaultDataResolver }: UserFac id: inputData.id }); const create = async (inputData: Partial = {}) => { - const data = await buildCreateInput(inputData); + const data = await buildCreateInput(inputData).then(screen); return await getClient().user.create({ data }); }; const createForConnect = (inputData: Partial = {}) => create(inputData).then(pickForConnect); @@ -93,6 +129,7 @@ function autoGeneratePostScalarsOrEnums({ seq }: { function definePostFactoryInternal({ defaultData: defaultDataResolver }: PostFactoryDefineOptions) { const seqKey = {}; const getSeq = () => getSequenceCounter(seqKey); + const screen = createScreener("Post", modelFieldDefinitions); const buildCreateInput = async (inputData: Partial = {}) => { const seq = getSeq(); const requiredScalarData = autoGeneratePostScalarsOrEnums({ seq }); @@ -110,7 +147,7 @@ function definePostFactoryInternal({ defaultData: defaultDataResolver }: PostFac id: inputData.id }); const create = async (inputData: Partial = {}) => { - const data = await buildCreateInput(inputData); + const data = await buildCreateInput(inputData).then(screen); return await getClient().post.create({ data }); }; const createForConnect = (inputData: Partial = {}) => create(inputData).then(pickForConnect); @@ -163,6 +200,7 @@ function autoGenerateReviewScalarsOrEnums({ seq }: { function defineReviewFactoryInternal({ defaultData: defaultDataResolver }: ReviewFactoryDefineOptions) { const seqKey = {}; const getSeq = () => getSequenceCounter(seqKey); + const screen = createScreener("Review", modelFieldDefinitions); const buildCreateInput = async (inputData: Partial = {}) => { const seq = getSeq(); const requiredScalarData = autoGenerateReviewScalarsOrEnums({ seq }); @@ -183,7 +221,7 @@ function defineReviewFactoryInternal({ defaultData: defaultDataResolver }: Revie id: inputData.id }); const create = async (inputData: Partial = {}) => { - const data = await buildCreateInput(inputData); + const data = await buildCreateInput(inputData).then(screen); return await getClient().review.create({ data }); }; const createForConnect = (inputData: Partial = {}) => create(inputData).then(pickForConnect); diff --git a/packages/ts-compile-testing/fixtures/relations-one-to-one/__generated__/fabbrica/index.ts b/packages/ts-compile-testing/fixtures/relations-one-to-one/__generated__/fabbrica/index.ts index 2c0d90e7..41972426 100644 --- a/packages/ts-compile-testing/fixtures/relations-one-to-one/__generated__/fabbrica/index.ts +++ b/packages/ts-compile-testing/fixtures/relations-one-to-one/__generated__/fabbrica/index.ts @@ -3,12 +3,28 @@ import { Profile } from "./../client"; import { Prisma } from "./../client"; import type { PrismaClient } from "./../client"; import { getClient } from "@quramy/prisma-fabbrica/lib/clientHolder"; +import { ModelWithFields, createScreener } from "@quramy/prisma-fabbrica/lib/relations"; import scalarFieldValueGenerator from "@quramy/prisma-fabbrica/lib/scalar/gen"; import { Resolver, normalizeResolver, getSequenceCounter } from "@quramy/prisma-fabbrica/lib/helpers"; export { initialize, resetSequence } from "@quramy/prisma-fabbrica"; type BuildDataOptions = { readonly seq: number; }; +const modelFieldDefinitions: ModelWithFields[] = [{ + name: "User", + fields: [{ + name: "profile", + type: "Profile", + relationName: "ProfileToUser" + }] + }, { + name: "Profile", + fields: [{ + name: "user", + type: "User", + relationName: "ProfileToUser" + }] + }]; type UserScalarOrEnumFields = { id: string; name: string; @@ -39,6 +55,7 @@ function autoGenerateUserScalarsOrEnums({ seq }: { function defineUserFactoryInternal({ defaultData: defaultDataResolver }: UserFactoryDefineOptions) { const seqKey = {}; const getSeq = () => getSequenceCounter(seqKey); + const screen = createScreener("User", modelFieldDefinitions); const buildCreateInput = async (inputData: Partial = {}) => { const seq = getSeq(); const requiredScalarData = autoGenerateUserScalarsOrEnums({ seq }); @@ -56,7 +73,7 @@ function defineUserFactoryInternal({ defaultData: defaultDataResolver }: UserFac id: inputData.id }); const create = async (inputData: Partial = {}) => { - const data = await buildCreateInput(inputData); + const data = await buildCreateInput(inputData).then(screen); return await getClient().user.create({ data }); }; const createForConnect = (inputData: Partial = {}) => create(inputData).then(pickForConnect); @@ -98,6 +115,7 @@ function autoGenerateProfileScalarsOrEnums({ seq }: { function defineProfileFactoryInternal({ defaultData: defaultDataResolver }: ProfileFactoryDefineOptions) { const seqKey = {}; const getSeq = () => getSequenceCounter(seqKey); + const screen = createScreener("Profile", modelFieldDefinitions); const buildCreateInput = async (inputData: Partial = {}) => { const seq = getSeq(); const requiredScalarData = autoGenerateProfileScalarsOrEnums({ seq }); @@ -115,7 +133,7 @@ function defineProfileFactoryInternal({ defaultData: defaultDataResolver }: Prof id: inputData.id }); const create = async (inputData: Partial = {}) => { - const data = await buildCreateInput(inputData); + const data = await buildCreateInput(inputData).then(screen); return await getClient().profile.create({ data }); }; const createForConnect = (inputData: Partial = {}) => create(inputData).then(pickForConnect); diff --git a/packages/ts-compile-testing/fixtures/simple-model/__generated__/fabbrica/index.ts b/packages/ts-compile-testing/fixtures/simple-model/__generated__/fabbrica/index.ts index 13eff060..ec586ba9 100644 --- a/packages/ts-compile-testing/fixtures/simple-model/__generated__/fabbrica/index.ts +++ b/packages/ts-compile-testing/fixtures/simple-model/__generated__/fabbrica/index.ts @@ -2,12 +2,17 @@ import { User } from "./../client"; import { Prisma } from "./../client"; import type { PrismaClient } from "./../client"; import { getClient } from "@quramy/prisma-fabbrica/lib/clientHolder"; +import { ModelWithFields, createScreener } from "@quramy/prisma-fabbrica/lib/relations"; import scalarFieldValueGenerator from "@quramy/prisma-fabbrica/lib/scalar/gen"; import { Resolver, normalizeResolver, getSequenceCounter } from "@quramy/prisma-fabbrica/lib/helpers"; export { initialize, resetSequence } from "@quramy/prisma-fabbrica"; type BuildDataOptions = { readonly seq: number; }; +const modelFieldDefinitions: ModelWithFields[] = [{ + name: "User", + fields: [] + }]; type UserScalarOrEnumFields = { id: string; name: string; @@ -30,6 +35,7 @@ function autoGenerateUserScalarsOrEnums({ seq }: { function defineUserFactoryInternal({ defaultData: defaultDataResolver }: UserFactoryDefineOptions) { const seqKey = {}; const getSeq = () => getSequenceCounter(seqKey); + const screen = createScreener("User", modelFieldDefinitions); const buildCreateInput = async (inputData: Partial = {}) => { const seq = getSeq(); const requiredScalarData = autoGenerateUserScalarsOrEnums({ seq }); @@ -43,7 +49,7 @@ function defineUserFactoryInternal({ defaultData: defaultDataResolver }: UserFac id: inputData.id }); const create = async (inputData: Partial = {}) => { - const data = await buildCreateInput(inputData); + const data = await buildCreateInput(inputData).then(screen); return await getClient().user.create({ data }); }; const createForConnect = (inputData: Partial = {}) => create(inputData).then(pickForConnect); From e079e907f6766d97b5d43b5e25ee26f4f249cb83 Mon Sep 17 00:00:00 2001 From: Quramy Date: Mon, 28 Nov 2022 01:41:06 +0900 Subject: [PATCH 07/10] docs: Write about child relation --- README.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/README.md b/README.md index dda1b217..5fda147e 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ Prisma generator for model factories. - [Required relation](#required-relation) - [Connection helper](#connection-helper) - [Build input data only](#build-input-data-only) + - [has-many / has-one relation](#has-many--has-one-relation) - [Generator configuration](#generator-configuration) - [Tips](#tips) - [Works with jest-prisma](#works-with-jest-prisma) @@ -265,6 +266,32 @@ await PostFactory.create(); console.log(await prisma.user.count()); // -> 1 ``` +### has-many / has-one relation + +Sometimes, you may want a user data whose has post record. You can use `PostFactory.buildCreateInput` too. + +```ts +await UserFactory.create({ + posts: { + create: [await PostFactory.buildCreateInput()], + }, +}); + +console.log(await prisma.post.count()); // -> 1 +``` + +Note: In the above example, `PostFactory.buildCreateInput()` resolves JSON data such as: + +```ts +{ + id: "...", + title: "...", + author: { ... } // Derived from PostFactory defaultData +} +``` + +The `author` field is not allowed in `prisma.user.create` context. So `UserFactory` automatically filters the `author` field out in `.create` method. + ## Generator configuration The following options are available: From 248031e5e5b06084b81225358c1f85d2afbac97f Mon Sep 17 00:00:00 2001 From: Quramy Date: Mon, 28 Nov 2022 01:55:52 +0900 Subject: [PATCH 08/10] feat: Rename buildCreateInput to build --- README.md | 14 ++++---- .../src/__generated__/fabbrica/index.d.ts | 4 ++- .../src/__generated__/fabbrica/index.js | 16 +++++---- .../__snapshots__/getSourceFile.test.ts.snap | 7 ++-- .../prisma-fabbrica/src/templates/index.ts | 12 +++---- .../__generated__/fabbrica/index.ts | 14 ++++---- .../__generated__/fabbrica/index.ts | 14 ++++---- .../__generated__/fabbrica/index.ts | 33 ++++++++++--------- .../__generated__/fabbrica/index.ts | 22 +++++++------ .../__generated__/fabbrica/index.ts | 7 ++-- 10 files changed, 79 insertions(+), 64 deletions(-) diff --git a/README.md b/README.md index 5fda147e..40a8649e 100644 --- a/README.md +++ b/README.md @@ -230,17 +230,17 @@ console.log(posts.length); // -> 2 ### Build input data only -`.buildCreateInput` method in factories provides data set to create the model, but never insert. +`.build` method in factories provides data set to create the model, but never insert. ```ts await UserFactory.create(); // The above code is equivalent to the bellow: -const data = await UserFactory.buildCreateInput(); +const data = await UserFactory.build(); await prisma.user.create({ data }); ``` -For example, you can use `.buildCreateInput` method in other model's factory definition: +For example, you can use `.build` method in other model's factory definition: ```ts const UserFactory = defineUserFactory(); @@ -252,7 +252,7 @@ const PostFactory = definePostFactory({ where: { id: "user001", }, - create: await UserFactory.buildCreateInput({ + create: await UserFactory.build({ id: "user001", }), }, @@ -268,19 +268,19 @@ console.log(await prisma.user.count()); // -> 1 ### has-many / has-one relation -Sometimes, you may want a user data whose has post record. You can use `PostFactory.buildCreateInput` too. +Sometimes, you may want a user data whose has post record. You can use `PostFactory.build` too. ```ts await UserFactory.create({ posts: { - create: [await PostFactory.buildCreateInput()], + create: [await PostFactory.build()], }, }); console.log(await prisma.post.count()); // -> 1 ``` -Note: In the above example, `PostFactory.buildCreateInput()` resolves JSON data such as: +Note: In the above example, `PostFactory.build()` resolves JSON data such as: ```ts { diff --git a/examples/example-prj/src/__generated__/fabbrica/index.d.ts b/examples/example-prj/src/__generated__/fabbrica/index.d.ts index 4a99b0c4..8353a537 100644 --- a/examples/example-prj/src/__generated__/fabbrica/index.d.ts +++ b/examples/example-prj/src/__generated__/fabbrica/index.d.ts @@ -16,6 +16,7 @@ type UserFactoryDefineOptions = { }; export declare function defineUserFactory(args?: UserFactoryDefineOptions): { _factoryFor: "User"; + build: (inputData?: Partial) => Promise; buildCreateInput: (inputData?: Partial) => Promise; pickForConnect: (inputData: User) => { id: string; @@ -27,7 +28,7 @@ export declare function defineUserFactory(args?: UserFactoryDefineOptions): { }; type PostauthorFactory = { _factoryFor: "User"; - buildCreateInput: () => PromiseLike; + build: () => PromiseLike; }; type PostFactoryDefineInput = { id?: string; @@ -39,6 +40,7 @@ type PostFactoryDefineOptions = { }; export declare function definePostFactory(args: PostFactoryDefineOptions): { _factoryFor: "Post"; + build: (inputData?: Partial) => Promise; buildCreateInput: (inputData?: Partial) => Promise; pickForConnect: (inputData: Post) => { id: string; diff --git a/examples/example-prj/src/__generated__/fabbrica/index.js b/examples/example-prj/src/__generated__/fabbrica/index.js index 59750bcf..fdde8936 100644 --- a/examples/example-prj/src/__generated__/fabbrica/index.js +++ b/examples/example-prj/src/__generated__/fabbrica/index.js @@ -36,7 +36,7 @@ function defineUserFactoryInternal({ defaultData: defaultDataResolver }) { const seqKey = {}; const getSeq = () => (0, helpers_1.getSequenceCounter)(seqKey); const screen = (0, relations_1.createScreener)("User", modelFieldDefinitions); - const buildCreateInput = async (inputData = {}) => { + const build = async (inputData = {}) => { const seq = getSeq(); const requiredScalarData = autoGenerateUserScalarsOrEnums({ seq }); const resolveValue = (0, helpers_1.normalizeResolver)(defaultDataResolver ?? {}); @@ -49,13 +49,14 @@ function defineUserFactoryInternal({ defaultData: defaultDataResolver }) { id: inputData.id }); const create = async (inputData = {}) => { - const data = await buildCreateInput(inputData).then(screen); + const data = await build(inputData).then(screen); return await (0, clientHolder_1.getClient)().user.create({ data }); }; const createForConnect = (inputData = {}) => create(inputData).then(pickForConnect); return { _factoryFor: "User", - buildCreateInput, + build, + buildCreateInput: build, pickForConnect, create, createForConnect, @@ -78,14 +79,14 @@ function definePostFactoryInternal({ defaultData: defaultDataResolver }) { const seqKey = {}; const getSeq = () => (0, helpers_1.getSequenceCounter)(seqKey); const screen = (0, relations_1.createScreener)("Post", modelFieldDefinitions); - const buildCreateInput = async (inputData = {}) => { + const build = async (inputData = {}) => { const seq = getSeq(); const requiredScalarData = autoGeneratePostScalarsOrEnums({ seq }); const resolveValue = (0, helpers_1.normalizeResolver)(defaultDataResolver ?? {}); const defaultData = await resolveValue({ seq }); const defaultAssociations = { author: isPostauthorFactory(defaultData.author) ? { - create: await defaultData.author.buildCreateInput() + create: await defaultData.author.build() } : defaultData.author }; const data = { ...requiredScalarData, ...defaultData, ...defaultAssociations, ...inputData }; @@ -95,13 +96,14 @@ function definePostFactoryInternal({ defaultData: defaultDataResolver }) { id: inputData.id }); const create = async (inputData = {}) => { - const data = await buildCreateInput(inputData).then(screen); + const data = await build(inputData).then(screen); return await (0, clientHolder_1.getClient)().post.create({ data }); }; const createForConnect = (inputData = {}) => create(inputData).then(pickForConnect); return { _factoryFor: "Post", - buildCreateInput, + build, + buildCreateInput: build, pickForConnect, create, createForConnect, diff --git a/packages/prisma-fabbrica/src/templates/__snapshots__/getSourceFile.test.ts.snap b/packages/prisma-fabbrica/src/templates/__snapshots__/getSourceFile.test.ts.snap index f3d266e0..1dd96668 100644 --- a/packages/prisma-fabbrica/src/templates/__snapshots__/getSourceFile.test.ts.snap +++ b/packages/prisma-fabbrica/src/templates/__snapshots__/getSourceFile.test.ts.snap @@ -39,7 +39,7 @@ function defineUserFactoryInternal({ defaultData: defaultDataResolver }: UserFac const seqKey = {}; const getSeq = () => getSequenceCounter(seqKey); const screen = createScreener("User", modelFieldDefinitions); - const buildCreateInput = async (inputData: Partial = {}) => { + const build = async (inputData: Partial = {}) => { const seq = getSeq(); const requiredScalarData = autoGenerateUserScalarsOrEnums({ seq }); const resolveValue = normalizeResolver(defaultDataResolver ?? {}); @@ -52,13 +52,14 @@ function defineUserFactoryInternal({ defaultData: defaultDataResolver }: UserFac id: inputData.id }); const create = async (inputData: Partial = {}) => { - const data = await buildCreateInput(inputData).then(screen); + const data = await build(inputData).then(screen); return await getClient().user.create({ data }); }; const createForConnect = (inputData: Partial = {}) => create(inputData).then(pickForConnect); return { _factoryFor: "User" as const, - buildCreateInput, + build, + buildCreateInput: build, pickForConnect, create, createForConnect, diff --git a/packages/prisma-fabbrica/src/templates/index.ts b/packages/prisma-fabbrica/src/templates/index.ts index 3098457b..f91671a6 100644 --- a/packages/prisma-fabbrica/src/templates/index.ts +++ b/packages/prisma-fabbrica/src/templates/index.ts @@ -160,8 +160,7 @@ export const modelBelongsToRelationFactory = (fieldType: DMMF.SchemaArg, model: return template.statement` type ${() => ast.identifier(`${model.name}${fieldType.name}Factory`)} = { _factoryFor: ${() => ast.literalTypeNode(ast.stringLiteral(targetModel.type))}; - buildCreateInput: () => PromiseLike - ast.identifier(fieldType.inputTypes[0].type as string)}["create"]>; + build: () => PromiseLike ast.identifier(fieldType.inputTypes[0].type as string)}["create"]>; }; `(); }; @@ -272,7 +271,7 @@ export const defineModelFactoryInernal = (model: DMMF.Model, inputType: DMMF.Inp const getSeq = () => getSequenceCounter(seqKey); const screen = createScreener(${() => ast.stringLiteral(model.name)}, modelFieldDefinitions); - const buildCreateInput = async ( + const build = async ( inputData: Partial = {} ) => { const seq = getSeq(); @@ -286,7 +285,7 @@ export const defineModelFactoryInernal = (model: DMMF.Model, inputType: DMMF.Inp field.name, template.expression` IS_MODEL_BELONGS_TO_RELATION_FACTORY(defaultData.FIELD_NAME) ? { - create: await defaultData.FIELD_NAME.buildCreateInput() + create: await defaultData.FIELD_NAME.build() } : defaultData.FIELD_NAME `({ IS_MODEL_BELONGS_TO_RELATION_FACTORY: ast.identifier(`is${model.name}${field.name}Factory`), @@ -313,7 +312,7 @@ export const defineModelFactoryInernal = (model: DMMF.Model, inputType: DMMF.Inp const create = async ( inputData: Partial = {} ) => { - const data = await buildCreateInput(inputData).then(screen); + const data = await build(inputData).then(screen); return await getClient().MODEL_KEY.create({ data }); }; @@ -321,7 +320,8 @@ export const defineModelFactoryInernal = (model: DMMF.Model, inputType: DMMF.Inp return { _factoryFor: ${() => ast.stringLiteral(model.name)} as const, - buildCreateInput, + build, + buildCreateInput: build, pickForConnect, create, createForConnect, diff --git a/packages/ts-compile-testing/fixtures/field-variation/__generated__/fabbrica/index.ts b/packages/ts-compile-testing/fixtures/field-variation/__generated__/fabbrica/index.ts index 1d944913..e7a07451 100644 --- a/packages/ts-compile-testing/fixtures/field-variation/__generated__/fabbrica/index.ts +++ b/packages/ts-compile-testing/fixtures/field-variation/__generated__/fabbrica/index.ts @@ -43,7 +43,7 @@ function defineUserFactoryInternal({ defaultData: defaultDataResolver }: UserFac const seqKey = {}; const getSeq = () => getSequenceCounter(seqKey); const screen = createScreener("User", modelFieldDefinitions); - const buildCreateInput = async (inputData: Partial = {}) => { + const build = async (inputData: Partial = {}) => { const seq = getSeq(); const requiredScalarData = autoGenerateUserScalarsOrEnums({ seq }); const resolveValue = normalizeResolver(defaultDataResolver ?? {}); @@ -56,13 +56,14 @@ function defineUserFactoryInternal({ defaultData: defaultDataResolver }: UserFac id: inputData.id }); const create = async (inputData: Partial = {}) => { - const data = await buildCreateInput(inputData).then(screen); + const data = await build(inputData).then(screen); return await getClient().user.create({ data }); }; const createForConnect = (inputData: Partial = {}) => create(inputData).then(pickForConnect); return { _factoryFor: "User" as const, - buildCreateInput, + build, + buildCreateInput: build, pickForConnect, create, createForConnect, @@ -94,7 +95,7 @@ function defineComplexIdModelFactoryInternal({ defaultData: defaultDataResolver const seqKey = {}; const getSeq = () => getSequenceCounter(seqKey); const screen = createScreener("ComplexIdModel", modelFieldDefinitions); - const buildCreateInput = async (inputData: Partial = {}) => { + const build = async (inputData: Partial = {}) => { const seq = getSeq(); const requiredScalarData = autoGenerateComplexIdModelScalarsOrEnums({ seq }); const resolveValue = normalizeResolver(defaultDataResolver ?? {}); @@ -108,13 +109,14 @@ function defineComplexIdModelFactoryInternal({ defaultData: defaultDataResolver lastName: inputData.lastName }); const create = async (inputData: Partial = {}) => { - const data = await buildCreateInput(inputData).then(screen); + const data = await build(inputData).then(screen); return await getClient().complexIdModel.create({ data }); }; const createForConnect = (inputData: Partial = {}) => create(inputData).then(pickForConnect); return { _factoryFor: "ComplexIdModel" as const, - buildCreateInput, + build, + buildCreateInput: build, pickForConnect, create, createForConnect, diff --git a/packages/ts-compile-testing/fixtures/relations-many-to-many/__generated__/fabbrica/index.ts b/packages/ts-compile-testing/fixtures/relations-many-to-many/__generated__/fabbrica/index.ts index 5e184126..bc1df720 100644 --- a/packages/ts-compile-testing/fixtures/relations-many-to-many/__generated__/fabbrica/index.ts +++ b/packages/ts-compile-testing/fixtures/relations-many-to-many/__generated__/fabbrica/index.ts @@ -49,7 +49,7 @@ function definePostFactoryInternal({ defaultData: defaultDataResolver }: PostFac const seqKey = {}; const getSeq = () => getSequenceCounter(seqKey); const screen = createScreener("Post", modelFieldDefinitions); - const buildCreateInput = async (inputData: Partial = {}) => { + const build = async (inputData: Partial = {}) => { const seq = getSeq(); const requiredScalarData = autoGeneratePostScalarsOrEnums({ seq }); const resolveValue = normalizeResolver(defaultDataResolver ?? {}); @@ -62,13 +62,14 @@ function definePostFactoryInternal({ defaultData: defaultDataResolver }: PostFac id: inputData.id }); const create = async (inputData: Partial = {}) => { - const data = await buildCreateInput(inputData).then(screen); + const data = await build(inputData).then(screen); return await getClient().post.create({ data }); }; const createForConnect = (inputData: Partial = {}) => create(inputData).then(pickForConnect); return { _factoryFor: "Post" as const, - buildCreateInput, + build, + buildCreateInput: build, pickForConnect, create, createForConnect, @@ -101,7 +102,7 @@ function defineCategoryFactoryInternal({ defaultData: defaultDataResolver }: Cat const seqKey = {}; const getSeq = () => getSequenceCounter(seqKey); const screen = createScreener("Category", modelFieldDefinitions); - const buildCreateInput = async (inputData: Partial = {}) => { + const build = async (inputData: Partial = {}) => { const seq = getSeq(); const requiredScalarData = autoGenerateCategoryScalarsOrEnums({ seq }); const resolveValue = normalizeResolver(defaultDataResolver ?? {}); @@ -114,13 +115,14 @@ function defineCategoryFactoryInternal({ defaultData: defaultDataResolver }: Cat id: inputData.id }); const create = async (inputData: Partial = {}) => { - const data = await buildCreateInput(inputData).then(screen); + const data = await build(inputData).then(screen); return await getClient().category.create({ data }); }; const createForConnect = (inputData: Partial = {}) => create(inputData).then(pickForConnect); return { _factoryFor: "Category" as const, - buildCreateInput, + build, + buildCreateInput: build, pickForConnect, create, createForConnect, 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 2ae79b4e..e69c1119 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 @@ -70,7 +70,7 @@ function defineUserFactoryInternal({ defaultData: defaultDataResolver }: UserFac const seqKey = {}; const getSeq = () => getSequenceCounter(seqKey); const screen = createScreener("User", modelFieldDefinitions); - const buildCreateInput = async (inputData: Partial = {}) => { + const build = async (inputData: Partial = {}) => { const seq = getSeq(); const requiredScalarData = autoGenerateUserScalarsOrEnums({ seq }); const resolveValue = normalizeResolver(defaultDataResolver ?? {}); @@ -83,13 +83,14 @@ function defineUserFactoryInternal({ defaultData: defaultDataResolver }: UserFac id: inputData.id }); const create = async (inputData: Partial = {}) => { - const data = await buildCreateInput(inputData).then(screen); + const data = await build(inputData).then(screen); return await getClient().user.create({ data }); }; const createForConnect = (inputData: Partial = {}) => create(inputData).then(pickForConnect); return { _factoryFor: "User" as const, - buildCreateInput, + build, + buildCreateInput: build, pickForConnect, create, createForConnect, @@ -104,7 +105,7 @@ type PostScalarOrEnumFields = { }; type PostauthorFactory = { _factoryFor: "User"; - buildCreateInput: () => PromiseLike; + build: () => PromiseLike; }; type PostFactoryDefineInput = { id?: string; @@ -130,14 +131,14 @@ function definePostFactoryInternal({ defaultData: defaultDataResolver }: PostFac const seqKey = {}; const getSeq = () => getSequenceCounter(seqKey); const screen = createScreener("Post", modelFieldDefinitions); - const buildCreateInput = async (inputData: Partial = {}) => { + const build = async (inputData: Partial = {}) => { const seq = getSeq(); const requiredScalarData = autoGeneratePostScalarsOrEnums({ seq }); const resolveValue = normalizeResolver(defaultDataResolver ?? {}); const defaultData = await resolveValue({ seq }); const defaultAssociations = { author: isPostauthorFactory(defaultData.author) ? { - create: await defaultData.author.buildCreateInput() + create: await defaultData.author.build() } : defaultData.author }; const data: Prisma.PostCreateInput = { ...requiredScalarData, ...defaultData, ...defaultAssociations, ...inputData }; @@ -147,13 +148,14 @@ function definePostFactoryInternal({ defaultData: defaultDataResolver }: PostFac id: inputData.id }); const create = async (inputData: Partial = {}) => { - const data = await buildCreateInput(inputData).then(screen); + const data = await build(inputData).then(screen); return await getClient().post.create({ data }); }; const createForConnect = (inputData: Partial = {}) => create(inputData).then(pickForConnect); return { _factoryFor: "Post" as const, - buildCreateInput, + build, + buildCreateInput: build, pickForConnect, create, createForConnect, @@ -168,11 +170,11 @@ type ReviewScalarOrEnumFields = { }; type ReviewpostFactory = { _factoryFor: "Post"; - buildCreateInput: () => PromiseLike; + build: () => PromiseLike; }; type ReviewreviewerFactory = { _factoryFor: "User"; - buildCreateInput: () => PromiseLike; + build: () => PromiseLike; }; type ReviewFactoryDefineInput = { id?: string; @@ -201,17 +203,17 @@ function defineReviewFactoryInternal({ defaultData: defaultDataResolver }: Revie const seqKey = {}; const getSeq = () => getSequenceCounter(seqKey); const screen = createScreener("Review", modelFieldDefinitions); - const buildCreateInput = async (inputData: Partial = {}) => { + const build = async (inputData: Partial = {}) => { const seq = getSeq(); const requiredScalarData = autoGenerateReviewScalarsOrEnums({ seq }); const resolveValue = normalizeResolver(defaultDataResolver ?? {}); const defaultData = await resolveValue({ seq }); const defaultAssociations = { post: isReviewpostFactory(defaultData.post) ? { - create: await defaultData.post.buildCreateInput() + create: await defaultData.post.build() } : defaultData.post, reviewer: isReviewreviewerFactory(defaultData.reviewer) ? { - create: await defaultData.reviewer.buildCreateInput() + create: await defaultData.reviewer.build() } : defaultData.reviewer }; const data: Prisma.ReviewCreateInput = { ...requiredScalarData, ...defaultData, ...defaultAssociations, ...inputData }; @@ -221,13 +223,14 @@ function defineReviewFactoryInternal({ defaultData: defaultDataResolver }: Revie id: inputData.id }); const create = async (inputData: Partial = {}) => { - const data = await buildCreateInput(inputData).then(screen); + const data = await build(inputData).then(screen); return await getClient().review.create({ data }); }; const createForConnect = (inputData: Partial = {}) => create(inputData).then(pickForConnect); return { _factoryFor: "Review" as const, - buildCreateInput, + build, + buildCreateInput: build, pickForConnect, create, createForConnect, diff --git a/packages/ts-compile-testing/fixtures/relations-one-to-one/__generated__/fabbrica/index.ts b/packages/ts-compile-testing/fixtures/relations-one-to-one/__generated__/fabbrica/index.ts index 41972426..980791ca 100644 --- a/packages/ts-compile-testing/fixtures/relations-one-to-one/__generated__/fabbrica/index.ts +++ b/packages/ts-compile-testing/fixtures/relations-one-to-one/__generated__/fabbrica/index.ts @@ -31,7 +31,7 @@ type UserScalarOrEnumFields = { }; type UserprofileFactory = { _factoryFor: "Profile"; - buildCreateInput: () => PromiseLike; + build: () => PromiseLike; }; type UserFactoryDefineInput = { id?: string; @@ -56,14 +56,14 @@ function defineUserFactoryInternal({ defaultData: defaultDataResolver }: UserFac const seqKey = {}; const getSeq = () => getSequenceCounter(seqKey); const screen = createScreener("User", modelFieldDefinitions); - const buildCreateInput = async (inputData: Partial = {}) => { + const build = async (inputData: Partial = {}) => { const seq = getSeq(); const requiredScalarData = autoGenerateUserScalarsOrEnums({ seq }); const resolveValue = normalizeResolver(defaultDataResolver ?? {}); const defaultData = await resolveValue({ seq }); const defaultAssociations = { profile: isUserprofileFactory(defaultData.profile) ? { - create: await defaultData.profile.buildCreateInput() + create: await defaultData.profile.build() } : defaultData.profile }; const data: Prisma.UserCreateInput = { ...requiredScalarData, ...defaultData, ...defaultAssociations, ...inputData }; @@ -73,13 +73,14 @@ function defineUserFactoryInternal({ defaultData: defaultDataResolver }: UserFac id: inputData.id }); const create = async (inputData: Partial = {}) => { - const data = await buildCreateInput(inputData).then(screen); + const data = await build(inputData).then(screen); return await getClient().user.create({ data }); }; const createForConnect = (inputData: Partial = {}) => create(inputData).then(pickForConnect); return { _factoryFor: "User" as const, - buildCreateInput, + build, + buildCreateInput: build, pickForConnect, create, createForConnect, @@ -93,7 +94,7 @@ type ProfileScalarOrEnumFields = { }; type ProfileuserFactory = { _factoryFor: "User"; - buildCreateInput: () => PromiseLike; + build: () => PromiseLike; }; type ProfileFactoryDefineInput = { id?: string; @@ -116,14 +117,14 @@ function defineProfileFactoryInternal({ defaultData: defaultDataResolver }: Prof const seqKey = {}; const getSeq = () => getSequenceCounter(seqKey); const screen = createScreener("Profile", modelFieldDefinitions); - const buildCreateInput = async (inputData: Partial = {}) => { + const build = async (inputData: Partial = {}) => { const seq = getSeq(); const requiredScalarData = autoGenerateProfileScalarsOrEnums({ seq }); const resolveValue = normalizeResolver(defaultDataResolver ?? {}); const defaultData = await resolveValue({ seq }); const defaultAssociations = { user: isProfileuserFactory(defaultData.user) ? { - create: await defaultData.user.buildCreateInput() + create: await defaultData.user.build() } : defaultData.user }; const data: Prisma.ProfileCreateInput = { ...requiredScalarData, ...defaultData, ...defaultAssociations, ...inputData }; @@ -133,13 +134,14 @@ function defineProfileFactoryInternal({ defaultData: defaultDataResolver }: Prof id: inputData.id }); const create = async (inputData: Partial = {}) => { - const data = await buildCreateInput(inputData).then(screen); + const data = await build(inputData).then(screen); return await getClient().profile.create({ data }); }; const createForConnect = (inputData: Partial = {}) => create(inputData).then(pickForConnect); return { _factoryFor: "Profile" as const, - buildCreateInput, + build, + buildCreateInput: build, pickForConnect, create, createForConnect, diff --git a/packages/ts-compile-testing/fixtures/simple-model/__generated__/fabbrica/index.ts b/packages/ts-compile-testing/fixtures/simple-model/__generated__/fabbrica/index.ts index ec586ba9..b3c0274e 100644 --- a/packages/ts-compile-testing/fixtures/simple-model/__generated__/fabbrica/index.ts +++ b/packages/ts-compile-testing/fixtures/simple-model/__generated__/fabbrica/index.ts @@ -36,7 +36,7 @@ function defineUserFactoryInternal({ defaultData: defaultDataResolver }: UserFac const seqKey = {}; const getSeq = () => getSequenceCounter(seqKey); const screen = createScreener("User", modelFieldDefinitions); - const buildCreateInput = async (inputData: Partial = {}) => { + const build = async (inputData: Partial = {}) => { const seq = getSeq(); const requiredScalarData = autoGenerateUserScalarsOrEnums({ seq }); const resolveValue = normalizeResolver(defaultDataResolver ?? {}); @@ -49,13 +49,14 @@ function defineUserFactoryInternal({ defaultData: defaultDataResolver }: UserFac id: inputData.id }); const create = async (inputData: Partial = {}) => { - const data = await buildCreateInput(inputData).then(screen); + const data = await build(inputData).then(screen); return await getClient().user.create({ data }); }; const createForConnect = (inputData: Partial = {}) => create(inputData).then(pickForConnect); return { _factoryFor: "User" as const, - buildCreateInput, + build, + buildCreateInput: build, pickForConnect, create, createForConnect, From 97c4e009f0eb4f163d877584e29b49094fb5b1c7 Mon Sep 17 00:00:00 2001 From: Quramy Date: Mon, 28 Nov 2022 02:07:29 +0900 Subject: [PATCH 09/10] feat: Add createList and buildList method --- .../src/__generated__/fabbrica/index.d.ts | 4 +++ .../src/__generated__/fabbrica/index.js | 20 +++++++++++++ .../example-prj/src/connectOrCreate.test.ts | 6 ++-- examples/example-prj/src/sample.test.ts | 4 +-- .../__snapshots__/getSourceFile.test.ts.snap | 10 +++++++ .../prisma-fabbrica/src/templates/index.ts | 16 ++++++++++ .../__generated__/fabbrica/index.ts | 20 +++++++++++++ .../__generated__/fabbrica/index.ts | 20 +++++++++++++ .../__generated__/fabbrica/index.ts | 30 +++++++++++++++++++ .../__generated__/fabbrica/index.ts | 20 +++++++++++++ .../__generated__/fabbrica/index.ts | 10 +++++++ 11 files changed, 154 insertions(+), 6 deletions(-) diff --git a/examples/example-prj/src/__generated__/fabbrica/index.d.ts b/examples/example-prj/src/__generated__/fabbrica/index.d.ts index 8353a537..c00646f5 100644 --- a/examples/example-prj/src/__generated__/fabbrica/index.d.ts +++ b/examples/example-prj/src/__generated__/fabbrica/index.d.ts @@ -17,11 +17,13 @@ type UserFactoryDefineOptions = { export declare function defineUserFactory(args?: UserFactoryDefineOptions): { _factoryFor: "User"; build: (inputData?: Partial) => Promise; + buildList: (inputData: number | Partial[]) => Promise; buildCreateInput: (inputData?: Partial) => Promise; pickForConnect: (inputData: User) => { id: string; }; create: (inputData?: Partial) => Promise; + createList: (inputData: number | Partial[]) => Promise; createForConnect: (inputData?: Partial) => Promise<{ id: string; }>; @@ -41,11 +43,13 @@ type PostFactoryDefineOptions = { export declare function definePostFactory(args: PostFactoryDefineOptions): { _factoryFor: "Post"; build: (inputData?: Partial) => Promise; + buildList: (inputData: number | Partial[]) => Promise; buildCreateInput: (inputData?: Partial) => Promise; pickForConnect: (inputData: Post) => { id: string; }; create: (inputData?: Partial) => Promise; + createList: (inputData: number | Partial[]) => Promise; createForConnect: (inputData?: Partial) => Promise<{ id: string; }>; diff --git a/examples/example-prj/src/__generated__/fabbrica/index.js b/examples/example-prj/src/__generated__/fabbrica/index.js index fdde8936..f13d7714 100644 --- a/examples/example-prj/src/__generated__/fabbrica/index.js +++ b/examples/example-prj/src/__generated__/fabbrica/index.js @@ -45,6 +45,10 @@ function defineUserFactoryInternal({ defaultData: defaultDataResolver }) { const data = { ...requiredScalarData, ...defaultData, ...defaultAssociations, ...inputData }; return data; }; + const buildList = (inputData) => { + const list = typeof inputData === "number" ? [...new Array(inputData).keys()].map(() => ({})) : inputData; + return Promise.all(list.map(data => build(data))); + }; const pickForConnect = (inputData) => ({ id: inputData.id }); @@ -52,13 +56,19 @@ function defineUserFactoryInternal({ defaultData: defaultDataResolver }) { const data = await build(inputData).then(screen); return await (0, clientHolder_1.getClient)().user.create({ data }); }; + const createList = (inputData) => { + const list = typeof inputData === "number" ? [...new Array(inputData).keys()].map(() => ({})) : inputData; + return Promise.all(list.map(data => create(data))); + }; const createForConnect = (inputData = {}) => create(inputData).then(pickForConnect); return { _factoryFor: "User", build, + buildList, buildCreateInput: build, pickForConnect, create, + createList, createForConnect, }; } @@ -92,6 +102,10 @@ function definePostFactoryInternal({ defaultData: defaultDataResolver }) { const data = { ...requiredScalarData, ...defaultData, ...defaultAssociations, ...inputData }; return data; }; + const buildList = (inputData) => { + const list = typeof inputData === "number" ? [...new Array(inputData).keys()].map(() => ({})) : inputData; + return Promise.all(list.map(data => build(data))); + }; const pickForConnect = (inputData) => ({ id: inputData.id }); @@ -99,13 +113,19 @@ function definePostFactoryInternal({ defaultData: defaultDataResolver }) { const data = await build(inputData).then(screen); return await (0, clientHolder_1.getClient)().post.create({ data }); }; + const createList = (inputData) => { + const list = typeof inputData === "number" ? [...new Array(inputData).keys()].map(() => ({})) : inputData; + return Promise.all(list.map(data => create(data))); + }; const createForConnect = (inputData = {}) => create(inputData).then(pickForConnect); return { _factoryFor: "Post", build, + buildList, buildCreateInput: build, pickForConnect, create, + createList, createForConnect, }; } diff --git a/examples/example-prj/src/connectOrCreate.test.ts b/examples/example-prj/src/connectOrCreate.test.ts index d94e11ce..f1ae44ab 100644 --- a/examples/example-prj/src/connectOrCreate.test.ts +++ b/examples/example-prj/src/connectOrCreate.test.ts @@ -11,7 +11,7 @@ const PostFactory = definePostFactory({ where: { id: "user001", }, - create: await UserFactory.buildCreateInput({ + create: await UserFactory.build({ id: "user001", }), }, @@ -27,9 +27,7 @@ describe("factories", () => { }); it("creates related user at most one", async () => { - await PostFactory.create(); - await PostFactory.create(); - await PostFactory.create(); + await PostFactory.createList(3); await expect(prisma.user.count()).resolves.toBe(1); }); }); diff --git a/examples/example-prj/src/sample.test.ts b/examples/example-prj/src/sample.test.ts index a7832b57..b62fbcec 100644 --- a/examples/example-prj/src/sample.test.ts +++ b/examples/example-prj/src/sample.test.ts @@ -27,11 +27,11 @@ describe("factories", () => { it("creates record with children relation", async () => { await UserFactory.create({ posts: { - create: [await PostFactory.buildCreateInput()], + create: await PostFactory.buildList(2), }, }); const created = await prisma.user.findFirst({ include: { posts: true } }); - expect(created?.posts.length).toBe(1); + expect(created?.posts.length).toBe(2); }); }); diff --git a/packages/prisma-fabbrica/src/templates/__snapshots__/getSourceFile.test.ts.snap b/packages/prisma-fabbrica/src/templates/__snapshots__/getSourceFile.test.ts.snap index 1dd96668..150d64c5 100644 --- a/packages/prisma-fabbrica/src/templates/__snapshots__/getSourceFile.test.ts.snap +++ b/packages/prisma-fabbrica/src/templates/__snapshots__/getSourceFile.test.ts.snap @@ -48,6 +48,10 @@ function defineUserFactoryInternal({ defaultData: defaultDataResolver }: UserFac const data: Prisma.UserCreateInput = { ...requiredScalarData, ...defaultData, ...defaultAssociations, ...inputData }; return data; }; + const buildList = (inputData: number | Partial[]) => { + const list = typeof inputData === "number" ? [...new Array(inputData).keys()].map(() => ({})) : inputData; + return Promise.all(list.map(data => build(data))); + }; const pickForConnect = (inputData: User) => ({ id: inputData.id }); @@ -55,13 +59,19 @@ function defineUserFactoryInternal({ defaultData: defaultDataResolver }: UserFac const data = await build(inputData).then(screen); return await getClient().user.create({ data }); }; + const createList = (inputData: number | Partial[]) => { + const list = typeof inputData === "number" ? [...new Array(inputData).keys()].map(() => ({})) : inputData; + return Promise.all(list.map(data => create(data))); + }; const createForConnect = (inputData: Partial = {}) => create(inputData).then(pickForConnect); return { _factoryFor: "User" as const, build, + buildList, buildCreateInput: build, pickForConnect, create, + createList, createForConnect, }; } diff --git a/packages/prisma-fabbrica/src/templates/index.ts b/packages/prisma-fabbrica/src/templates/index.ts index f91671a6..654c22d8 100644 --- a/packages/prisma-fabbrica/src/templates/index.ts +++ b/packages/prisma-fabbrica/src/templates/index.ts @@ -299,6 +299,13 @@ export const defineModelFactoryInernal = (model: DMMF.Model, inputType: DMMF.Inp return data; }; + const buildList = ( + inputData: number | Partial[] + ) => { + const list = typeof inputData === "number" ? [...new Array(inputData).keys()].map(() => ({})) : inputData; + return Promise.all(list.map(data => build(data))); + } + const pickForConnect = (inputData: ${() => ast.typeReferenceNode(model.name)}) => ( ${() => ast.objectLiteralExpression( @@ -316,14 +323,23 @@ export const defineModelFactoryInernal = (model: DMMF.Model, inputType: DMMF.Inp return await getClient().MODEL_KEY.create({ data }); }; + const createList = ( + inputData: number | Partial[] + ) => { + const list = typeof inputData === "number" ? [...new Array(inputData).keys()].map(() => ({})) : inputData; + return Promise.all(list.map(data => create(data))); + } + const createForConnect = (inputData: Partial = {}) => create(inputData).then(pickForConnect); return { _factoryFor: ${() => ast.stringLiteral(model.name)} as const, build, + buildList, buildCreateInput: build, pickForConnect, create, + createList, createForConnect, }; } diff --git a/packages/ts-compile-testing/fixtures/field-variation/__generated__/fabbrica/index.ts b/packages/ts-compile-testing/fixtures/field-variation/__generated__/fabbrica/index.ts index e7a07451..e7ed38ce 100644 --- a/packages/ts-compile-testing/fixtures/field-variation/__generated__/fabbrica/index.ts +++ b/packages/ts-compile-testing/fixtures/field-variation/__generated__/fabbrica/index.ts @@ -52,6 +52,10 @@ function defineUserFactoryInternal({ defaultData: defaultDataResolver }: UserFac const data: Prisma.UserCreateInput = { ...requiredScalarData, ...defaultData, ...defaultAssociations, ...inputData }; return data; }; + const buildList = (inputData: number | Partial[]) => { + const list = typeof inputData === "number" ? [...new Array(inputData).keys()].map(() => ({})) : inputData; + return Promise.all(list.map(data => build(data))); + }; const pickForConnect = (inputData: User) => ({ id: inputData.id }); @@ -59,13 +63,19 @@ function defineUserFactoryInternal({ defaultData: defaultDataResolver }: UserFac const data = await build(inputData).then(screen); return await getClient().user.create({ data }); }; + const createList = (inputData: number | Partial[]) => { + const list = typeof inputData === "number" ? [...new Array(inputData).keys()].map(() => ({})) : inputData; + return Promise.all(list.map(data => create(data))); + }; const createForConnect = (inputData: Partial = {}) => create(inputData).then(pickForConnect); return { _factoryFor: "User" as const, build, + buildList, buildCreateInput: build, pickForConnect, create, + createList, createForConnect, }; } @@ -104,6 +114,10 @@ function defineComplexIdModelFactoryInternal({ defaultData: defaultDataResolver const data: Prisma.ComplexIdModelCreateInput = { ...requiredScalarData, ...defaultData, ...defaultAssociations, ...inputData }; return data; }; + const buildList = (inputData: number | Partial[]) => { + const list = typeof inputData === "number" ? [...new Array(inputData).keys()].map(() => ({})) : inputData; + return Promise.all(list.map(data => build(data))); + }; const pickForConnect = (inputData: ComplexIdModel) => ({ firstName: inputData.firstName, lastName: inputData.lastName @@ -112,13 +126,19 @@ function defineComplexIdModelFactoryInternal({ defaultData: defaultDataResolver const data = await build(inputData).then(screen); return await getClient().complexIdModel.create({ data }); }; + const createList = (inputData: number | Partial[]) => { + const list = typeof inputData === "number" ? [...new Array(inputData).keys()].map(() => ({})) : inputData; + return Promise.all(list.map(data => create(data))); + }; const createForConnect = (inputData: Partial = {}) => create(inputData).then(pickForConnect); return { _factoryFor: "ComplexIdModel" as const, build, + buildList, buildCreateInput: build, pickForConnect, create, + createList, createForConnect, }; } diff --git a/packages/ts-compile-testing/fixtures/relations-many-to-many/__generated__/fabbrica/index.ts b/packages/ts-compile-testing/fixtures/relations-many-to-many/__generated__/fabbrica/index.ts index bc1df720..6c057f30 100644 --- a/packages/ts-compile-testing/fixtures/relations-many-to-many/__generated__/fabbrica/index.ts +++ b/packages/ts-compile-testing/fixtures/relations-many-to-many/__generated__/fabbrica/index.ts @@ -58,6 +58,10 @@ function definePostFactoryInternal({ defaultData: defaultDataResolver }: PostFac const data: Prisma.PostCreateInput = { ...requiredScalarData, ...defaultData, ...defaultAssociations, ...inputData }; return data; }; + const buildList = (inputData: number | Partial[]) => { + const list = typeof inputData === "number" ? [...new Array(inputData).keys()].map(() => ({})) : inputData; + return Promise.all(list.map(data => build(data))); + }; const pickForConnect = (inputData: Post) => ({ id: inputData.id }); @@ -65,13 +69,19 @@ function definePostFactoryInternal({ defaultData: defaultDataResolver }: PostFac const data = await build(inputData).then(screen); return await getClient().post.create({ data }); }; + const createList = (inputData: number | Partial[]) => { + const list = typeof inputData === "number" ? [...new Array(inputData).keys()].map(() => ({})) : inputData; + return Promise.all(list.map(data => create(data))); + }; const createForConnect = (inputData: Partial = {}) => create(inputData).then(pickForConnect); return { _factoryFor: "Post" as const, build, + buildList, buildCreateInput: build, pickForConnect, create, + createList, createForConnect, }; } @@ -111,6 +121,10 @@ function defineCategoryFactoryInternal({ defaultData: defaultDataResolver }: Cat const data: Prisma.CategoryCreateInput = { ...requiredScalarData, ...defaultData, ...defaultAssociations, ...inputData }; return data; }; + const buildList = (inputData: number | Partial[]) => { + const list = typeof inputData === "number" ? [...new Array(inputData).keys()].map(() => ({})) : inputData; + return Promise.all(list.map(data => build(data))); + }; const pickForConnect = (inputData: Category) => ({ id: inputData.id }); @@ -118,13 +132,19 @@ function defineCategoryFactoryInternal({ defaultData: defaultDataResolver }: Cat const data = await build(inputData).then(screen); return await getClient().category.create({ data }); }; + const createList = (inputData: number | Partial[]) => { + const list = typeof inputData === "number" ? [...new Array(inputData).keys()].map(() => ({})) : inputData; + return Promise.all(list.map(data => create(data))); + }; const createForConnect = (inputData: Partial = {}) => create(inputData).then(pickForConnect); return { _factoryFor: "Category" as const, build, + buildList, buildCreateInput: build, pickForConnect, create, + createList, createForConnect, }; } 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 e69c1119..71eec8db 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 @@ -79,6 +79,10 @@ function defineUserFactoryInternal({ defaultData: defaultDataResolver }: UserFac const data: Prisma.UserCreateInput = { ...requiredScalarData, ...defaultData, ...defaultAssociations, ...inputData }; return data; }; + const buildList = (inputData: number | Partial[]) => { + const list = typeof inputData === "number" ? [...new Array(inputData).keys()].map(() => ({})) : inputData; + return Promise.all(list.map(data => build(data))); + }; const pickForConnect = (inputData: User) => ({ id: inputData.id }); @@ -86,13 +90,19 @@ function defineUserFactoryInternal({ defaultData: defaultDataResolver }: UserFac const data = await build(inputData).then(screen); return await getClient().user.create({ data }); }; + const createList = (inputData: number | Partial[]) => { + const list = typeof inputData === "number" ? [...new Array(inputData).keys()].map(() => ({})) : inputData; + return Promise.all(list.map(data => create(data))); + }; const createForConnect = (inputData: Partial = {}) => create(inputData).then(pickForConnect); return { _factoryFor: "User" as const, build, + buildList, buildCreateInput: build, pickForConnect, create, + createList, createForConnect, }; } @@ -144,6 +154,10 @@ function definePostFactoryInternal({ defaultData: defaultDataResolver }: PostFac const data: Prisma.PostCreateInput = { ...requiredScalarData, ...defaultData, ...defaultAssociations, ...inputData }; return data; }; + const buildList = (inputData: number | Partial[]) => { + const list = typeof inputData === "number" ? [...new Array(inputData).keys()].map(() => ({})) : inputData; + return Promise.all(list.map(data => build(data))); + }; const pickForConnect = (inputData: Post) => ({ id: inputData.id }); @@ -151,13 +165,19 @@ function definePostFactoryInternal({ defaultData: defaultDataResolver }: PostFac const data = await build(inputData).then(screen); return await getClient().post.create({ data }); }; + const createList = (inputData: number | Partial[]) => { + const list = typeof inputData === "number" ? [...new Array(inputData).keys()].map(() => ({})) : inputData; + return Promise.all(list.map(data => create(data))); + }; const createForConnect = (inputData: Partial = {}) => create(inputData).then(pickForConnect); return { _factoryFor: "Post" as const, build, + buildList, buildCreateInput: build, pickForConnect, create, + createList, createForConnect, }; } @@ -219,6 +239,10 @@ function defineReviewFactoryInternal({ defaultData: defaultDataResolver }: Revie const data: Prisma.ReviewCreateInput = { ...requiredScalarData, ...defaultData, ...defaultAssociations, ...inputData }; return data; }; + const buildList = (inputData: number | Partial[]) => { + const list = typeof inputData === "number" ? [...new Array(inputData).keys()].map(() => ({})) : inputData; + return Promise.all(list.map(data => build(data))); + }; const pickForConnect = (inputData: Review) => ({ id: inputData.id }); @@ -226,13 +250,19 @@ function defineReviewFactoryInternal({ defaultData: defaultDataResolver }: Revie const data = await build(inputData).then(screen); return await getClient().review.create({ data }); }; + const createList = (inputData: number | Partial[]) => { + const list = typeof inputData === "number" ? [...new Array(inputData).keys()].map(() => ({})) : inputData; + return Promise.all(list.map(data => create(data))); + }; const createForConnect = (inputData: Partial = {}) => create(inputData).then(pickForConnect); return { _factoryFor: "Review" as const, build, + buildList, buildCreateInput: build, pickForConnect, create, + createList, createForConnect, }; } diff --git a/packages/ts-compile-testing/fixtures/relations-one-to-one/__generated__/fabbrica/index.ts b/packages/ts-compile-testing/fixtures/relations-one-to-one/__generated__/fabbrica/index.ts index 980791ca..2b5f7d93 100644 --- a/packages/ts-compile-testing/fixtures/relations-one-to-one/__generated__/fabbrica/index.ts +++ b/packages/ts-compile-testing/fixtures/relations-one-to-one/__generated__/fabbrica/index.ts @@ -69,6 +69,10 @@ function defineUserFactoryInternal({ defaultData: defaultDataResolver }: UserFac const data: Prisma.UserCreateInput = { ...requiredScalarData, ...defaultData, ...defaultAssociations, ...inputData }; return data; }; + const buildList = (inputData: number | Partial[]) => { + const list = typeof inputData === "number" ? [...new Array(inputData).keys()].map(() => ({})) : inputData; + return Promise.all(list.map(data => build(data))); + }; const pickForConnect = (inputData: User) => ({ id: inputData.id }); @@ -76,13 +80,19 @@ function defineUserFactoryInternal({ defaultData: defaultDataResolver }: UserFac const data = await build(inputData).then(screen); return await getClient().user.create({ data }); }; + const createList = (inputData: number | Partial[]) => { + const list = typeof inputData === "number" ? [...new Array(inputData).keys()].map(() => ({})) : inputData; + return Promise.all(list.map(data => create(data))); + }; const createForConnect = (inputData: Partial = {}) => create(inputData).then(pickForConnect); return { _factoryFor: "User" as const, build, + buildList, buildCreateInput: build, pickForConnect, create, + createList, createForConnect, }; } @@ -130,6 +140,10 @@ function defineProfileFactoryInternal({ defaultData: defaultDataResolver }: Prof const data: Prisma.ProfileCreateInput = { ...requiredScalarData, ...defaultData, ...defaultAssociations, ...inputData }; return data; }; + const buildList = (inputData: number | Partial[]) => { + const list = typeof inputData === "number" ? [...new Array(inputData).keys()].map(() => ({})) : inputData; + return Promise.all(list.map(data => build(data))); + }; const pickForConnect = (inputData: Profile) => ({ id: inputData.id }); @@ -137,13 +151,19 @@ function defineProfileFactoryInternal({ defaultData: defaultDataResolver }: Prof const data = await build(inputData).then(screen); return await getClient().profile.create({ data }); }; + const createList = (inputData: number | Partial[]) => { + const list = typeof inputData === "number" ? [...new Array(inputData).keys()].map(() => ({})) : inputData; + return Promise.all(list.map(data => create(data))); + }; const createForConnect = (inputData: Partial = {}) => create(inputData).then(pickForConnect); return { _factoryFor: "Profile" as const, build, + buildList, buildCreateInput: build, pickForConnect, create, + createList, createForConnect, }; } diff --git a/packages/ts-compile-testing/fixtures/simple-model/__generated__/fabbrica/index.ts b/packages/ts-compile-testing/fixtures/simple-model/__generated__/fabbrica/index.ts index b3c0274e..8b34bc49 100644 --- a/packages/ts-compile-testing/fixtures/simple-model/__generated__/fabbrica/index.ts +++ b/packages/ts-compile-testing/fixtures/simple-model/__generated__/fabbrica/index.ts @@ -45,6 +45,10 @@ function defineUserFactoryInternal({ defaultData: defaultDataResolver }: UserFac const data: Prisma.UserCreateInput = { ...requiredScalarData, ...defaultData, ...defaultAssociations, ...inputData }; return data; }; + const buildList = (inputData: number | Partial[]) => { + const list = typeof inputData === "number" ? [...new Array(inputData).keys()].map(() => ({})) : inputData; + return Promise.all(list.map(data => build(data))); + }; const pickForConnect = (inputData: User) => ({ id: inputData.id }); @@ -52,13 +56,19 @@ function defineUserFactoryInternal({ defaultData: defaultDataResolver }: UserFac const data = await build(inputData).then(screen); return await getClient().user.create({ data }); }; + const createList = (inputData: number | Partial[]) => { + const list = typeof inputData === "number" ? [...new Array(inputData).keys()].map(() => ({})) : inputData; + return Promise.all(list.map(data => create(data))); + }; const createForConnect = (inputData: Partial = {}) => create(inputData).then(pickForConnect); return { _factoryFor: "User" as const, build, + buildList, buildCreateInput: build, pickForConnect, create, + createList, createForConnect, }; } From 07bd7a209ac24806d43c422d84b9cbb8f3bc8c5a Mon Sep 17 00:00:00 2001 From: Quramy Date: Mon, 28 Nov 2022 02:24:10 +0900 Subject: [PATCH 10/10] docs: Write about createList and buildList --- README.md | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 40a8649e..594e6fa4 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ Prisma generator for model factories. - [Usage of factories](#usage-of-factories) - [Field default values](#field-default-values) - [Use sequence for scalar fields](#use-sequence-for-scalar-fields) + - [Shorthand for create list](#shorthand-for-create-list) - [Required relation](#required-relation) - [Connection helper](#connection-helper) - [Build input data only](#build-input-data-only) @@ -153,6 +154,24 @@ import { resetSequence } from "./__generated__/fabbrica"; beforeEach(() => resetSequence()); ``` +### Shorthand for create list + +Each factory provides `.createList` method to insert multiple records. + +```ts +await UserFactory.createList(3); + +// The above code is equivalent to the following + +await Promise.all([0, 1, 2].map(() => UserFactory.create())); +``` + +You can also pass list data assignable to `Partial[]` : + +```ts +await UserFactory.createList([{ id: "user01" }, { id: "user02" }]); +``` + ### Required relation Sometimes, creating a model requires other model existence. For example, the following model `Post` belongs to other model `User`. @@ -266,18 +285,20 @@ await PostFactory.create(); console.log(await prisma.user.count()); // -> 1 ``` +Like `createList`, `buildList` is also available. + ### has-many / has-one relation -Sometimes, you may want a user data whose has post record. You can use `PostFactory.build` too. +Sometimes, you may want a user data whose has post record. You can use `PostFactory.build` or `PostFactory.buildList` . ```ts await UserFactory.create({ posts: { - create: [await PostFactory.build()], + create: await PostFactory.buildList(2), }, }); -console.log(await prisma.post.count()); // -> 1 +console.log(await prisma.post.count()); // -> 2 ``` Note: In the above example, `PostFactory.build()` resolves JSON data such as: