Skip to content

Commit

Permalink
Merge pull request #45 from Quramy/screen_before_create
Browse files Browse the repository at this point in the history
Screen built input before create execution
  • Loading branch information
Quramy committed Nov 27, 2022
2 parents 173365f + e079e90 commit 9a359d4
Show file tree
Hide file tree
Showing 16 changed files with 469 additions and 19 deletions.
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ 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)
- [has-many / has-one relation](#has-many--has-one-relation)
- [Generator configuration](#generator-configuration)
- [Tips](#tips)
- [Works with jest-prisma](#works-with-jest-prisma)
Expand Down Expand Up @@ -264,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:
Expand Down
22 changes: 20 additions & 2 deletions examples/example-prj/src/__generated__/fabbrica/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions examples/example-prj/src/sample.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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", () => {
Expand Down
42 changes: 42 additions & 0 deletions packages/prisma-fabbrica/src/helpers/createJSONLiteral.ts
Original file line number Diff line number Diff line change
@@ -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<JSONObjLike>): 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);
}
2 changes: 2 additions & 0 deletions packages/prisma-fabbrica/src/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ export * from "./valueResolver";
export * from "./stringConverter";
export * from "./astShorthand";
export * from "./sequence";
export * from "./selectors";
export * from "./createJSONLiteral";
3 changes: 3 additions & 0 deletions packages/prisma-fabbrica/src/helpers/selectors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function byName<T extends { readonly name: string }>(name: string | { readonly name: string }) {
return (x: T) => x.name === (typeof name === "string" ? name : name.name);
}
1 change: 1 addition & 0 deletions packages/prisma-fabbrica/src/relations/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./screen";
138 changes: 138 additions & 0 deletions packages/prisma-fabbrica/src/relations/screen.test.ts
Original file line number Diff line number Diff line change
@@ -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: <T>(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: "" },
},
});
});
});
});
});
Loading

0 comments on commit 9a359d4

Please sign in to comment.