diff --git a/.eslintrc b/.eslintrc
index f295ab2..6834163 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -22,6 +22,15 @@
],
"max-lines-per-function": "off",
"consistent-return": "off",
- "jest/no-if": "off"
- }
+ "jest/no-if": "off",
+ "one-var": "off"
+ },
+ "overrides": [
+ {
+ "files": ["*.test.ts"],
+ "rules": {
+ "max-lines": "off"
+ }
+ }
+ ]
}
diff --git a/.github/workflows/prisma-nested-middleware.yml b/.github/workflows/prisma-nested-middleware.yml
index a7b72f2..8d7fc99 100644
--- a/.github/workflows/prisma-nested-middleware.yml
+++ b/.github/workflows/prisma-nested-middleware.yml
@@ -23,6 +23,7 @@ jobs:
- run: npm run validate
env:
CI: true
+
release:
runs-on: ubuntu-latest
needs: test
diff --git a/.gitignore b/.gitignore
index 934a37c..0e41fc7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,6 @@ dist
# Test coverage directory
coverage
+
+# vscode
+.vscode
diff --git a/README.md b/README.md
index 4830ae7..c531fbd 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,22 @@
Prisma Nested Middleware
-
Util for calling Prisma middleware for nested write operations.
+
Middleware that is called for every nested relation in a Prisma query.
-
Existing Prisma middleware is called once for every operation, but due to some operations containing nested writes it can become complex to ensure middleware is applied in all scenarios. See the existing issue regarding nested middleware for more information.
+
+ Vanilla Prisma middleware is great for modifying top-level queries but
+ becomes difficult to use when middleware must handle
+ nested writes
+ or modify where objects that reference relations.
+ See the existing issue regarding nested middleware
+ for more information.
+
+
+
+ This library creates middleware that is called for relations nested in the
+ params object, allowing you to modify params and results without having to
+ recurse through params objects yourself.
+
@@ -22,8 +35,22 @@
- [Installation](#installation)
- [Usage](#usage)
- - [Lifecycle](#lifecycle)
- - [Operations Nested in Lists](#operations-nested-in-lists)
+ - [Params](#params)
+ - [Nested Writes](#nested-writes)
+ - [Changing Nested Write Actions](#changing-nested-write-actions)
+ - [Splitting Nested Write Actions](#splitting-nested-write-actions)
+ - [Write Results](#write-results)
+ - [Where](#where)
+ - [Where Results](#where-results)
+ - [Include](#include)
+ - [Include Results](#include-results)
+ - [Select](#select)
+ - [Select Results](#select-results)
+ - [Relations](#relations)
+ - [Modifying Nested Write Params](#modifying-nested-write-params)
+ - [Modifying Where Params](#modifying-where-params)
+ - [Modifying Results](#modifying-results)
+ - [Errors](#errors)
- [LICENSE](#license)
@@ -37,9 +64,17 @@ should be installed as one of your project's dependencies:
npm install --save prisma-nested-middleware
```
+`@prisma/client` is a peer dependency of this library, so you will need to
+install it if you haven't already:
+
+```
+npm install --save @prisma/client
+```
+
## Usage
-Pass a [middleware function](https://www.prisma.io/docs/concepts/components/prisma-client/middleware) to `createNestedMiddleware`, the returned middleware can be passed to Prisma client's `$use` method:
+Pass a [middleware function](https://www.prisma.io/docs/concepts/components/prisma-client/middleware) to
+`createNestedMiddleware`, the returned middleware can be passed to Prisma client's `$use` method:
```javascript
import { createNestedMiddleware } from 'prisma-nested-middleware'
@@ -52,79 +87,1150 @@ client.$use(createNestedMiddleware(async (params, next) => {
));
```
-The middleware function passed to `createNestedMiddleware` is called for every
-[nested write](https://www.prisma.io/docs/concepts/components/prisma-client/relation-queries#nested-writes) operation.
+### Params
+
+The params object passed to the middleware function is a normal Prisma.MiddlewareParams object with the following
+differences:
+
+- the `action` field adds the following options: 'connectOrCreate', 'connect', 'disconnect', 'include', 'select' and 'where'
+- there is an additional `scope` field that contains information specific to nested relations:
+
+ - the `parentParams` field contains the params object of the parent relation
+ - the `modifier` field contains any modifiers the params were wrapped in, for example `some` or `every`.
+ - the `logicalOperators` field contains any logical operators between the current relation and it's parent, for example `AND` or `NOT`.
+ - the `relations` field contains an object with the relation `to` the current model and `from` the model back to it's parent.
+
+For more information on the `modifier` and `logicalOperators` fields see the [Where](#Where) section.
+
+For more information on the `relations` field see the [Relations](#Relations) section.
+
+The type for the params object is:
+
+```typescript
+type NestedParams = Omit & {
+ action:
+ | Prisma.PrismaAction
+ | "where"
+ | "include"
+ | "select"
+ | "connect"
+ | "connectOrCreate"
+ | "disconnect";
+ scope?: {
+ parentParams: NestedParams;
+ relations: { to: Prisma.DMMF.Field; from: Prisma.DMMF.Field };
+ modifier?: "is" | "isNot" | "some" | "none" | "every";
+ logicalOperators?: ("AND" | "OR" | "NOT)[];
+ };
+};
+```
+
+### Nested Writes
+
+The middleware function is called for every [nested write](https://www.prisma.io/docs/concepts/components/prisma-client/relation-queries#nested-writes)
+operation in the query. The `action` field is set to the operation being performed, for example "create" or "update".
+The `model` field is set to the model being operated on, for example "User" or "Post".
+
+For example take the following query:
+
+```javascript
+const result = await client.user.update({
+ data: {
+ posts: {
+ update: {
+ where: { id: 1 },
+ data: { title: "Hello World" },
+ },
+ },
+ },
+});
+```
+
+The middleware function will be called with:
+
+```javascript
+{
+ action: 'update',
+ model: 'Post',
+ args: {
+ where: { id: 1 },
+ data: { title: 'Hello World' }
+ },
+ relations: {
+ to: { kind: 'object', name: 'posts', isList: true, ... },
+ from: { kind: 'object', name: 'author', isList: false, ... },
+ },
+ scope: [root params],
+}
+```
+
+Some nested writes can be passed as an array of operations. In this case the middleware function is called for each
+operation in the array. For example take the following query:
+
+```javascript
+const result = await client.user.update({
+ data: {
+ posts: {
+ update: [
+ { where: { id: 1 }, data: { title: "Hello World" } },
+ { where: { id: 2 }, data: { title: "Hello World 2" } },
+ ],
+ },
+ },
+});
+```
+
+The middleware function will be called with:
+
+```javascript
+ {
+ action: 'update',
+ model: 'Post',
+ args: {
+ where: { id: 1 },
+ data: { title: 'Hello World' }
+ },
+ relations: {
+ to: { kind: 'object', name: 'posts', isList: true, ... },
+ from: { kind: 'object', name: 'author', isList: false, ... },
+ },
+ scope: [root params],
+}
+```
+
+and
+
+```javascript
+ {
+ action: 'update',
+ model: 'Post',
+ args: {
+ where: { id: 2 },
+ data: { title: 'Hello World 2' }
+ },
+ relations: {
+ to: { kind: 'object', name: 'posts', isList: true, ... },
+ from: { kind: 'object', name: 'author', isList: false, ... },
+ },
+ scope: [root params],
+}
+```
+
+#### Changing Nested Write Actions
-There are some differences to note when using nested middleware:
+The middleware function can change the action that is performed on the model. For example take the following query:
-- the list of actions that might be in params is expanded to include `connectOrCreate`, `include` and `select`.
-- The parent operation's params have been added to the params of nested middleware as a `scope` object. This is useful when the parent is relevant, for example when handling a `connectOrCreate` and you need to know the parent being connected to.
-- the return value of `next` matches the part of the response that the middleware was called for. For example if the middleware function is called for a nested create, the `next` function resolves with the value of that create.
-- if a relation is not included using `include` then that middleware's `next` function will resolve with `undefined`.
-- if a nested operation's result is within an array then the nested operation's `next` function returns a flattened array of all the models found in the parent array. See [Operations Nested in Lists](#operations-nested-in-lists) for more information.
+```javascript
+const result = await client.user.update({
+ data: {
+ posts: {
+ update: {
+ where: { id: 1 }
+ data: { title: 'Hello World' }
+ },
+ },
+ },
+});
+```
-### Lifecycle
+The middleware function could be used to change the action to `upsert`:
-It is helpful to walk through the lifecycle of an operation:
+```javascript
+const middleware = createNestedMiddleware((params, next) => {
+ if (params.model === "Post" && params.action === "update") {
+ return next({
+ ...params,
+ action: "upsert",
+ args: {
+ where: params.args.where,
+ create: params.args.data,
+ update: params.args.data,
+ },
+ });
+ }
+ return next(params);
+});
+```
+
+The final query would be modified by the above middleware to:
+
+```javascript
+const result = await client.user.update({
+ data: {
+ posts: {
+ upsert: {
+ where: { id: 1 },
+ create: { title: "Hello World" },
+ update: { title: "Hello World" },
+ },
+ },
+ },
+});
+```
-For the following update
+When changing the action it is possible for the action to already exist. In this case the resulting actions are merged.
+For example take the following query:
```javascript
-client.user.update({
- where: { id: 1 },
+const result = await client.user.update({
data: {
- comments: {
+ posts: {
update: {
+ where: { id: 1 },
+ data: { title: "Hello World" },
+ },
+ upsert: {
where: { id: 2 },
+ create: { title: "Hello World 2" },
+ update: { title: "Hello World 2" },
+ },
+ },
+ },
+});
+```
+
+Using the same middleware defined before the update action would be changed to an upsert action, however there is
+already an upsert action so the two actions are merged into a upsert operation array with the new operation added to
+the end of the array. When the existing action is already a list of operations the new operation is added to the end of
+the list. The final query in this case would be:
+
+```javascript
+const result = await client.user.update({
+ data: {
+ posts: {
+ upsert: [
+ {
+ where: { id: 2 },
+ create: { title: "Hello World 2" },
+ update: { title: "Hello World 2" },
+ },
+ {
+ where: { id: 1 },
+ create: { title: "Hello World" },
+ update: { title: "Hello World" },
+ },
+ ],
+ },
+ },
+});
+```
+
+Sometimes it is not possible to merge the actions together in this way. The `createMany` action does not support
+operation arrays so the `data` field of the `createMany` action is merged instead. For example take the following query:
+
+```javascript
+const result = await client.user.create({
+ data: {
+ posts: {
+ createMany: {
+ data: [{ title: "Hello World" }, { title: "Hello World 2" }],
+ },
+ create: {
+ title: "Hello World 3",
+ },
+ },
+ },
+});
+```
+
+If the `create` action was changed to be a `createMany` action the `data` field would be added to the end of the existing
+`createMany` action. The final query would be:
+
+```javascript
+const result = await client.user.create({
+ data: {
+ posts: {
+ createMany: {
+ data: [
+ { title: "Hello World" },
+ { title: "Hello World 2" },
+ { title: "Hello World 3" },
+ ],
+ },
+ },
+ },
+});
+```
+
+It is also not possible to merge the actions together by creating an array of operations for non-list relations. For
+example take the following query:
+
+```javascript
+const result = await client.user.update({
+ data: {
+ profile: {
+ create: {
+ bio: "My personal bio",
+ age: 30,
+ },
+ update: {
+ where: { id: 1 },
+ data: { bio: "Updated bio" },
+ },
+ },
+ },
+});
+```
+
+If the `update` action was changed to be a `create` action using the following middleware:
+
+```javascript
+const middleware = createNestedMiddleware((params, next) => {
+ if (params.model === "Profile" && params.action === "update") {
+ return next({
+ ...params,
+ action: "create",
+ args: params.args.data,
+ });
+ }
+ return next(params);
+});
+```
+
+The `create` action from the `update` action would need be merged with the existing `create` action, however since
+`profile` is not a list relation we must merge together the resulting objects instead, resulting in the final query:
+
+```javascript
+const result = await client.user.create({
+ data: {
+ profile: {
+ create: {
+ bio: "Updated bio",
+ age: 30,
+ },
+ },
+ },
+});
+```
+
+#### Splitting Nested Write Actions
+
+The middleware function can also split the action into multiple actions by passing an array of params to the `next`
+function. For example take the following query:
+
+```javascript
+const result = await client.user.update({
+ data: {
+ posts: {
+ delete: { id: 1 },
+ },
+ },
+});
+```
+
+A middleware function that changes the `delete` into an `update` and `disconnect` could be defined as:
+
+```javascript
+const middleware = createNestedMiddleware((params, next) => {
+ if (params.model === "Post" && params.action === "delete") {
+ return next([
+ {
+ ...params,
+ action: "update",
+ args: {
+ where: params.args,
+ data: { deleted: true },
+ },
+ },
+ {
+ ...params,
+ action: "disconnect",
+ args: params.args.where,
+ },
+ ]);
+ }
+ return next(params);
+});
+```
+
+The final query would be:
+
+```javascript
+const result = await client.user.update({
+ data: {
+ posts: {
+ update: {
+ where: { id: 1 },
+ data: { deleted: true },
+ },
+ disconnect: { id: 1 },
+ },
+ },
+});
+```
+
+#### Write Results
+
+The `next` function of middleware calls for nested write actions always return `undefined` as their result. This is
+because it the results returned from the root query may not include the data for a particular nested write.
+For example take the following query:
+
+```javascript
+const result = await client.user.update({
+ data: {
+ profile: {
+ create: {
+ bio: "My personal bio",
+ age: 30,
+ },
+ }
+ posts: {
+ updateMany: {
+ where: {
+ published: false,
+ },
data: {
- post: {
- connectOrCreate: {
- where: { id: 3 },
- create: {
- title: 'Hello World',
+ published: true,
+ },
+ },
+ },
+ },
+ select: {
+ id: true,
+ posts: {
+ where: {
+ title: {
+ contains: "Hello",
+ },
+ },
+ select: {
+ id: true,
+ },
+ },
+ }
+});
+```
+
+The `profile` field is not included in the `select` object so the result of the `create` action will not be included in
+the root result. The `posts` field is included in the `select` object but the `where` object only includes posts with
+titles that contain "Hello" and returns only the "id" field, in this case it is not possible to match the result of the
+`updateMany` action to the returned Posts.
+
+See [Modifying Results](#modifying-results) for more information on how to update the results of queries.
+
+### Where
+
+The `where` action is called for any relations found inside where objects in params.
+
+Note that the `where` action is not called for the root where object, this is because you need the root action to know
+what properties the root where object accepts. For nested where objects this is not a problem as they always follow the
+same pattern.
+
+To see where the `where` action is called take the following query:
+
+```javascript
+const result = await client.user.findMany({
+ where: {
+ posts: {
+ some: {
+ published: true,
+ },
+ },
+ },
+});
+```
+
+The where object above produces a call for "posts" relation found in the where object. The `modifier` field is set to
+"some" since the where object is within the "some" field.
+
+```javascript
+{
+ action: 'where',
+ model: 'Post',
+ args: {
+ published: true,
+ },
+ scope: {
+ parentParams: {...}
+ modifier: 'some',
+ relations: {...}
+ },
+}
+```
+
+Relations found inside where AND, OR and NOT logical operators are also found and called with the middleware function,
+however the `where` action is not called for the logical operators themselves. For example take the following query:
+
+```javascript
+const result = await client.user.findMany({
+ where: {
+ posts: {
+ some: {
+ published: true,
+ AND: [
+ {
+ title: "Hello World",
+ },
+ {
+ comments: {
+ every: {
+ text: "Great post!",
},
},
},
+ ],
+ },
+ },
+ },
+});
+```
+
+The middleware function will be called with the params for "posts" similarly to before, however it will also be called
+with the following params:
+
+```javascript
+{
+ action: 'where',
+ model: 'Comment',
+ args: {
+ text: "Great post!",
+ },
+ scope: {
+ parentParams: {...}
+ modifier: 'every',
+ logicalOperators: ['AND'],
+ relations: {...}
+ },
+}
+```
+
+Since the "comments" relation is found inside the "AND" logical operator the
+middleware is called for it. The `modifier` field is set to "every" since the where object is in the "every" field and
+the `logicalOperators` field is set to `['AND']` since the where object is inside the "AND" logical operator.
+
+Notice that the middleware function is not called for the first item in the "AND" array, this is because the first item
+does not contain any relations.
+
+The `logicalOperators` field tracks all the logical operators between the `parentParams` and the current params. For
+example take the following query:
+
+```javascript
+const result = await client.user.findMany({
+ where: {
+ AND: [
+ {
+ NOT: {
+ OR: [
+ {
+ posts: {
+ some: {
+ published: true,
+ },
+ },
+ },
+ ],
},
},
+ ],
+ },
+});
+```
+
+The middleware function will be called with the following params:
+
+```javascript
+{
+ action: 'where',
+ model: 'Post',
+ args: {
+ published: true,
+ },
+ scope: {
+ parentParams: {...}
+ modifier: 'some',
+ logicalOperators: ['AND', 'NOT', 'OR'],
+ relations: {...},
+ },
+}
+```
+
+The `where` action is also called for relations found in the `where` field of includes and selects. For example:
+
+```javascript
+const result = await client.user.findMany({
+ select: {
+ posts: {
+ where: {
+ published: true,
+ },
},
},
+});
+```
+
+The middleware function will be called with the following params:
+
+```javascript
+{
+ action: 'where',
+ model: 'Post',
+ args: {
+ published: true,
+ },
+ scope: {...}
+}
+```
+
+#### Where Results
+
+The `next` function for a `where` action always resolves with `undefined`.
+
+### Include
+
+The `include` action will be called for any included relation. The `args` field will contain the object or boolean
+passed as the relation include. For example take the following query:
+
+```javascript
+const result = await client.user.findMany({
include: {
- comments: {
- include: {
- post: {
- select: {
- title: true,
- },
- },
+ profile: true,
+ posts: {
+ where: {
+ published: true,
+ },
+ },
+ },
+});
+```
+
+For the "profile" relation the middleware function will be called with:
+
+```javascript
+{
+ action: 'include',
+ model: 'Profile',
+ args: true,
+ scope: {...}
+}
+```
+
+and for the "posts" relation the middleware function will be called with:
+
+```javascript
+{
+ action: 'include',
+ model: 'Post',
+ args: {
+ where: {
+ published: true,
+ },
+ },
+ scope: {...}
+}
+```
+
+#### Include Results
+
+The `next` function for an `include` action resolves with the result of the `include` action. For example take the
+following query:
+
+```javascript
+const result = await client.user.findMany({
+ include: {
+ profile: true,
+ },
+});
+```
+
+The middleware function for the "profile" relation will be called with:
+
+```javascript
+{
+ action: 'include',
+ model: 'Profile',
+ args: true,
+ scope: {...}
+}
+```
+
+And the `next` function will resolve with the result of the `include` action, in this case something like:
+
+```javascript
+{
+ id: 2,
+ bio: 'My personal bio',
+ age: 30,
+ userId: 1,
+}
+```
+
+For relations that are included within a list of parent results the `next` function will resolve with a flattened array
+of all the models from each parent result. For example take the following query:
+
+```javascript
+const result = await client.user.findMany({
+ include: {
+ posts: true,
+ },
+});
+```
+
+If the root result looks like the following:
+
+```javascript
+[
+ {
+ id: 1,
+ name: "Alice",
+ posts: [
+ {
+ id: 1,
+ title: "Hello World",
+ published: false,
+ userId: 1,
+ },
+ {
+ id: 2,
+ title: "My first published post",
+ published: true,
+ userId: 1,
+ },
+ ],
+ },
+ {
+ id: 2,
+ name: "Bob",
+ posts: [
+ {
+ id: 3,
+ title: "Clean Code",
+ published: true,
+ userId: 2,
+ },
+ ],
+ },
+];
+```
+
+The `next` function for the "posts" relation will resolve with the following:
+
+```javascript
+[
+ {
+ id: 1,
+ title: "Hello World",
+ published: false,
+ userId: 1,
+ },
+ {
+ id: 2,
+ title: "My first published post",
+ published: true,
+ userId: 1,
+ },
+ {
+ id: 3,
+ title: "Clean Code",
+ published: true,
+ userId: 2,
+ },
+];
+```
+
+For more information on how to modify the results of an `include` action see the [Modifying Results](#modifying-results)
+
+### Select
+
+Similarly to the `include` action, the `select` action will be called for any selected relation with the `args` field
+containing the object or boolean passed as the relation select. For example take the following query:
+
+```javascript
+const result = await client.user.findMany({
+ select: {
+ posts: true,
+ profile: {
+ select: {
+ bio: true,
+ },
+ },
+ },
+});
+```
+
+and for the "posts" relation the middleware function will be called with:
+
+```javascript
+{
+ action: 'select',
+ model: 'Post',
+ args: true,
+ scope: {...}
+}
+```
+
+For the "profile" relation the middleware function will be called with:
+
+```javascript
+{
+ action: 'select',
+ model: 'Profile',
+ args: {
+ bio: true,
+ },
+ scope: {...}
+}
+```
+
+#### Select Results
+
+The `next` function for a `select` action resolves with the result of the `select` action. This is the same as the
+`include` action. See the [Include Results](#include-results) section for more information.
+
+### Relations
+
+The `relations` field of the `scope` object contains the relations relevant to the current model. For example take the
+following query:
+
+```javascript
+const result = await client.user.create({
+ data: {
+ email: "test@test.com",
+ profile: {
+ create: {
+ bio: "Hello World",
+ },
+ },
+ posts: {
+ create: {
+ title: "Hello World",
},
},
},
});
```
-`createNestedMiddleware` calls the passed middleware function from the most deeply nested operation up to the top level operation. For the above example the middleware function is called in the following order:
+The middleware function will be called with the following params for the "profile" relation:
-1. `{ model: 'Post', action: 'connectOrCreate', args: { create: {...}, connect: {...} } }`
-2. `{ model: 'Post', action: 'select', args: { title: true } }`
-3. `{ model: 'Post', action: 'include', args: { select: { title: true } } }`
-4. `{ model: 'Comment', action: 'update', args: { where: { id: 2 }, data: {...} } }`
-5. `{ model: 'Comment', action: 'include', args: { include: { post: { select: { title: true } } } } }`
-6. `{ model: 'User', action: 'update', args: { where: { id: 1 }, data: {...} } }`
+```javascript
+{
+ action: 'create',
+ model: 'Profile',
+ args: {
+ bio: "Hello World",
+ },
+ scope: {
+ parentParams: {...}
+ relations: {
+ to: { name: 'profile', kind: 'object', isList: false, ... },
+ from: { name: 'user', kind: 'object', isList: false, ... },
+ },
+ },
+}
+```
-The params object passed to the `next` function will modify the relevant part of the original params object. Once all the middleware calls have called `next` the updated params object is passed to the Prisma client.
+and the following params for the "posts" relation:
-The result of the Prisma client operation is then returned from the `next` functions according to the part of the result they are concerned with. So the `next` function called for the `User` model resolves with the `User` model's result, the `next` function called for the `Comment` model resolves with the `Comment` model's result, and so on.
+```javascript
+{
+ action: 'create',
+ model: 'Post',
+ args: {
+ title: "Hello World",
+ },
+ scope: {
+ parentParams: {...}
+ relations: {
+ to: { name: 'posts', kind: 'object', isList: true, ... },
+ from: { name: 'author', kind: 'object', isList: false, ... },
+ },
+ },
+}
+```
+
+### Modifying Nested Write Params
+
+When writing middleware that modifies the params of a query you should first write the middleware as if it were vanilla
+middleware and then add conditions for nested writes.
+
+Say you are writing middleware that sets a default value when creating a model for a particular model:
+
+```javascript
+client.$use(
+ createNestedMiddleware((params, next) => {
+ // ignore any non-root actions
+ if (params.scope) {
+ return next(params);
+ }
+
+ // we only want to add default values for the "Invite" model
+ if (params.model !== "Invite") {
+ return next(params);
+ }
+
+ // handle root actions
+ if (params.action === "create") {
+ // set default value for the "code" field
+ if (!params.args.data.code) {
+ params.args.data.code = createCode();
+ }
+ }
-Modifications to the result are made in the same way as modifications to the params. Once all the middleware calls have returned their slice of the result the combined result object is returned from the Prisma client method, in this case `client.user.update`.
+ if (params.action === "createMany") {
+ // set default value for the "code" field
+ params.args.data.forEach((data) => {
+ if (!data.code) {
+ data.code = createCode();
+ }
+ });
+ }
+
+ if (params.action === "upsert") {
+ // set default value for the "code" field
+ if (!params.args.create.code) {
+ params.args.create.code = createCode();
+ }
+ }
+
+ // pass params to next middleware
+ return next(params);
+ })
+);
+```
-If any middleware throws an error at any point then `client.country.update` will throw with that error.
+Then add conditions for the different args and actions that can be found in nested writes:
+
+```javascript
+client.$use(
+ createNestedMiddleware((params, next) => {
+ // we only want to add default values for the "Invite" model
+ if (params.model !== "Invite") {
+ return next(params);
+ }
+
+ // handle root actions
+ if (params.action === "create") {
+ // when the "create" action is from a nested write the data is not in the "data" field
+ if (params.scope) {
+ if (!params.args.code) {
+ params.args.code = createCode();
+ }
+ } else {
+ if (!params.args.data.code) {
+ params.args.data.code = createCode();
+ }
+ }
+ }
+
+ // createMany and upsert do not change
+ [...]
+
+ // handle the "connectOrCreate" action
+ if (params.action === "connectOrCreate") {
+ if (!params.args.create.code) {
+ params.args.create.code = createCode();
+ }
+ }
+
+ // pass params to next middleware
+ return next(params);
+ })
+);
+```
+
+### Modifying Where Params
+
+When writing middleware that modifies the where params of a query it is very important to first write the middleware as
+if it were vanilla middleware and then handle the `where` action. This is because the `where` action is not called for
+the root where object and so you will need to handle it manually.
+
+Say you are writing middleware that excludes models with a particular field, let's call it "invisible" rather than
+"deleted" to make this less familiar:
+
+```javascript
+client.$use(
+ createNestedMiddleware((params, next) => {
+ // ignore any non-root actions
+ if (params.scope) {
+ return next(params);
+ }
+
+ // handle root actions
+ // don't handle actions that only accept unique fields such as findUnique or upsert
+ if (
+ params.action === "findFirst" ||
+ params.action === "findMany" ||
+ params.action === "updateMany" ||
+ params.action === "deleteMany" ||
+ params.action === "count" ||
+ params.action === "aggregate"
+ ) {
+ return next({
+ ...params,
+ where: {
+ ...params.where,
+ invisible: false,
+ },
+ })
+ }
+
+ // pass params to next middleware
+ return next(params);
+ })
+);
+```
+
+Then add conditions for the `where` action:
+
+```javascript
+client.$use(
+ createNestedMiddleware((params, next) => {
+ // handle the "where" action
+ if (params.action === "where") {
+ return next({
+ ...params,
+ args: {
+ ...params.args,
+ invisible: false,
+ },
+ })
+ }
+
+ // handle root actions
+ // don't handle actions that only accept unique fields such as findUnique or upsert
+ if (
+ params.action === "findFirst" ||
+ params.action === "findMany" ||
+ params.action === "updateMany" ||
+ params.action === "deleteMany" ||
+ params.action === "count" ||
+ params.action === "aggregate"
+ ) {
+ return next({
+ ...params,
+ where: {
+ ...params.where,
+ invisible: false,
+ },
+ })
+ }
+
+ // pass params to next middleware
+ return next(params);
+ })
+);
+```
+
+### Modifying Results
+
+When writing middleware that modifies the results of a query you should take the following process:
+
+- handle all the root cases in the same way as you would with vanilla Prisma middleware.
+- handle nested results using the `include` and `select` actions.
+
+Say you are writing middleware that adds a timestamp to the results of a query. You would first handle the root cases:
+
+```javascript
+client.$use(
+ createNestedMiddleware((params, next) => {
+ // ignore any non-root actions
+ if (params.scope) {
+ return next(params);
+ }
+
+ // get result from next middleware
+ const result = await next(params);
+
+ // ensure result is defined
+ if (!result) return result;
+
+ // handle root actions
+ if (
+ params.action === 'findFirst' ||
+ params.action === 'findUnique' ||
+ params.action === 'create' ||
+ params.action === 'update' ||
+ params.action === 'upsert' ||
+ params.action === 'delete'
+ ) {
+ result.timestamp = Date.now();
+ return result;
+ }
+
+ if (params.action === 'findMany') {
+ const result = await next(params);
+ result.forEach(model => {
+ model.timestamp = Date.now();
+ })
+ return result;
+ }
+
+ return result;
+ })
+)
+```
+
+Then you would handle the nested results using the `include` and `select` actions:
+
+```javascript
+client.$use(
+ createNestedMiddleware((params, next) => {
+ // get result from next middleware
+ const result = await next(params);
+
+ // ensure result is defined
+ if (!result) return result;
+
+ // handle root actions
+ [...]
+
+ // handle nested actions
+ if (
+ params.action === 'include' ||
+ params.action === 'select'
+ ) {
+ if (Array.isArray(result)) {
+ result.forEach(model => {
+ model.timestamp = Date.now();
+ })
+ } else {
+ result.timestamp = Date.now();
+ }
+ return result
+ }
+
+ return result;
+ })
+)
+```
+
+You could also write the above middleware by creating new objects for each result rather than mutating the existing
+objects:
+
+```javascript
+client.$use(
+ createNestedMiddleware((params, next) => {
+ [...]
+
+ if (
+ params.action === 'include' ||
+ params.action === 'select'
+ ) {
+ if (Array.isArray(result)) {
+ return result.map(model => ({
+ ...model,
+ timestamp: Date.now(),
+ }))
+ } else {
+ return {
+ ...result,
+ timestamp: Date.now(),
+ }
+ }
+ }
+
+ return result;
+ })
+)
+```
-### Operations Nested in Lists
+NOTE: When modifying results from `include` or `select` actions it is important to either mutate the existing objects or
+spread the existing objects into the new objects. This is because `createNestedMiddleware` needs some fields from the
+original objects in order to correct update the root results.
-When a `next` function needs to return a relation that is nested within a list it combines all the relation values into a single flat array. This means middleware only has to handle flat arrays of results which makes modifying the result before it is returned easier. If the result's parent is needed then it is possible to go through the parent middleware and traverse that relation; only a single depth of relation needs to be traversed as the middleware will be called for each layer.
+### Errors
-For example if a comment is created within an array of posts, the `next` function for comments returns a flattened array of all the comments found within the posts array. When the flattened array is returned at the end of the middleware function the comments are put back into their corresponding posts.
+If any middleware throws an error at any point then the root query will throw with that error. Any middleware that is
+pending will have it's promises rejects at that point.
## LICENSE
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..000266d
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,14 @@
+version: '3.7'
+
+services:
+ postgres:
+ image: "postgres:latest"
+ container_name: further-postgres
+ hostname: postgres
+ user: postgres
+ restart: always
+ environment:
+ - POSTGRES_DATABASE=test
+ - POSTGRES_PASSWORD=123
+ ports:
+ - '5432:5432'
diff --git a/jest.config.e2e.js b/jest.config.e2e.js
new file mode 100644
index 0000000..337cd69
--- /dev/null
+++ b/jest.config.e2e.js
@@ -0,0 +1,6 @@
+/** @type {import('ts-jest').JestConfigWithTsJest} */
+module.exports = {
+ preset: "ts-jest",
+ testEnvironment: "node",
+ testRegex: "test/e2e/.+\\.test\\.ts$",
+};
diff --git a/jest.config.unit.js b/jest.config.unit.js
new file mode 100644
index 0000000..dc8720c
--- /dev/null
+++ b/jest.config.unit.js
@@ -0,0 +1,6 @@
+/** @type {import('ts-jest').JestConfigWithTsJest} */
+module.exports = {
+ preset: "ts-jest",
+ testEnvironment: "node",
+ testRegex: "test/unit/.+\\.test\\.ts$",
+};
diff --git a/package.json b/package.json
index dd26144..2998236 100644
--- a/package.json
+++ b/package.json
@@ -9,7 +9,9 @@
"build": "npm-run-all build:cjs build:esm",
"build:cjs": "tsc -p tsconfig.build.json",
"build:esm": "tsc -p tsconfig.esm.json",
- "test": "prisma generate && jest",
+ "test:unit": "prisma generate && jest --config jest.config.unit.js",
+ "test:e2e": "./test/scripts/run-with-postgres.sh jest --config jest.config.e2e.js --runInBand",
+ "test": "./test/scripts/run-with-postgres.sh jest --runInBand",
"lint": "eslint ./src --fix --ext .ts",
"typecheck": "npm run build:cjs -- --noEmit && npm run build:esm -- --noEmit",
"validate": "kcd-scripts validate lint,typecheck,test",
@@ -34,7 +36,7 @@
"@prisma/client": "*"
},
"devDependencies": {
- "@prisma/client": "^4.8.1",
+ "@prisma/client": "^4.11.0",
"@types/faker": "^5.5.9",
"@types/jest": "^29.2.5",
"@types/lodash": "^4.14.185",
@@ -47,7 +49,7 @@
"jest": "^29.3.1",
"kcd-scripts": "^5.0.0",
"npm-run-all": "^4.1.5",
- "prisma": "^4.8.1",
+ "prisma": "^4.11.0",
"semantic-release": "^17.0.2",
"ts-jest": "^29.0.3",
"ts-node": "^9.1.1",
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 68d6838..75439f2 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -46,6 +46,7 @@ model Comment {
model Profile {
id Int @id @default(autoincrement())
bio String?
+ age Int?
user User @relation(fields: [userId], references: [id])
userId Int @unique
}
diff --git a/src/lib/createNestedMiddleware.ts b/src/lib/createNestedMiddleware.ts
index a298359..dac66eb 100644
--- a/src/lib/createNestedMiddleware.ts
+++ b/src/lib/createNestedMiddleware.ts
@@ -1,23 +1,16 @@
import { Prisma } from "@prisma/client";
import { NestedMiddleware, MiddlewareCall } from "./types";
-import { extractNestedActions } from "./utils/actions";
+import { extractNestedActions } from "./utils/extractNestedActions";
import { executeMiddleware } from "./utils/execution";
import { buildParamsFromCalls } from "./utils/params";
-import { getNestedResult, setNestedResult } from "./utils/results";
-
-if (!Prisma.dmmf) {
- throw new Error(
- "Prisma DMMF not found, please generate Prisma client using `npx prisma generate`"
- );
-}
-
-export const relationsByModel: Record = {};
-Prisma.dmmf.datamodel.models.forEach((model: Prisma.DMMF.Model) => {
- relationsByModel[model.name] = model.fields.filter(
- (field) => field.kind === "object" && field.relationName
- );
-});
+import { buildTargetRelationPath } from "./utils/targets";
+import {
+ addIdSymbolsToResult,
+ getRelationResult,
+ stripIdSymbolsFromResult,
+ updateResultRelation,
+} from "./utils/results";
function isFulfilled(
result: PromiseSettledResult
@@ -35,18 +28,15 @@ export function createNestedMiddleware(
middleware: NestedMiddleware
): Prisma.Middleware {
const nestedMiddleware: NestedMiddleware = async (params, next) => {
- const relations = relationsByModel[params.model || ""] || [];
let calls: MiddlewareCall[] = [];
try {
const executionResults = await Promise.allSettled(
- relations.flatMap((relation) =>
- extractNestedActions(params, relation).map((nestedAction) =>
- executeMiddleware(
- nestedMiddleware,
- nestedAction.params,
- nestedAction.target
- )
+ extractNestedActions(params).map((nestedAction) =>
+ executeMiddleware(
+ middleware,
+ nestedAction.params,
+ nestedAction.target
)
)
);
@@ -55,30 +45,88 @@ export function createNestedMiddleware(
// next promises if we find a rejection
calls = executionResults
.filter(isFulfilled)
- .map(({ value: call }) => call);
+ .flatMap(({ value }) => value);
// consider any rejected execution as a failure of all nested middleware
const failedExecution = executionResults.find(isRejected);
if (failedExecution) throw failedExecution.reason;
+ // build updated params from middleware calls
+ const updatedParams = buildParamsFromCalls(calls, params);
+
// evaluate result from parent middleware
- const result = await middleware(
- buildParamsFromCalls(calls, params),
- next
- );
+ const result = await middleware(updatedParams, next);
- // resolve nested middleware next functions with relevant slice of result
- await Promise.all(
- calls.map(async (call) => {
- // if relationship hasn't been included nestedResult is undefined.
- call.nextPromise.resolve(
- result && getNestedResult(result, call.target.relationName)
- );
+ // bail out if result is null
+ if (result === null) {
+ calls.forEach((call) => call.nextPromise.resolve(undefined));
+ await Promise.all(calls.map((call) => call.result));
+ return null;
+ }
- // set final result relation to be result of nested middleware
- setNestedResult(result, call.target.relationName, await call.result);
+ // add id symbols to result so we can use them to update result relations
+ // with the results from nested middleware
+ addIdSymbolsToResult(result);
+
+ const nestedNextResults = await Promise.all(
+ calls.map(async (call) => {
+ const relationsPath = buildTargetRelationPath(call.target);
+
+ if (result === null || !relationsPath) {
+ call.nextPromise.resolve(undefined);
+ await call.result;
+ return null;
+ }
+
+ const relationResults = getRelationResult(result, relationsPath);
+ call.nextPromise.resolve(relationResults);
+ const updatedResult = await call.result;
+
+ if (typeof relationResults === "undefined") {
+ return null;
+ }
+
+ return {
+ relationsPath,
+ updatedResult,
+ };
})
);
+
+
+ // keep only the relevant result updates from nested next results
+ const resultUpdates = nestedNextResults.filter(
+ (update): update is { relationsPath: string[]; updatedResult: any } =>
+ !!update
+ );
+
+ resultUpdates
+ .sort((a, b) => b.relationsPath.length - a.relationsPath.length)
+ .forEach(({ relationsPath, updatedResult }, i) => {
+ const remainingUpdates = resultUpdates.slice(i);
+ const nextUpdatePath = relationsPath.slice(0, -1).join(".");
+
+ const nextUpdate = remainingUpdates.find(
+ (update) => update?.relationsPath.join(".") === nextUpdatePath
+ );
+
+ if (nextUpdate) {
+ updateResultRelation(
+ nextUpdate.updatedResult,
+ relationsPath[relationsPath.length - 1],
+ updatedResult
+ );
+ return;
+ }
+
+ updateResultRelation(
+ result,
+ relationsPath[relationsPath.length - 1],
+ updatedResult
+ );
+ });
+
+ stripIdSymbolsFromResult(result);
return result;
} catch (e) {
diff --git a/src/lib/types.ts b/src/lib/types.ts
index 25cf218..90b5aad 100644
--- a/src/lib/types.ts
+++ b/src/lib/types.ts
@@ -1,12 +1,18 @@
import { Prisma } from "@prisma/client";
import { DeferredPromise } from "@open-draft/deferred-promise";
+export type Modifier = "is" | "isNot" | "some" | "none" | "every";
+export type LogicalOperator = "AND" | "OR" | "NOT";
+
+export type NestedQueryAction = "where";
export type NestedReadAction = "include" | "select";
export type NestedWriteAction =
| "create"
| "update"
| "upsert"
| "connectOrCreate"
+ | "connect"
+ | "disconnect"
| "createMany"
| "updateMany"
| "delete"
@@ -16,22 +22,33 @@ export type NestedAction =
| Prisma.PrismaAction
| NestedWriteAction
| NestedReadAction
- | "connectOrCreate";
+ | NestedQueryAction;
+
+export type QueryTarget = {
+ action: NestedQueryAction;
+ relationName?: string;
+ modifier?: Modifier;
+ operations?: { logicalOperator: LogicalOperator; index?: number }[];
+ readAction?: NestedReadAction;
+ parentTarget?: Target;
+};
export type ReadTarget = {
action: NestedReadAction;
- relationName: string;
+ relationName?: string;
field?: string;
+ parentTarget?: Target;
};
export type WriteTarget = {
action: NestedWriteAction;
relationName: string;
- field: string;
+ field?: string;
index?: number;
+ parentTarget?: Target;
};
-export type Target = ReadTarget | WriteTarget;
+export type Target = ReadTarget | WriteTarget | QueryTarget;
export type MiddlewareCall = {
nextPromise: DeferredPromise;
@@ -41,13 +58,19 @@ export type MiddlewareCall = {
target: Target;
};
+export type Scope = {
+ parentParams: NestedParams;
+ relations: { to: Prisma.DMMF.Field; from: Prisma.DMMF.Field };
+ modifier?: Modifier;
+ logicalOperators?: LogicalOperator[];
+};
+
export type NestedParams = Omit & {
action: NestedAction;
- scope?: NestedParams;
- relation?: Prisma.DMMF.Field;
+ scope?: Scope;
};
export type NestedMiddleware = (
params: NestedParams,
- next: (modifiedParams: NestedParams) => Promise
+ next: (modifiedParams: NestedParams | NestedParams[]) => Promise
) => Promise;
diff --git a/src/lib/utils/actions.ts b/src/lib/utils/actions.ts
index e43fde1..41bbc1b 100644
--- a/src/lib/utils/actions.ts
+++ b/src/lib/utils/actions.ts
@@ -1,27 +1,39 @@
-import { Prisma } from "@prisma/client";
-import get from "lodash/get";
-
import {
- NestedParams,
+ LogicalOperator,
+ Modifier,
+ NestedQueryAction,
NestedReadAction,
NestedWriteAction,
- Target,
} from "../types";
-import { normaliseRootArgs } from "./args";
-
+export const queryActions: NestedQueryAction[] = ["where"];
export const readActions: NestedReadAction[] = ["include", "select"];
export const writeActions: NestedWriteAction[] = [
"create",
"update",
"upsert",
- "connectOrCreate",
"createMany",
"updateMany",
"delete",
"deleteMany",
+ "disconnect",
+ "connect",
+ "connectOrCreate",
+];
+export const toOneRelationNonListActions: NestedWriteAction[] = [
+ "create",
+ "update",
+ "delete",
+ "upsert",
+ "connect",
+ "connectOrCreate",
+ "disconnect",
];
+export function isQueryAction(action: any): action is NestedQueryAction {
+ return queryActions.includes(action);
+}
+
export function isReadAction(action: any): action is NestedReadAction {
return readActions.includes(action);
}
@@ -30,146 +42,5 @@ export function isWriteAction(action: any): action is NestedWriteAction {
return writeActions.includes(action);
}
-type NestedActionInfo = {
- params: NestedParams;
- target: Target;
-};
-
-const fieldsByWriteAction: Record = {
- create: ["data"],
- update: ["data"],
- upsert: ["update", "create"],
- connectOrCreate: ["create"],
- createMany: ["data"],
- updateMany: ["data"],
- delete: [],
- deleteMany: [],
-};
-
-export function extractNestedWriteActions(
- params: NestedParams,
- relation: Prisma.DMMF.Field
-): NestedActionInfo[] {
- if (!isWriteAction(params.action)) {
- return [];
- }
-
- const model = relation.type as Prisma.ModelName;
- const runInTransaction = params.runInTransaction;
- const nestedWriteActions: NestedActionInfo[] = [];
- const fields = fieldsByWriteAction[params.action] || [];
-
- fields.forEach((field) => {
- const arg = get(params.args, [field, relation.name], {});
-
- Object.keys(arg)
- .filter(isWriteAction)
- .forEach((action) => {
- /*
- Add single writes passed as a list as separate operations.
-
- Checking if the operation is an array is enough since only lists of
- separate operations are passed as arrays at the top level. For example
- a nested create may be passed as an array but a nested createMany will
- pass an object with a data array.
- */
- if (Array.isArray(arg[action])) {
- nestedWriteActions.push(
- ...arg[action].map((item: any, index: number) => ({
- target: {
- field,
- relationName: relation.name,
- action,
- index,
- },
- params: {
- model,
- action,
- args: normaliseRootArgs(action, item),
- runInTransaction,
- dataPath: [],
- scope: params,
- relation,
- },
- }))
- );
- return;
- }
-
- nestedWriteActions.push({
- target: {
- field,
- relationName: relation.name,
- action,
- },
- params: {
- model,
- action,
- args: normaliseRootArgs(action, arg[action]),
- runInTransaction,
- dataPath: [],
- scope: params,
- relation,
- },
- });
- });
- });
-
- return nestedWriteActions;
-}
-
-export function extractNestedReadActions(
- params: NestedParams,
- relation: Prisma.DMMF.Field
-): NestedActionInfo[] {
- const model = relation.type as Prisma.ModelName;
- const runInTransaction = params.runInTransaction;
- const relationName = relation.name;
- const nestedReadActions: NestedActionInfo[] = [];
-
- readActions.forEach((action) => {
- const arg = get(params.args, [action, relation.name]);
- if (!arg) return;
-
- nestedReadActions.push({
- target: { action, relationName: relation.name },
- params: {
- model,
- action,
- args: normaliseRootArgs(action, arg),
- runInTransaction,
- dataPath: [],
- scope: params,
- relation,
- },
- });
-
- // push select nested in an include
- if (action === "include" && arg.select) {
- nestedReadActions.push({
- target: { field: "include", action: "select", relationName },
- params: {
- model,
- action: "select",
- args: normaliseRootArgs("select", arg.select),
- runInTransaction,
- dataPath: [],
- scope: params,
- relation,
- },
- });
- }
- });
-
- return nestedReadActions;
-}
-
-export function extractNestedActions(
- params: NestedParams,
- relation: Prisma.DMMF.Field
-): NestedActionInfo[] {
- return [
- ...extractNestedWriteActions(params, relation),
- ...extractNestedReadActions(params, relation),
- ];
-}
+export const modifiers: Modifier[] = ["is", "isNot", "some", "none", "every"];
+export const logicalOperators: LogicalOperator[] = ["AND", "OR", "NOT"];
diff --git a/src/lib/utils/args.ts b/src/lib/utils/args.ts
deleted file mode 100644
index b6bcbcd..0000000
--- a/src/lib/utils/args.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { NestedAction } from "../types";
-
-export function normaliseRootArgs(action: NestedAction, args: any) {
- if (action === "create") {
- return { data: args };
- }
-
- return args;
-}
-
-export function normaliseNestedArgs(action: NestedAction, args: any) {
- if (action === "create") {
- return args.data;
- }
-
- return args;
-}
diff --git a/src/lib/utils/execution.ts b/src/lib/utils/execution.ts
index 1b8c199..ef87111 100644
--- a/src/lib/utils/execution.ts
+++ b/src/lib/utils/execution.ts
@@ -9,20 +9,17 @@ import {
Target,
} from "../types";
-import { isReadAction, isWriteAction } from "./actions";
-import { isReadTarget, isWriteTarget } from "./targets";
-
export async function executeMiddleware(
middleware: NestedMiddleware,
params: NestedParams,
target: Target
-): Promise {
- const originalParams = cloneDeep(params);
-
- const paramsUpdatedPromise = new DeferredPromise();
+): Promise {
+ const paramsUpdatedPromise = new DeferredPromise<
+ NestedParams | NestedParams[]
+ >();
const nextPromise = new DeferredPromise();
- const result = middleware(params, (updatedParams) => {
+ const result = middleware(cloneDeep(params), (updatedParams) => {
paramsUpdatedPromise.resolve(updatedParams);
return nextPromise;
}).catch((e) => {
@@ -37,8 +34,38 @@ export async function executeMiddleware(
const updatedParams = await paramsUpdatedPromise;
+ if (Array.isArray(updatedParams)) {
+ const calls = await Promise.all(
+ updatedParams.map((updatedParamsItem) => {
+ // execute middleware with updated params if action has changed
+ if (updatedParamsItem.action !== params.action) {
+ return executeMiddleware(
+ middleware,
+ updatedParamsItem,
+ omit(target, "index") as Target
+ );
+ }
+
+ return [
+ {
+ nextPromise,
+ result,
+ updatedParams: updatedParamsItem,
+ origin: target,
+ target: {
+ ...target,
+ action: updatedParamsItem.action as any,
+ },
+ },
+ ];
+ })
+ );
+
+ return calls.flat();
+ }
+
// execute middleware with updated params if action has changed
- if (updatedParams.action !== originalParams.action) {
+ if (updatedParams.action !== params.action) {
return executeMiddleware(
middleware,
updatedParams,
@@ -46,34 +73,13 @@ export async function executeMiddleware(
);
}
- if (isWriteAction(updatedParams.action) && isWriteTarget(target)) {
- return {
+ return [
+ {
nextPromise,
result,
updatedParams,
origin: target,
- target: {
- action: updatedParams.action,
- field: target.field,
- relationName: target.relationName,
- index: target.index,
- },
- };
- }
-
- if (isReadAction(updatedParams.action) && isReadTarget(target)) {
- return {
- nextPromise,
- result,
- updatedParams,
- origin: target,
- target: {
- action: updatedParams.action,
- field: target.field,
- relationName: target.relationName,
- },
- };
- }
-
- throw new Error("Invalid target and params combination");
+ target: { ...target, action: updatedParams.action as any },
+ },
+ ];
}
diff --git a/src/lib/utils/extractNestedActions.ts b/src/lib/utils/extractNestedActions.ts
new file mode 100644
index 0000000..faa1416
--- /dev/null
+++ b/src/lib/utils/extractNestedActions.ts
@@ -0,0 +1,516 @@
+import { Prisma } from "@prisma/client";
+import get from "lodash/get";
+
+import {
+ LogicalOperator,
+ NestedParams,
+ NestedWriteAction,
+ Target,
+} from "../types";
+
+import {
+ isWriteAction,
+ logicalOperators,
+ modifiers,
+ readActions,
+} from "./actions";
+import { findOppositeRelation, relationsByModel } from "./relations";
+
+type NestedActionInfo = {
+ params: NestedParams;
+ target: Target;
+};
+
+// actions have nested relations inside fields within the args object, sometimes
+// relations are defined directly in the args object because the action is in a
+// to one relation, for example the update action. Add undefined for actions where this
+// can happen
+export const fieldsByWriteAction: Record<
+ NestedWriteAction,
+ (string | undefined)[]
+> = {
+ create: [undefined, "data"],
+ update: [undefined, "data"],
+ upsert: ["update", "create"],
+ connectOrCreate: ["create"],
+ createMany: ["data"],
+ updateMany: ["data"],
+ connect: [],
+ disconnect: [],
+ delete: [],
+ deleteMany: [],
+};
+
+export function extractRelationLogicalWhereActions(
+ params: NestedParams,
+ parentTarget?: Target,
+ parentOperations: { logicalOperator: LogicalOperator; index?: number }[] = []
+): NestedActionInfo[] {
+ const relations = relationsByModel[params.model || ""] || [];
+ const nestedWhereActions: NestedActionInfo[] = [];
+
+ const operationsPath: string[] = [];
+ parentOperations.forEach(({ logicalOperator, index }) => {
+ operationsPath.push(logicalOperator);
+
+ if (typeof index === "number") {
+ operationsPath.push(index.toString());
+ }
+ });
+
+ logicalOperators.forEach((logicalOperator) => {
+ const baseArgPath = params.scope ? ["args"] : ["args", "where"];
+ const logicalArg = get(params, [
+ ...baseArgPath,
+ ...operationsPath,
+ logicalOperator,
+ ]);
+ if (!logicalArg) return;
+
+ const nestedOperators = Array.isArray(logicalArg)
+ ? logicalArg.map((_, index) => ({ logicalOperator, index }))
+ : [{ logicalOperator }];
+
+ nestedOperators.forEach((nestedOperator) => {
+ nestedWhereActions.push(
+ ...extractRelationLogicalWhereActions(params, parentTarget, [
+ ...parentOperations,
+ nestedOperator,
+ ])
+ );
+ });
+
+ relations.forEach((relation) => {
+ const model = relation.type as Prisma.ModelName;
+ const oppositeRelation = findOppositeRelation(relation);
+
+ if (Array.isArray(logicalArg)) {
+ logicalArg.forEach((where, index) => {
+ const arg = where[relation.name];
+ if (!arg) return;
+
+ const operations = [...parentOperations, { logicalOperator, index }];
+ const foundModifiers = modifiers.filter((mod) => arg[mod]);
+
+ // if there are no modifiers call the where action without a modifier
+ if (!foundModifiers.length) {
+ nestedWhereActions.push({
+ target: {
+ action: "where" as const,
+ relationName: relation.name,
+ operations,
+ parentTarget,
+ },
+ params: {
+ model,
+ action: "where",
+ args: arg,
+ runInTransaction: params.runInTransaction,
+ dataPath: [],
+ scope: {
+ parentParams: params,
+ logicalOperators: operations.map((op) => op.logicalOperator),
+ relations: { to: relation, from: oppositeRelation },
+ },
+ },
+ });
+
+ return;
+ }
+
+ // if there are modifiers call the where action with each modifier but
+ // not the action without a modifier
+ foundModifiers.forEach((modifier) => {
+ nestedWhereActions.push({
+ target: {
+ action: "where" as const,
+ relationName: relation.name,
+ modifier,
+ operations,
+ parentTarget,
+ },
+ params: {
+ model,
+ action: "where",
+ args: arg[modifier],
+ runInTransaction: params.runInTransaction,
+ dataPath: [],
+ scope: {
+ parentParams: params,
+ modifier,
+ logicalOperators: operations.map((op) => op.logicalOperator),
+ relations: { to: relation, from: oppositeRelation },
+ },
+ },
+ });
+ });
+ });
+
+ return;
+ }
+
+ const arg = logicalArg[relation.name];
+ if (!arg) return;
+
+ const operations = [...parentOperations, { logicalOperator }];
+ const foundModifiers = modifiers.filter((mod) => arg[mod]);
+
+ if (!foundModifiers.length) {
+ nestedWhereActions.push({
+ target: {
+ action: "where",
+ relationName: relation.name,
+ operations,
+ parentTarget,
+ },
+ params: {
+ model,
+ action: "where",
+ args: arg,
+ runInTransaction: params.runInTransaction,
+ dataPath: [],
+ scope: {
+ parentParams: params,
+ logicalOperators: operations.map((op) => op.logicalOperator),
+ relations: { to: relation, from: oppositeRelation },
+ },
+ },
+ });
+
+ return;
+ }
+
+ foundModifiers.forEach((modifier) => {
+ nestedWhereActions.push({
+ target: {
+ action: "where",
+ relationName: relation.name,
+ modifier,
+ operations,
+ parentTarget,
+ },
+ params: {
+ model,
+ action: "where",
+ args: modifier ? arg[modifier] : arg,
+ runInTransaction: params.runInTransaction,
+ dataPath: [],
+ scope: {
+ parentParams: params,
+ modifier,
+ logicalOperators: operations.map((op) => op.logicalOperator),
+ relations: { to: relation, from: oppositeRelation },
+ },
+ },
+ });
+ });
+ });
+ });
+
+ return nestedWhereActions;
+}
+
+export function extractRelationWhereActions(
+ params: NestedParams,
+ parentTarget?: Target
+): NestedActionInfo[] {
+ const relations = relationsByModel[params.model || ""] || [];
+ const runInTransaction = params.runInTransaction;
+
+ const nestedWhereActions = extractRelationLogicalWhereActions(
+ params,
+ parentTarget
+ );
+
+ relations.forEach((relation) => {
+ const model = relation.type as Prisma.ModelName;
+ const oppositeRelation = findOppositeRelation(relation);
+
+ const baseArgPath = params.scope ? ["args"] : ["args", "where"];
+ const arg = get(params, [...baseArgPath, relation.name]);
+ if (!arg) return;
+
+ const foundModifiers = modifiers.filter((mod) => arg[mod]);
+ if (!foundModifiers.length) {
+ nestedWhereActions.push({
+ target: {
+ action: "where",
+ relationName: relation.name,
+ parentTarget,
+ },
+ params: {
+ model,
+ action: "where",
+ args: arg,
+ runInTransaction,
+ dataPath: [],
+ scope: {
+ parentParams: params,
+ relations: { to: relation, from: oppositeRelation },
+ },
+ },
+ });
+
+ return;
+ }
+
+ foundModifiers.forEach((modifier) => {
+ nestedWhereActions.push({
+ target: {
+ action: "where",
+ relationName: relation.name,
+ modifier,
+ parentTarget,
+ },
+ params: {
+ model,
+ action: "where",
+ args: modifier ? arg[modifier] : arg,
+ runInTransaction,
+ dataPath: [],
+ scope: {
+ parentParams: params,
+ modifier,
+ relations: { to: relation, from: oppositeRelation },
+ },
+ },
+ });
+ });
+ });
+
+ return nestedWhereActions.concat(
+ nestedWhereActions.flatMap((nestedActionInfo) =>
+ extractRelationWhereActions(
+ nestedActionInfo.params,
+ nestedActionInfo.target
+ )
+ )
+ );
+}
+
+export function extractRelationWriteActions(
+ params: NestedParams,
+ parentTarget?: Target
+): NestedActionInfo[] {
+ const relations = relationsByModel[params.model || ""] || [];
+
+ if (!isWriteAction(params.action)) return [];
+
+ const nestedWriteActions: NestedActionInfo[] = [];
+ const fields = fieldsByWriteAction[params.action] || [];
+
+ relations.forEach((relation) => {
+ const model = relation.type as Prisma.ModelName;
+ const runInTransaction = params.runInTransaction;
+ const oppositeRelation = findOppositeRelation(relation);
+
+ fields.forEach((field) => {
+ const argPath = ["args", field, relation.name].filter(
+ (part): part is string => !!part
+ );
+ const arg = get(params, argPath, {});
+
+ Object.keys(arg)
+ .filter(isWriteAction)
+ .forEach((action) => {
+ /*
+ Add single writes passed as a list as separate operations.
+
+ Checking if the operation is an array is enough since only lists of
+ separate operations are passed as arrays at the top level. For example
+ a nested create may be passed as an array but a nested createMany will
+ pass an object with a data array.
+ */
+ if (Array.isArray(arg[action])) {
+ nestedWriteActions.push(
+ ...arg[action].map(
+ (item: any, index: number): NestedActionInfo => ({
+ target: {
+ field,
+ relationName: relation.name,
+ action,
+ index,
+ parentTarget,
+ },
+ params: {
+ model,
+ action,
+ args: item,
+ runInTransaction,
+ dataPath: [],
+ scope: {
+ parentParams: params,
+ relations: { to: relation, from: oppositeRelation },
+ },
+ },
+ })
+ )
+ );
+ return;
+ }
+
+ nestedWriteActions.push({
+ target: {
+ field,
+ relationName: relation.name,
+ action,
+ parentTarget,
+ },
+ params: {
+ model,
+ action,
+ args: arg[action],
+ runInTransaction,
+ dataPath: [],
+ scope: {
+ parentParams: params,
+ relations: { to: relation, from: oppositeRelation },
+ },
+ },
+ });
+ });
+ });
+ });
+
+ return nestedWriteActions.concat(
+ nestedWriteActions.flatMap((nestedActionInfo) =>
+ extractRelationWriteActions(
+ nestedActionInfo.params,
+ nestedActionInfo.target
+ )
+ )
+ );
+}
+
+export function extractRelationReadActions(
+ params: NestedParams,
+ parentTarget?: Target
+): NestedActionInfo[] {
+ const relations = relationsByModel[params.model || ""] || [];
+ const nestedActions: NestedActionInfo[] = [];
+
+ relations.forEach((relation) => {
+ const model = relation.type as Prisma.ModelName;
+ const runInTransaction = params.runInTransaction;
+ const oppositeRelation = findOppositeRelation(relation);
+
+ readActions.forEach((action) => {
+ const arg = get(params, ["args", action, relation.name]);
+ if (!arg) return;
+
+ const readActionInfo = {
+ params: {
+ model,
+ action,
+ args: arg,
+ runInTransaction,
+ dataPath: [],
+ scope: {
+ parentParams: params,
+ relations: { to: relation, from: oppositeRelation },
+ },
+ },
+ target: { action, relationName: relation.name, parentTarget },
+ };
+
+ nestedActions.push(readActionInfo);
+
+ if (readActionInfo.params.args?.where) {
+ const whereActionInfo = {
+ target: {
+ action: "where" as const,
+ relationName: relation.name,
+ readAction: action,
+ parentTarget: readActionInfo.target,
+ },
+ params: {
+ model: readActionInfo.params.model,
+ action: "where" as const,
+ args: readActionInfo.params.args.where,
+ runInTransaction: params.runInTransaction,
+ dataPath: [],
+ scope: {
+ parentParams: readActionInfo.params,
+ relations: readActionInfo.params.scope.relations,
+ },
+ },
+ };
+ nestedActions.push(whereActionInfo);
+ nestedActions.push(
+ ...extractRelationWhereActions(
+ whereActionInfo.params,
+ whereActionInfo.target
+ )
+ );
+ }
+
+ // push select nested in an include
+ if (action === "include" && arg.select) {
+ const nestedSelectActionInfo = {
+ params: {
+ model,
+ action: "select" as const,
+ args: arg.select,
+ runInTransaction,
+ dataPath: [],
+ scope: {
+ parentParams: readActionInfo.params,
+ relations: readActionInfo.params.scope.relations,
+ },
+ },
+ target: {
+ field: "include" as const,
+ action: "select" as const,
+ relationName: relation.name,
+ parentTarget,
+ },
+ };
+
+ nestedActions.push(nestedSelectActionInfo);
+
+ if (nestedSelectActionInfo.params.args?.where) {
+ const whereActionInfo = {
+ target: {
+ action: "where" as const,
+ relationName: relation.name,
+ readAction: "select" as const,
+ parentTarget: nestedSelectActionInfo.target,
+ },
+ params: {
+ model: nestedSelectActionInfo.params.model,
+ action: "where" as const,
+ args: nestedSelectActionInfo.params.args.where,
+ runInTransaction,
+ dataPath: [],
+ scope: {
+ parentParams: nestedSelectActionInfo.params,
+ relations: nestedSelectActionInfo.params.scope.relations,
+ },
+ },
+ };
+ nestedActions.push(whereActionInfo);
+ nestedActions.push(
+ ...extractRelationWhereActions(
+ whereActionInfo.params,
+ whereActionInfo.target
+ )
+ );
+ }
+ }
+ });
+ });
+
+ return nestedActions.concat(
+ nestedActions.flatMap((nestedAction) =>
+ extractRelationReadActions(nestedAction.params, nestedAction.target)
+ )
+ );
+}
+
+export function extractNestedActions(params: NestedParams): NestedActionInfo[] {
+ return [
+ ...extractRelationWhereActions(params),
+ ...extractRelationReadActions(params),
+ ...extractRelationWriteActions(params),
+ ];
+}
diff --git a/src/lib/utils/params.ts b/src/lib/utils/params.ts
index 77b7137..2c37563 100644
--- a/src/lib/utils/params.ts
+++ b/src/lib/utils/params.ts
@@ -1,37 +1,57 @@
import set from "lodash/set";
import get from "lodash/get";
import unset from "lodash/unset";
+import merge from "lodash/merge";
import cloneDeep from "lodash/cloneDeep";
+import { omit } from "lodash";
import {
MiddlewareCall,
NestedAction,
NestedParams,
+ NestedWriteAction,
ReadTarget,
+ Target,
WriteTarget,
} from "../types";
import {
+ buildQueryTargetPath,
buildReadTargetPath,
+ buildTargetPath,
buildWriteTargetPath,
+ isQueryTarget,
isReadTarget,
isWriteTarget,
+ targetChainLength,
} from "./targets";
-import { setNestedResult } from "./results";
-import { normaliseNestedArgs } from "./args";
-import { isReadAction, isWriteAction } from "./actions";
+import {
+ isQueryAction,
+ isReadAction,
+ isWriteAction,
+ toOneRelationNonListActions,
+} from "./actions";
+import { fieldsByWriteAction } from "./extractNestedActions";
function addWriteArgsToParams(
params: NestedParams,
target: WriteTarget,
- args: any
+ updatedParams: NestedParams
) {
+ const toOneRelation = !updatedParams.scope?.relations.to.isList;
const targetPath = buildWriteTargetPath(target);
const targetArgs = get(params.args, targetPath);
- // if target doesn't exist, we can just set the updated args
- if (!targetArgs) {
- set(params.args, targetPath, args);
+ // it's possible to target args that have already been updated if the user
+ // has reused the same object in multiple places when changing action, in this
+ // case we can just return
+ if (targetArgs === updatedParams.args) {
+ return;
+ }
+
+ // if target doesn't exist or is a boolean action, we can just set the args
+ if (!targetArgs || typeof targetArgs === "boolean") {
+ set(params.args, targetPath, updatedParams.args);
return;
}
@@ -41,30 +61,36 @@ function addWriteArgsToParams(
set(
params.args,
[...targetPath, "data"],
- [...targetArgs.data, ...args.data]
+ [...targetArgs.data, ...updatedParams.args.data]
);
return;
}
+ // to one relations have actions that cannot be turned into arrays of operations
+ // so merge their args
+ if (toOneRelation && toOneRelationNonListActions.includes(target.action)) {
+ merge(get(params.args, targetPath), updatedParams.args);
+ return;
+ }
+
// if target is an array of operations push args as another operation
if (Array.isArray(targetArgs)) {
- targetArgs.push(args);
+ targetArgs.push(updatedParams.args);
return;
}
// convert target to an array of operations with the target args as the
// first operation and passed args as the second
- set(params.args, targetPath, [targetArgs, args]);
+ set(params.args, targetPath, [targetArgs, updatedParams.args]);
}
function removeWriteArgsFromParams(params: NestedParams, target: WriteTarget) {
// remove args from target
- unset(params.args, buildWriteTargetPath(target));
-
- // TODO:- check if we must handle createMany operations
+ const targetPath = buildWriteTargetPath(target);
+ unset(params.args, targetPath);
// if target parent is now an empty object or array we must remove it
- const targetParentPath = [target.field, target.relationName];
+ const targetParentPath = targetPath.slice(0, -1);
const targetParent = get(params.args, targetParentPath);
if (Object.keys(targetParent).length === 0) {
unset(params.args, targetParentPath);
@@ -72,9 +98,8 @@ function removeWriteArgsFromParams(params: NestedParams, target: WriteTarget) {
}
function removeReadArgsFromParams(params: NestedParams, target: ReadTarget) {
- const targetPath = buildReadTargetPath(target);
-
// remove args from target
+ const targetPath = buildReadTargetPath(target);
unset(params.args, targetPath);
// if target parent is an array with only unset values we must remove it
@@ -100,6 +125,34 @@ export function assertActionChangeIsValid(
"Changing a write action to a read action is not supported"
);
}
+
+ if (isQueryAction(previousAction) && !isQueryAction(nextAction)) {
+ throw new Error(
+ "Changing a query action to a non-query action is not supported"
+ );
+ }
+}
+
+function moveActionChangesToEnd(callA: MiddlewareCall, callB: MiddlewareCall) {
+ if (callA.target.action !== callA.origin.action) {
+ return 1;
+ }
+ if (callB.target.action !== callB.origin.action) {
+ return -1;
+ }
+ return 0;
+}
+
+function findParentCall(
+ calls: MiddlewareCall[],
+ origin: Target
+): MiddlewareCall | undefined {
+ return calls.find(
+ (call) =>
+ origin.parentTarget &&
+ buildTargetPath(origin.parentTarget).join(".") ===
+ buildTargetPath(call.origin).join(".")
+ );
}
export function buildParamsFromCalls(
@@ -108,34 +161,76 @@ export function buildParamsFromCalls(
) {
const finalParams = cloneDeep(parentParams);
- calls.forEach(({ target, origin, updatedParams }) => {
- assertActionChangeIsValid(origin.action, target.action);
+ // calls should update the parent calls updated params
+
+ // sort calls so we set from deepest to shallowest
+ // actions that are at the same depth should put action changes at the end
+ const sortedCalls = calls.sort((a, b) => {
+ const aDepth = targetChainLength(a.target);
+ const bDepth = targetChainLength(b.target);
+
+ if (aDepth === bDepth) {
+ return moveActionChangesToEnd(a, b);
+ }
+
+ return bDepth - aDepth;
+ });
+
+ // eslint-disable-next-line complexity
+ sortedCalls.forEach((call, i) => {
+ const parentCall = findParentCall(calls.slice(i), call.origin);
+ const targetParams = parentCall?.updatedParams || finalParams;
+
+ const origin = omit(call.origin, "parentTarget");
+ const target = omit(call.target, "parentTarget");
+
+ if (origin.action !== target.action) {
+ assertActionChangeIsValid(origin.action, target.action);
+ }
if (isWriteTarget(target) && isWriteTarget(origin)) {
- const targetPath = buildWriteTargetPath(target);
- const targetArgs = get(finalParams.args, targetPath);
- const updatedArgs = normaliseNestedArgs(
- target.action,
- updatedParams.args
- );
+ // if action has not changed use normal target to set args
+ if (target.action === origin.action) {
+ const targetPath = buildWriteTargetPath(target);
+ const targetArgs = get(targetParams.args, targetPath);
+
+ // if target hasn't changed but is an array it has been merged
+ // the original target must be the first element of the array
+ if (Array.isArray(targetArgs)) {
+ targetArgs[0] = call.updatedParams.args;
+ return;
+ }
- // if target hasn't changed but is an array it has been merged
- // the original target must be the first element of the array
- if (target.action === origin.action && Array.isArray(targetArgs)) {
- targetArgs[0] = updatedArgs;
+ // set the updated args if the target hasn't changed
+ set(targetParams.args, targetPath, call.updatedParams.args);
return;
}
- // set the updated args if the target hasn't changed
- if (target.action === origin.action) {
- set(finalParams.args, targetPath, updatedArgs);
+ // if parent action has not changed we can use our normal targets
+ if (
+ targetParams.action === call.updatedParams.scope?.parentParams.action
+ ) {
+ addWriteArgsToParams(targetParams, target, call.updatedParams);
+ removeWriteArgsFromParams(targetParams, origin);
return;
}
- // if action has changed we add merge args with target and remove the args
- // from the origin
- addWriteArgsToParams(finalParams, target, updatedArgs);
- removeWriteArgsFromParams(finalParams, origin);
+ // if parent action has changed we must modify out target to match the
+ // parent action
+ const fields =
+ fieldsByWriteAction[targetParams.action as NestedWriteAction];
+
+ fields.forEach((field) => {
+ const newOrigin = { ...origin, field };
+ const newTarget = { ...target, field };
+
+ if (get(targetParams.args, buildWriteTargetPath(newOrigin))) {
+ // if action has changed we add merge args with target and remove the
+ // args from the origin
+ addWriteArgsToParams(targetParams, newTarget, call.updatedParams);
+ removeWriteArgsFromParams(targetParams, newOrigin);
+ }
+ });
}
if (isReadTarget(target) && isReadTarget(origin)) {
@@ -145,7 +240,7 @@ export function buildParamsFromCalls(
// pass this through so Prisma can throw an error to let the user know.
if (
origin.action !== target.action &&
- typeof get(finalParams.args, targetPath) !== "undefined"
+ typeof get(targetParams, targetPath) !== "undefined"
) {
return;
}
@@ -153,17 +248,27 @@ export function buildParamsFromCalls(
// because includes and selects cannot be at the same level we can safely
// set target path to be the updated args without worrying about
// overwriting the original args
- setNestedResult(
- finalParams.args,
- targetPath,
- normaliseNestedArgs(target.action, updatedParams.args)
- );
+ set(targetParams.args, targetPath, call.updatedParams.args);
// remove the origin args if the action has changed
if (target.action !== origin.action) {
- removeReadArgsFromParams(finalParams, origin);
+ removeReadArgsFromParams(targetParams, origin);
}
}
+
+ if (isQueryTarget(target) && isQueryTarget(origin)) {
+ if (target.readAction) {
+ set(targetParams.args, "where", call.updatedParams.args);
+ return;
+ }
+
+ const basePath = parentCall ? [] : ["where"];
+ set(
+ targetParams.args,
+ [...basePath, ...buildQueryTargetPath(target)],
+ call.updatedParams.args
+ );
+ }
});
return finalParams;
diff --git a/src/lib/utils/relations.ts b/src/lib/utils/relations.ts
new file mode 100644
index 0000000..18d02e8
--- /dev/null
+++ b/src/lib/utils/relations.ts
@@ -0,0 +1,31 @@
+import { Prisma } from "@prisma/client";
+
+if (!Prisma.dmmf) {
+ throw new Error(
+ "Prisma DMMF not found, please generate Prisma client using `npx prisma generate`"
+ );
+}
+
+export const relationsByModel: Record = {};
+Prisma.dmmf.datamodel.models.forEach((model: Prisma.DMMF.Model) => {
+ relationsByModel[model.name] = model.fields.filter(
+ (field) => field.kind === "object" && field.relationName
+ );
+});
+
+export function findOppositeRelation(relation: Prisma.DMMF.Field) {
+ const parentRelations =
+ relationsByModel[relation.type as Prisma.ModelName] || [];
+
+ const oppositeRelation = parentRelations.find(
+ (parentRelation) =>
+ parentRelation !== relation &&
+ parentRelation.relationName === relation.relationName
+ );
+
+ if (!oppositeRelation) {
+ throw new Error(`Unable to find opposite relation to ${relation.name}`);
+ }
+
+ return oppositeRelation;
+}
diff --git a/src/lib/utils/results.ts b/src/lib/utils/results.ts
index 5156dc7..a615df7 100644
--- a/src/lib/utils/results.ts
+++ b/src/lib/utils/results.ts
@@ -1,73 +1,154 @@
-import get from "lodash/get";
-import set from "lodash/set";
+const idSymbol = Symbol("id");
+const parentIdSymbol = Symbol("parentId");
-const parentSymbol = Symbol("parent");
-
-function addParentToResult(parent: any, result: any) {
- if (!Array.isArray(result)) {
- return { ...result, [parentSymbol]: parent };
+function addIdSymbolsToObject(
+ obj: Record,
+ id: number,
+ parentId?: number
+) {
+ obj[idSymbol] = id;
+ if (parentId) {
+ obj[parentIdSymbol] = parentId;
}
+}
- return result.map((item) => ({ ...item, [parentSymbol]: parent }));
+function stripIdSymbolsFromObject(obj: Record) {
+ if (obj[idSymbol]) {
+ delete obj[idSymbol];
+ }
+ if (obj[parentIdSymbol]) {
+ delete obj[parentIdSymbol];
+ }
}
-function removeParentFromResult(result: any) {
- if (!Array.isArray(result)) {
- const { [parentSymbol]: _, ...rest } = result;
- return rest;
+export function addIdSymbolsToResult(
+ result: any,
+ parentId?: number,
+ startId = 1
+): number {
+ let id = startId;
+
+ if (Array.isArray(result)) {
+ result.forEach((item) => {
+ if (typeof item === "object" && item !== null) {
+ addIdSymbolsToObject(item, id, parentId);
+ id += 1;
+
+ Object.getOwnPropertyNames(item).forEach((key) => {
+ if (typeof item[key] === "object" && item[key] !== null) {
+ id = addIdSymbolsToResult(item[key], item[idSymbol], id);
+ }
+ });
+ }
+ });
+
+ return id;
}
- return result.map(({ [parentSymbol]: _, ...rest }: any) => rest);
+ if (typeof result === "object" && result !== null) {
+ addIdSymbolsToObject(result, id, parentId);
+ id += 1;
+
+ Object.getOwnPropertyNames(result).forEach((key) => {
+ if (typeof result[key] === "object" && result[key] !== null) {
+ id = addIdSymbolsToResult(result[key], result[idSymbol], id);
+ }
+ });
+ }
+
+ return id;
}
-export function getNestedResult(result: any, targetPath: string | string[]) {
- if (!Array.isArray(result)) {
- return get(result, targetPath);
+export function stripIdSymbolsFromResult(result: any) {
+ if (Array.isArray(result)) {
+ result.forEach((item) => {
+ if (typeof item === "object" && item !== null) {
+ stripIdSymbolsFromObject(item);
+
+ Object.getOwnPropertyNames(item).forEach((key) => {
+ if (typeof item[key] === "object" && item[key] !== null) {
+ stripIdSymbolsFromResult(item[key]);
+ }
+ });
+ }
+ });
+ return;
}
- return result.reduce((acc, item) => {
- const itemResult = get(item, targetPath);
- if (typeof itemResult !== "object" || itemResult === null) {
- return acc;
+ if (typeof result === "object" && result !== null) {
+ stripIdSymbolsFromObject(result);
+
+ Object.getOwnPropertyNames(result).forEach((key) => {
+ if (typeof result[key] === "object" && result[key] !== null) {
+ stripIdSymbolsFromResult(result[key]);
+ }
+ });
+ }
+}
+
+export function getRelationResult(result: any, relations: string[]): any {
+ let relationResult = result;
+
+ for (const relation of relations) {
+ if (!relationResult) return;
+
+ if (Array.isArray(relationResult)) {
+ relationResult = relationResult
+ .flatMap((item) => item[relation])
+ .filter(Boolean);
+ } else {
+ relationResult = relationResult[relation];
}
+ }
- return acc.concat(addParentToResult(item, itemResult));
- }, []);
+ return relationResult;
}
-export function setNestedResult(
+function injectRelationResult(
result: any,
- targetPath: string | string[],
- modifiedResult: any
+ relation: string,
+ relationResult: any
) {
- if (!Array.isArray(result)) {
- return set(result, targetPath, modifiedResult);
+ if (Array.isArray(relationResult) && Array.isArray(result[relation])) {
+ result[relation] = relationResult.filter(
+ (item) => item[parentIdSymbol] === result[idSymbol]
+ );
+ return;
}
- result.forEach((item: any) => {
- const originalResult = get(item, targetPath);
-
- // if original result was an array we need to filter the result to match
- if (Array.isArray(originalResult)) {
- return set(
- item,
- targetPath,
- removeParentFromResult(
- modifiedResult.filter(
- (modifiedItem: any) => modifiedItem[parentSymbol] === item
- )
- )
- );
- }
+ if (Array.isArray(relationResult) && !Array.isArray(result[relation])) {
+ result[relation] =
+ relationResult.find(
+ (item) => item[parentIdSymbol] === result[idSymbol]
+ ) || null;
+ return;
+ }
- // if the orginal result was not an array we can just set the result
- const modifiedResultItem = modifiedResult.find(
- ({ [parentSymbol]: parent }: any) => parent === item
- );
- return set(
- item,
- targetPath,
- modifiedResultItem ? removeParentFromResult(modifiedResultItem) : null
- );
- });
+ if (Array.isArray(result[relation])) {
+ throw new Error("Cannot inject a single result into an array result");
+ }
+
+ result[relation] = relationResult;
+}
+
+export function updateResultRelation(
+ result: any,
+ relation: string,
+ relationResult: any
+) {
+ if (Array.isArray(result)) {
+ result.forEach((item) => {
+ if (typeof item === "object" && item !== null && item[relation]) {
+ injectRelationResult(item, relation, relationResult);
+ }
+ });
+
+ return result;
+ }
+
+ if (typeof result === "object" && result !== null && result[relation]) {
+ injectRelationResult(result, relation, relationResult);
+ }
+
+ return result;
}
diff --git a/src/lib/utils/targets.ts b/src/lib/utils/targets.ts
index cd1b292..bb1d711 100644
--- a/src/lib/utils/targets.ts
+++ b/src/lib/utils/targets.ts
@@ -1,9 +1,16 @@
-import { ReadTarget, Target, WriteTarget } from "../types";
-
import {
- isReadAction,
- isWriteAction,
-} from "./actions";
+ LogicalOperator,
+ QueryTarget,
+ ReadTarget,
+ Target,
+ WriteTarget,
+} from "../types";
+
+import { isQueryAction, isReadAction, isWriteAction } from "./actions";
+
+export function isQueryTarget(target: any): target is QueryTarget {
+ return isQueryAction(target.action);
+}
export function isReadTarget(target: any): target is ReadTarget {
return isReadAction(target.action);
@@ -13,26 +20,101 @@ export function isWriteTarget(target: any): target is WriteTarget {
return isWriteAction(target.action);
}
-export function buildWriteTargetPath(target: WriteTarget) {
- const targetPath = [target.field, target.relationName, target.action];
+export function buildOperationsPath(
+ operations?: { logicalOperator: LogicalOperator; index?: number }[]
+) {
+ if (!operations) return [];
- if (typeof target.index === "number") {
- targetPath.push(target.index.toString());
+ return operations.flatMap((op) => {
+ if (typeof op.index === "number")
+ return [op.logicalOperator, op.index.toString()];
+
+ return [op.logicalOperator];
+ });
+}
+
+export function buildQueryTargetPath(target: QueryTarget): string[] {
+ const path = target.parentTarget
+ ? buildTargetPath(target.parentTarget)
+ : [];
+
+ if (!target.relationName) {
+ return [...path, target.action];
}
- return targetPath;
+ if (target.operations) {
+ path.push(...buildOperationsPath(target.operations));
+ }
+
+ if (target.readAction) {
+ path.push(target.readAction);
+ }
+
+ path.push(target.relationName);
+
+ if (target.readAction) {
+ path.push("where");
+ }
+
+ if (target.modifier) {
+ path.push(target.modifier);
+ }
+
+ return path;
}
-export function buildReadTargetPath(target: ReadTarget) {
+export function buildWriteTargetPath(target: WriteTarget): string[] {
+ const path = target.parentTarget ? buildTargetPath(target.parentTarget) : [];
+
if (target.field) {
- return [target.field, target.relationName, target.action];
+ path.push(target.field);
}
- return [target.action, target.relationName];
+ path.push(target.relationName, target.action);
+
+ if (typeof target.index === "number") {
+ path.push(target.index.toString());
+ }
+
+ return path;
+}
+
+export function buildReadTargetPath(target: ReadTarget): string[] {
+ const path = target.parentTarget ? buildTargetPath(target.parentTarget) : [];
+
+ if (!target.relationName) {
+ return [...path, target.action];
+ }
+
+ if (!target.field) {
+ return [...path, target.action, target.relationName];
+ }
+
+ return [...path, target.field, target.relationName, target.action];
}
export function buildTargetPath(target: Target) {
- return isReadTarget(target)
- ? buildReadTargetPath(target)
- : buildWriteTargetPath(target);
+ if (isQueryTarget(target)) return buildQueryTargetPath(target);
+ if (isReadTarget(target)) return buildReadTargetPath(target);
+ return buildWriteTargetPath(target);
+}
+
+export const buildTargetRelationPath = (target: Target): string[] | null => {
+ if (!isReadTarget(target)) return null;
+
+ if (target.parentTarget) {
+ const basePath = buildTargetRelationPath(target.parentTarget);
+ if (!basePath) return null;
+
+ return target.relationName ? [...basePath, target.relationName] : basePath;
+ }
+
+ return target.relationName ? [target.relationName] : [];
+};
+
+export function targetChainLength(target: Target, count = 0): number {
+ if (!target.parentTarget) {
+ return count + 1;
+ }
+ return targetChainLength(target.parentTarget, count + 1);
}
diff --git a/test/actions.test.ts b/test/actions.test.ts
deleted file mode 100644
index 7c09a4d..0000000
--- a/test/actions.test.ts
+++ /dev/null
@@ -1,1555 +0,0 @@
-import faker from "faker";
-import { set } from "lodash";
-
-import { createNestedMiddleware } from "../src";
-import { createParams } from "./utils/createParams";
-import { wait } from "./utils/wait";
-
-describe("actions", () => {
- it("allows middleware to modify root params action", async () => {
- const nestedMiddleware = createNestedMiddleware((params, next) => {
- return next({
- ...params,
- action: "findFirst",
- });
- });
-
- const next = jest.fn((_: any) => Promise.resolve(null));
- const params = createParams("User", "findUnique", {
- where: { id: faker.datatype.number() },
- });
- await nestedMiddleware(params, next);
-
- expect(next).toHaveBeenCalledWith(set(params, "action", "findFirst"));
- });
-
- it("allows middleware to modify root params action asynchronously", async () => {
- const nestedMiddleware = createNestedMiddleware(async (params, next) => {
- await wait(100);
- return next({
- ...params,
- action: "findFirst",
- });
- });
-
- const next = jest.fn((_: any) => Promise.resolve(null));
- const params = createParams("User", "findUnique", {
- where: { id: faker.datatype.number() },
- });
- await nestedMiddleware(params, next);
-
- expect(next).toHaveBeenCalledWith(set(params, "action", "findFirst"));
- });
-
- it("applies middleware to operations moved to new action type", async () => {
- const nestedMiddleware = createNestedMiddleware((params, next) => {
- if (params.action === "update" && params.scope) {
- return next({
- ...params,
- action: "upsert",
- args: {
- where: { id: params.args.where.id },
- create: params.args.data,
- update: params.args.data,
- },
- });
- }
-
- if (params.action === "upsert") {
- return next({
- ...params,
- args: {
- ...params.args,
- update: {
- ...params.args.update,
- number: faker.datatype.number(),
- },
- },
- });
- }
-
- return next(params);
- });
-
- const next = jest.fn((_: any) => Promise.resolve(null));
- const params = createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- upsert: {
- where: { id: faker.datatype.number() },
- create: { title: faker.lorem.sentence() },
- update: { title: faker.lorem.sentence() },
- },
- update: {
- where: { id: faker.datatype.number() },
- data: { title: faker.lorem.sentence() },
- },
- },
- },
- });
-
- await nestedMiddleware(params, next);
-
- expect(next).toHaveBeenCalledWith(
- set(params, "args.data.posts", {
- upsert: [
- {
- where: params.args.data.posts.upsert.where,
- create: params.args.data.posts.upsert.create,
- update: {
- title: params.args.data.posts.upsert.update.title,
- number: expect.any(Number),
- },
- },
- {
- where: params.args.data.posts.update.where,
- create: params.args.data.posts.update.data,
- update: {
- title: params.args.data.posts.update.data.title,
- // this should also be applied
- number: expect.any(Number),
- },
- },
- ],
- })
- );
- });
-
- it("merges operation converted to existing operation correctly when converted operation defined before target operation", async () => {
- const nestedMiddleware = createNestedMiddleware((params, next) => {
- if (params.action === "update" && params.scope) {
- return next({
- ...params,
- action: "upsert",
- args: {
- where: { id: params.args.where.id },
- create: params.args.data,
- update: params.args.data,
- },
- });
- }
-
- return next(params);
- });
-
- const next = jest.fn((_: any) => Promise.resolve(null));
- const params = createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- update: {
- where: { id: faker.datatype.number() },
- data: { title: faker.lorem.sentence() },
- },
- upsert: {
- where: { id: faker.datatype.number() },
- create: { title: faker.lorem.sentence() },
- update: { title: faker.lorem.sentence() },
- },
- },
- },
- });
-
- await nestedMiddleware(params, next);
-
- expect(next).toHaveBeenCalledWith(
- set(params, "args.data.posts", {
- upsert: [
- {
- where: params.args.data.posts.upsert.where,
- create: params.args.data.posts.upsert.create,
- update: params.args.data.posts.upsert.update,
- },
- {
- where: params.args.data.posts.update.where,
- create: params.args.data.posts.update.data,
- update: params.args.data.posts.update.data,
- },
- ],
- })
- );
- });
-
- it("waits for all middleware to finish before calling next when modifying nested action", async () => {
- const nestedMiddleware = createNestedMiddleware(async (params, next) => {
- if (params.action === "create" && params.scope) {
- await wait(100);
- return next({
- ...params,
- action: "update",
- args: {
- ...params.args,
- where: { id: params.args.data.id },
- },
- });
- }
-
- if (params.action === "update") {
- await wait(200);
- return next({
- ...params,
- action: "upsert",
- args: {
- where: params.args.where,
- create: params.args.data,
- update: params.args.data,
- },
- });
- }
-
- return next(params);
- });
-
- const next = jest.fn((_: any) => Promise.resolve(null));
- const params = createParams("User", "create", {
- data: {
- email: faker.internet.email(),
- posts: {
- create: {
- id: faker.datatype.number(),
- title: faker.lorem.sentence(),
- comments: {
- create: {
- id: faker.datatype.number(),
- content: faker.lorem.sentence(),
- authorId: faker.datatype.number(),
- },
- },
- },
- },
- },
- });
- await nestedMiddleware(params, next);
-
- expect(next).toHaveBeenCalledWith(
- set(params, "args.data.posts", {
- upsert: {
- where: { id: params.args.data.posts.create.id },
- create: {
- ...params.args.data.posts.create,
- comments: {
- upsert: {
- where: {
- id: params.args.data.posts.create.comments.create.id,
- },
- create: params.args.data.posts.create.comments.create,
- update: params.args.data.posts.create.comments.create,
- },
- },
- },
- update: {
- ...params.args.data.posts.create,
- comments: {
- upsert: {
- where: {
- id: params.args.data.posts.create.comments.create.id,
- },
- create: params.args.data.posts.create.comments.create,
- update: params.args.data.posts.create.comments.create,
- },
- },
- },
- },
- })
- );
- });
-
- it("allows middleware to modify deeply nested actions", async () => {
- const nestedMiddleware = createNestedMiddleware((params, next) => {
- if (params.action === "create" && params.args.data.id) {
- return next({
- ...params,
- action: "upsert",
- args: {
- where: { id: params.args.data.id },
- create: params.args.data,
- update: params.args.data,
- },
- });
- }
-
- return next(params);
- });
-
- const next = jest.fn((_: any) => Promise.resolve(null));
- const params = createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- create: {
- id: faker.datatype.number(),
- title: faker.lorem.sentence(),
- comments: {
- create: {
- id: faker.datatype.number(),
- authorId: faker.datatype.number(),
- content: faker.lorem.sentence(),
- },
- },
- },
- },
- },
- });
- const createCommentData = params.args.data.posts.create.comments.create;
- const createPostData = {
- ...params.args.data.posts.create,
- comments: {
- upsert: {
- where: { id: createCommentData.id },
- create: createCommentData,
- update: createCommentData,
- },
- },
- };
-
- await nestedMiddleware(params, next);
-
- expect(next).toHaveBeenCalledWith(
- set(params, "args.data.posts", {
- upsert: {
- where: { id: createPostData.id },
- create: createPostData,
- update: createPostData,
- },
- })
- );
- });
-
- describe("create", () => {
- it("allows middleware to modify nested create action to be an update", async () => {
- const nestedMiddleware = createNestedMiddleware((params, next) => {
- if (params.action === "create") {
- return next({
- ...params,
- action: "update",
- args: {
- where: { id: params.args.data.id },
- data: params.args.data,
- },
- });
- }
-
- return next(params);
- });
-
- const next = jest.fn((_: any) => Promise.resolve(null));
- const params = createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- create: {
- id: faker.datatype.number(),
- title: faker.lorem.sentence(),
- },
- },
- },
- });
-
- await nestedMiddleware(params, next);
-
- expect(next).toHaveBeenCalledWith(
- set(params, "args.data.posts", {
- update: {
- where: { id: params.args.data.posts.create.id },
- data: params.args.data.posts.create,
- },
- })
- );
- });
-
- it("allows middleware to modify nested create action to be an upsert", async () => {
- const nestedMiddleware = createNestedMiddleware((params, next) => {
- if (params.action === "create") {
- return next({
- ...params,
- action: "upsert",
- args: {
- where: { id: params.args.data.id },
- create: params.args.data,
- update: params.args.data,
- },
- });
- }
-
- return next(params);
- });
-
- const next = jest.fn((_: any) => Promise.resolve(null));
- const params = createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- create: {
- title: faker.lorem.sentence(),
- },
- },
- },
- });
- const createData = params.args.data.posts.create;
- await nestedMiddleware(params, next);
-
- expect(next).toHaveBeenCalledWith(
- set(params, "args.data.posts", {
- upsert: {
- where: { id: createData.id },
- create: createData,
- update: createData,
- },
- })
- );
- });
-
- it("allows middleware to modify nested create action to be a createMany", async () => {
- const nestedMiddleware = createNestedMiddleware((params, next) => {
- if (params.action === "create") {
- return next({
- ...params,
- action: "createMany",
- args: {
- data: [params.args.data],
- },
- });
- }
-
- return next(params);
- });
-
- const next = jest.fn((_: any) => Promise.resolve(null));
- const params = createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- create: { title: faker.lorem.sentence() },
- },
- },
- });
-
- await nestedMiddleware(params, next);
-
- expect(next).toHaveBeenCalledWith(
- set(params, "args.data.posts", {
- createMany: { data: [params.args.data.posts.create] },
- })
- );
- });
-
- it("allows middleware to modify nested create action array to be a createMany", async () => {
- const nestedMiddleware = createNestedMiddleware((params, next) => {
- if (params.action === "create") {
- return next({
- ...params,
- action: "createMany",
- args: { data: [params.args.data] },
- });
- }
-
- return next(params);
- });
-
- const next = jest.fn((_: any) => Promise.resolve(null));
- const params = createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- create: [
- { title: faker.lorem.sentence() },
- { title: faker.lorem.sentence() },
- ],
- },
- },
- });
-
- await nestedMiddleware(params, next);
-
- expect(next).toHaveBeenCalledWith(
- set(params, "args.data.posts", {
- createMany: { data: params.args.data.posts.create },
- })
- );
- });
-
- it("allows middleware to modify nested create action to be a connectOrCreate", async () => {
- const nestedMiddleware = createNestedMiddleware((params, next) => {
- if (params.action === "create") {
- return next({
- ...params,
- action: "connectOrCreate",
- args: {
- where: { id: params.args.data.id },
- create: params.args.data,
- },
- });
- }
-
- return next(params);
- });
-
- const next = jest.fn((_: any) => Promise.resolve(null));
- const params = createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- create: {
- id: faker.datatype.number(),
- title: faker.lorem.sentence(),
- },
- },
- },
- });
-
- await nestedMiddleware(params, next);
-
- expect(next).toHaveBeenCalledWith(
- set(params, "args.data.posts", {
- connectOrCreate: {
- where: { id: params.args.data.posts.create.id },
- create: params.args.data.posts.create,
- },
- })
- );
- });
- });
-
- describe("update", () => {
- it("allows middleware to modify nested update action to be a create", async () => {
- const nestedMiddleware = createNestedMiddleware((params, next) => {
- if (params.action === "update" && params.model === "Post") {
- return next({
- ...params,
- action: "create",
- args: {
- data: params.args.data,
- },
- });
- }
-
- return next(params);
- });
-
- const next = jest.fn((_: any) => Promise.resolve(null));
- const params = createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- update: {
- where: { id: faker.datatype.number() },
- data: { title: faker.lorem.sentence() },
- },
- },
- },
- });
-
- await nestedMiddleware(params, next);
-
- expect(next).toHaveBeenCalledWith(
- set(params, "args.data.posts", {
- create: params.args.data.posts.update.data,
- })
- );
- });
-
- it("allows middleware to modify nested update action to be a createMany", async () => {
- const nestedMiddleware = createNestedMiddleware((params, next) => {
- if (params.action === "update" && params.model === "Post") {
- return next({
- ...params,
- action: "createMany",
- args: {
- data: [params.args.data],
- },
- });
- }
-
- return next(params);
- });
-
- const next = jest.fn((_: any) => Promise.resolve(null));
- const params = createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- update: {
- where: { id: faker.datatype.number() },
- data: { title: faker.lorem.sentence() },
- },
- },
- },
- });
-
- await nestedMiddleware(params, next);
-
- expect(next).toHaveBeenCalledWith(
- set(params, "args.data.posts", {
- createMany: { data: [params.args.data.posts.update.data] },
- })
- );
- });
-
- it("allows middleware to modify nested update action to be a connectOrCreate", async () => {
- const nestedMiddleware = createNestedMiddleware((params, next) => {
- if (params.action === "update" && params.model === "Post") {
- return next({
- ...params,
- action: "connectOrCreate",
- args: {
- where: params.args.where,
- create: params.args.data,
- },
- });
- }
-
- return next(params);
- });
-
- const next = jest.fn((_: any) => Promise.resolve(null));
- const params = createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- update: {
- where: { id: faker.datatype.number() },
- data: {
- title: faker.lorem.sentence(),
- },
- },
- },
- },
- });
-
- await nestedMiddleware(params, next);
-
- expect(next).toHaveBeenCalledWith(
- set(params, "args.data.posts", {
- connectOrCreate: {
- where: params.args.data.posts.update.where,
- create: params.args.data.posts.update.data,
- },
- })
- );
- });
-
- it("allows middleware to modify nested update action to be an updateMany", async () => {
- const nestedMiddleware = createNestedMiddleware((params, next) => {
- if (params.action === "update" && params.model === "Post") {
- return next({
- ...params,
- action: "updateMany",
- });
- }
-
- return next(params);
- });
-
- const next = jest.fn((_: any) => Promise.resolve(null));
- const params = createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- update: {
- where: { id: faker.datatype.number() },
- data: {
- title: faker.lorem.sentence(),
- },
- },
- },
- },
- });
-
- await nestedMiddleware(params, next);
-
- expect(next).toHaveBeenCalledWith(
- set(params, "args.data.posts", {
- updateMany: params.args.data.posts.update,
- })
- );
- });
-
- it("allows middleware to modify nested update action to be an upsert", async () => {
- const nestedMiddleware = createNestedMiddleware((params, next) => {
- if (params.action === "update" && params.model === "Post") {
- return next({
- ...params,
- action: "upsert",
- args: {
- where: { id: params.args.where.id },
- create: params.args.data,
- update: params.args.data,
- },
- });
- }
-
- return next(params);
- });
-
- const next = jest.fn((_: any) => Promise.resolve(null));
- const params = createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- update: {
- where: { id: faker.datatype.number() },
- data: {
- title: faker.lorem.sentence(),
- },
- },
- },
- },
- });
- const updateId = params.args.data.posts.update.where.id;
- const updateData = params.args.data.posts.update.data;
-
- await nestedMiddleware(params, next);
-
- expect(next).toHaveBeenCalledWith(
- set(params, "args.data.posts", {
- upsert: {
- where: { id: updateId },
- create: updateData,
- update: updateData,
- },
- })
- );
- });
-
- it("allows middleware to modify nested update action to be a delete", async () => {
- const nestedMiddleware = createNestedMiddleware((params, next) => {
- if (params.action === "update" && params.model === "Post") {
- return next({
- ...params,
- action: "delete",
- args: {
- where: { id: params.args.where.id },
- },
- });
- }
-
- return next(params);
- });
-
- const next = jest.fn((_: any) => Promise.resolve(null));
- const params = createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- update: {
- where: { id: faker.datatype.number() },
- data: {
- title: faker.lorem.sentence(),
- },
- },
- },
- },
- });
-
- await nestedMiddleware(params, next);
-
- expect(next).toHaveBeenCalledWith(
- set(params, "args.data.posts", {
- delete: {
- where: params.args.data.posts.update.where,
- },
- })
- );
- });
-
- it("allows middleware to modify nested update action to be a deleteMany", async () => {
- const nestedMiddleware = createNestedMiddleware((params, next) => {
- if (params.action === "update" && params.model === "Post") {
- return next({
- ...params,
- action: "deleteMany",
- args: {
- where: { id: params.args.where.id },
- },
- });
- }
-
- return next(params);
- });
-
- const next = jest.fn((_: any) => Promise.resolve(null));
- const params = createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- update: {
- where: { id: faker.datatype.number() },
- data: {
- title: faker.lorem.sentence(),
- },
- },
- },
- },
- });
-
- await nestedMiddleware(params, next);
-
- expect(next).toHaveBeenCalledWith(
- set(params, "args.data.posts", {
- deleteMany: {
- where: params.args.data.posts.update.where,
- },
- })
- );
- });
- });
-
- describe("upsert", () => {
- it("allows middleware to modify nested upsert action to be a create", async () => {
- const nestedMiddleware = createNestedMiddleware((params, next) => {
- if (params.action === "upsert") {
- return next({
- ...params,
- action: "create",
- args: {
- data: params.args.create,
- },
- });
- }
-
- return next(params);
- });
-
- const next = jest.fn((_: any) => Promise.resolve(null));
- const params = createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- upsert: {
- where: { id: faker.datatype.number() },
- create: {
- title: faker.lorem.sentence(),
- },
- update: {
- title: faker.lorem.sentence(),
- },
- },
- },
- },
- });
-
- await nestedMiddleware(params, next);
-
- expect(next).toHaveBeenCalledWith(
- set(params, "args.data.posts", {
- create: params.args.data.posts.upsert.create,
- })
- );
- });
-
- it("allows middleware to modify nested upsert action array to be a create array", async () => {
- const nestedMiddleware = createNestedMiddleware((params, next) => {
- if (params.action === "upsert") {
- return next({
- ...params,
- action: "create",
- args: { data: params.args.create },
- });
- }
-
- return next(params);
- });
-
- const next = jest.fn((_: any) => Promise.resolve(null));
- const params = createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- upsert: [
- {
- where: { id: faker.datatype.number() },
- create: { title: faker.lorem.sentence() },
- update: { title: faker.lorem.sentence() },
- },
- {
- where: { id: faker.datatype.number() },
- create: { title: faker.lorem.sentence() },
- update: { title: faker.lorem.sentence() },
- },
- ],
- },
- },
- });
-
- await nestedMiddleware(params, next);
-
- expect(next).toHaveBeenCalledWith(
- set(params, "args.data.posts", {
- create: params.args.data.posts.upsert.map((u: any) => u.create),
- })
- );
- });
- });
-
- describe("connectOrCreate", () => {});
-
- describe("createMany", () => {
- // This is not possible with the current API, we would need to destinguish
- // between a root createMany and a nested createMany and then allow the user
- // to pass params with a list of operations as args or allow passing an array
- // of params to next
- it.failing(
- "allows middleware to modify nested createMany action to be a create list",
- async () => {
- const nestedMiddleware = createNestedMiddleware((params, next) => {
- if (params.action === "createMany") {
- // this is only a placeholder for what the user might be able to pass
- return next({
- ...params,
- action: "create",
- args: params.args.data,
- });
- }
-
- return next(params);
- });
-
- const next = jest.fn((_: any) => Promise.resolve(null));
- const params = createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- createMany: {
- data: [
- { title: faker.lorem.sentence() },
- { title: faker.lorem.sentence() },
- ],
- },
- },
- },
- });
-
- await nestedMiddleware(params, next);
-
- expect(next).toHaveBeenCalledWith(
- set(params, "args.data.posts", {
- create: params.args.data.posts.createMany.data,
- })
- );
- }
- );
- });
-
- describe("updateMany", () => {
- });
-
- describe("delete", () => {
- it("allows middleware to modify nested delete action to be an update", async () => {
- const nestedMiddleware = createNestedMiddleware((params, next) => {
- if (params.action === "delete") {
- return next({
- ...params,
- action: "update",
- args: {
- where: { id: params.args.id },
- data: { deleted: true },
- },
- });
- }
-
- return next(params);
- });
-
- const next = jest.fn((_: any) => Promise.resolve(null));
- const params = createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- delete: { id: faker.datatype.number() },
- },
- },
- });
- await nestedMiddleware(params, next);
-
- expect(next).toHaveBeenCalledWith(
- set(params, "args.data.posts", {
- update: {
- where: { id: params.args.data.posts.delete.id },
- data: { deleted: true },
- },
- })
- );
- });
-
- it("allows middleware to modify nested delete action list to be an update list", async () => {
- const nestedMiddleware = createNestedMiddleware((params, next) => {
- if (params.action === "delete") {
- return next({
- ...params,
- action: "update",
- args: {
- where: { id: params.args.id },
- data: { deleted: true },
- },
- });
- }
-
- return next(params);
- });
-
- const next = jest.fn((_: any) => Promise.resolve(null));
- const params = createParams("User", "update", {
- where: {
- id: faker.datatype.number(),
- },
- data: {
- email: faker.internet.email(),
- posts: {
- delete: [
- { id: faker.datatype.number() },
- { id: faker.datatype.number() },
- ],
- },
- },
- });
- await nestedMiddleware(params, next);
-
- expect(next).toHaveBeenCalledWith(
- set(params, "args.data.posts", {
- update: params.args.data.posts.delete.map((del: any) => ({
- where: del,
- data: { deleted: true },
- })),
- })
- );
- });
-
- it("allows middleware to modify nested boolean delete action to be an update", async () => {
- const nestedMiddleware = createNestedMiddleware((params, next) => {
- if (params.action === "delete" && params.args === true) {
- return next({
- ...params,
- action: "update",
- args: {
- ...params.args,
- deleted: true,
- },
- });
- }
-
- return next(params);
- });
-
- const next = jest.fn((_: any) => Promise.resolve(null));
- const params = createParams("User", "update", {
- where: {
- id: faker.datatype.number(),
- },
- data: {
- email: faker.internet.email(),
- profile: {
- delete: true,
- },
- },
- });
- await nestedMiddleware(params, next);
-
- expect(next).toHaveBeenCalledWith(
- set(params, "args.data.profile", { update: { deleted: true } })
- );
- });
-
- it("allows middleware to modify deeply nested delete action to be an update", async () => {
- const nestedMiddleware = createNestedMiddleware((params, next) => {
- if (params.action === "delete") {
- return next({
- ...params,
- action: "update",
- args: {
- where: { id: params.args.id },
- data: { deleted: true },
- },
- });
- }
-
- return next(params);
- });
-
- const next = jest.fn((_: any) => Promise.resolve(null));
- const params = createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- update: {
- where: { id: faker.datatype.number() },
- data: {
- title: faker.lorem.sentence(),
- comments: {
- delete: { id: faker.datatype.number() },
- },
- },
- },
- },
- },
- });
- await nestedMiddleware(params, next);
-
- expect(next).toHaveBeenCalledWith(
- set(params, "args.data.posts.update.data.comments", {
- update: {
- where: params.args.data.posts.update.data.comments.delete,
- data: { deleted: true },
- },
- })
- );
- });
- });
-
- describe("deleteMany", () => {
- });
-
- describe("include", () => {
- it("allows middleware to modify nested include action to be a select", async () => {
- const nestedMiddleware = createNestedMiddleware((params, next) => {
- if (params.action === "include") {
- return next({
- ...params,
- action: "select",
- });
- }
-
- return next(params);
- });
-
- const next = jest.fn((_: any) => Promise.resolve(null));
- const params = createParams("User", "findUnique", {
- where: { id: faker.datatype.number() },
- include: {
- posts: true,
- },
- });
-
- await nestedMiddleware(params, next);
-
- expect(next).toHaveBeenCalledWith({
- ...params,
- args: {
- where: params.args.where,
- select: {
- posts: true,
- },
- },
- });
- });
- });
-
- describe("select", () => {
- it("allows middleware to modify nested select action to be an include", async () => {
- const nestedMiddleware = createNestedMiddleware((params, next) => {
- if (params.action === "select") {
- return next({
- ...params,
- action: "include",
- });
- }
-
- return next(params);
- });
-
- const next = jest.fn((_: any) => Promise.resolve(null));
- const params = createParams("User", "findUnique", {
- where: { id: faker.datatype.number() },
- include: {
- posts: {
- select: {
- author: true,
- },
- },
- },
- });
- await nestedMiddleware(params, next);
-
- expect(next).toHaveBeenCalledWith(
- set(params, "args.include.posts", { include: { author: true } })
- );
- });
- });
-
- describe("merging", () => {
- it("merges converted write action args with existing write action args", async () => {
- const nestedMiddleware = createNestedMiddleware((params, next) => {
- if (params.action === "delete") {
- return next({
- ...params,
- action: "update",
- args: {
- where: { id: params.args.id },
- data: { deleted: true },
- },
- });
- }
-
- return next(params);
- });
-
- const next = jest.fn((_: any) => Promise.resolve(null));
- const params = createParams("User", "update", {
- where: {
- id: faker.datatype.number(),
- },
- data: {
- email: faker.internet.email(),
- posts: {
- update: {
- where: { id: faker.datatype.number() },
- data: { title: faker.lorem.sentence() },
- },
- delete: {
- id: faker.datatype.number(),
- },
- },
- },
- });
-
- await nestedMiddleware(params, next);
-
- expect(next).toHaveBeenCalledWith(
- set(params, "args.data.posts", {
- update: [
- {
- where: { id: params.args.data.posts.update.where.id },
- data: { title: params.args.data.posts.update.data.title },
- },
- {
- where: { id: params.args.data.posts.delete.id },
- data: { deleted: true },
- },
- ],
- })
- );
- });
-
- it("merges converted write array args with existing write action args", async () => {
- const nestedMiddleware = createNestedMiddleware((params, next) => {
- if (params.action === "delete") {
- return next({
- ...params,
- action: "update",
- args: {
- where: { id: params.args.id },
- data: { deleted: true },
- },
- });
- }
-
- return next(params);
- });
-
- const next = jest.fn((_: any) => Promise.resolve(null));
- const params = createParams("User", "update", {
- where: {
- id: faker.datatype.number(),
- },
- data: {
- email: faker.internet.email(),
- posts: {
- update: {
- where: { id: faker.datatype.number() },
- data: { title: faker.lorem.sentence() },
- },
- delete: [
- { id: faker.datatype.number() },
- { id: faker.datatype.number() },
- ],
- },
- },
- });
-
- await nestedMiddleware(params, next);
-
- expect(next).toHaveBeenCalledWith(
- set(params, "args.data.posts", {
- update: [
- {
- where: { id: params.args.data.posts.update.where.id },
- data: params.args.data.posts.update.data,
- },
- {
- where: { id: params.args.data.posts.delete[0].id },
- data: { deleted: true },
- },
- {
- where: { id: params.args.data.posts.delete[1].id },
- data: { deleted: true },
- },
- ],
- })
- );
- });
-
- it("merges converted write args with existing write action array args", async () => {
- const nestedMiddleware = createNestedMiddleware((params, next) => {
- if (params.action === "delete") {
- return next({
- ...params,
- action: "update",
- args: {
- where: { id: params.args.id },
- data: { deleted: true },
- },
- });
- }
-
- return next(params);
- });
-
- const next = jest.fn((_: any) => Promise.resolve(null));
- const params = createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- update: [
- {
- where: { id: faker.datatype.number() },
- data: { title: faker.lorem.sentence() },
- },
- ],
- delete: { id: faker.datatype.number() },
- },
- },
- });
-
- await nestedMiddleware(params, next);
-
- expect(next).toHaveBeenCalledWith(
- set(params, "args.data.posts", {
- update: [
- {
- where: { id: params.args.data.posts.update[0].where.id },
- data: params.args.data.posts.update[0].data,
- },
- {
- where: { id: params.args.data.posts.delete.id },
- data: { deleted: true },
- },
- ],
- })
- );
- });
-
- it("merges converted write array args with existing write action array args", async () => {
- const nestedMiddleware = createNestedMiddleware((params, next) => {
- if (params.action === "delete") {
- return next({
- ...params,
- action: "update",
- args: {
- where: { id: params.args.id },
- data: { deleted: true },
- },
- });
- }
-
- return next(params);
- });
-
- const next = jest.fn((_: any) => Promise.resolve(null));
- const params = createParams("User", "update", {
- where: {
- id: faker.datatype.number(),
- },
- data: {
- email: faker.internet.email(),
- posts: {
- update: [
- {
- where: { id: faker.datatype.number() },
- data: { title: faker.lorem.sentence() },
- },
- ],
- delete: [
- { id: faker.datatype.number() },
- { id: faker.datatype.number() },
- ],
- },
- },
- });
-
- await nestedMiddleware(params, next);
-
- expect(next).toHaveBeenCalledWith(
- set(params, "args.data.posts", {
- update: [
- {
- where: { id: params.args.data.posts.update[0].where.id },
- data: params.args.data.posts.update[0].data,
- },
- {
- where: { id: params.args.data.posts.delete[0].id },
- data: { deleted: true },
- },
- {
- where: { id: params.args.data.posts.delete[1].id },
- data: { deleted: true },
- },
- ],
- })
- );
- });
-
- it("merges converted write args with existing write action args when nested in action array", async () => {
- const nestedMiddleware = createNestedMiddleware((params, next) => {
- if (params.action === "delete") {
- return next({
- ...params,
- action: "update",
- args: {
- where: { id: params.args.id },
- data: { deleted: true },
- },
- });
- }
-
- return next(params);
- });
-
- const next = jest.fn((_: any) => Promise.resolve(null));
- const params = createParams("User", "update", {
- where: {
- id: faker.datatype.number(),
- },
- data: {
- email: faker.internet.email(),
- posts: {
- update: [
- {
- where: { id: faker.datatype.number() },
- data: {
- title: faker.lorem.sentence(),
- comments: {
- update: {
- where: { id: faker.datatype.number() },
- data: { content: "test comment content" },
- },
- delete: { id: faker.datatype.number() },
- },
- },
- },
- ],
- },
- },
- });
-
- await nestedMiddleware(params, next);
-
- expect(next).toHaveBeenCalledWith(
- set(params, "args.data.posts.update.0.data.comments", {
- update: [
- params.args.data.posts.update[0].data.comments.update,
- {
- where: params.args.data.posts.update[0].data.comments.delete,
- data: { deleted: true },
- },
- ],
- })
- );
- });
-
- it("merges operation converted to createMany with existing createMany", async () => {
- const nestedMiddleware = createNestedMiddleware((params, next) => {
- if (params.action === "upsert") {
- return next({
- ...params,
- action: "createMany",
- args: {
- data: [
- {
- title: params.args.create.title,
- number: params.args.create.title.includes("first") ? 1 : 2,
- },
- ],
- },
- });
- }
-
- return next(params);
- });
-
- const next = jest.fn((_: any) => Promise.resolve(null));
- const params = createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- createMany: {
- data: [{ title: "pre-existing" }],
- },
- upsert: [
- {
- where: { id: faker.datatype.number() },
- create: { title: "first-upsert" },
- update: { title: "first-upsert" },
- },
- {
- where: { id: faker.datatype.number() },
- create: { title: "second-upsert" },
- update: { title: "second-upsert" },
- },
- ],
- },
- },
- });
- await nestedMiddleware(params, next);
-
- // spread data here as a fix to: https://github.com/facebook/jest/issues/8475
- expect(next).toHaveBeenCalledTimes(1);
- expect([
- ...next.mock.calls[0][0].args.data.posts.createMany.data,
- ]).toEqual([
- { title: "pre-existing" },
- { title: "first-upsert", number: 1 },
- { title: "second-upsert", number: 2 },
- ]);
- });
-
- it("does nothing when middleware changes an select to be a include but an include already exists", async () => {
- const nestedMiddleware = createNestedMiddleware((params, next) => {
- if (params.action === "select") {
- return next({
- ...params,
- action: "include",
- });
- }
-
- return next(params);
- });
-
- const next = jest.fn((_: any) => Promise.resolve(null));
- const params = createParams("User", "findUnique", {
- where: { id: faker.datatype.number() },
- include: {
- posts: {
- select: { deleted: true },
- include: { author: true },
- },
- },
- });
-
- await nestedMiddleware(params, next);
-
- expect(next).toHaveBeenCalledWith(params);
- });
- });
-});
diff --git a/test/calls.test.ts b/test/calls.test.ts
deleted file mode 100644
index 5b55796..0000000
--- a/test/calls.test.ts
+++ /dev/null
@@ -1,2290 +0,0 @@
-import { Prisma } from "@prisma/client";
-import faker from "faker";
-import { get } from "lodash";
-
-import { createNestedMiddleware, NestedParams } from "../src";
-import { relationsByModel } from "../src/lib/createNestedMiddleware";
-import { createParams } from "./utils/createParams";
-
-type MiddlewareCall = {
- model: Model;
- action:
- | "create"
- | "update"
- | "upsert"
- | "delete"
- | "createMany"
- | "updateMany"
- | "deleteMany"
- | "connectOrCreate"
- | "findUnique"
- | "findFirst"
- | "findMany"
- | "include"
- | "select";
- argsPath: string;
- scope?: MiddlewareCall;
- relation?: Prisma.DMMF.Field;
-};
-
-function nestedParamsFromCall(
- rootParams: Prisma.MiddlewareParams,
- call: MiddlewareCall
-): NestedParams {
- const args = get(rootParams, call.argsPath);
- const params = createParams(
- call.model,
- call.action,
- call.action === "create" ? { data: args } : args
- );
- return {
- ...params,
- relation: call.relation,
- scope: call.scope
- ? nestedParamsFromCall(rootParams, call.scope)
- : rootParams,
- };
-}
-
-function getModelRelation(
- model: Model,
- relationName: string
-): Prisma.DMMF.Field | undefined {
- return relationsByModel[model].find(
- (relation) => relation.name === relationName
- );
-}
-
-describe("calls", () => {
- it("calls middleware once when there are no nested operations", async () => {
- const middleware = jest.fn((params, next) => next(params));
- const nestedMiddleware = createNestedMiddleware(middleware);
-
- const next = jest.fn((params: any) => params);
- const params = createParams("User", "create", {
- data: { email: faker.internet.email() },
- });
- await nestedMiddleware(params, next);
-
- // middleware is called with params and next
- expect(middleware).toHaveBeenCalledTimes(1);
- expect(middleware).toHaveBeenCalledWith(params, next);
- });
-
- it.each<{
- description: string;
- rootParams: Prisma.MiddlewareParams;
- calls: MiddlewareCall[];
- }>([
- {
- description: "nested create in create",
- rootParams: createParams("User", "create", {
- data: {
- email: faker.internet.email(),
- profile: { create: { bio: faker.lorem.paragraph() } },
- },
- }),
- calls: [
- {
- action: "create",
- model: "Profile",
- argsPath: "args.data.profile.create",
- relation: getModelRelation("User", "profile"),
- },
- ],
- },
- {
- description: "nested create in update",
- rootParams: createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- profile: { create: { bio: faker.lorem.paragraph() } },
- },
- }),
- calls: [
- {
- action: "create",
- model: "Profile",
- argsPath: "args.data.profile.create",
- relation: getModelRelation("User", "profile"),
- },
- ],
- },
- {
- description: "nested creates in upsert",
- rootParams: createParams("User", "upsert", {
- where: { id: faker.datatype.number() },
- create: {
- email: faker.internet.email(),
- profile: { create: { bio: faker.lorem.paragraph() } },
- },
- update: {
- email: faker.internet.email(),
- profile: { create: { bio: faker.lorem.paragraph() } },
- },
- }),
- calls: [
- {
- action: "create",
- model: "Profile",
- argsPath: "args.create.profile.create",
- relation: getModelRelation("User", "profile"),
- },
- {
- action: "create",
- model: "Profile",
- argsPath: "args.update.profile.create",
- relation: getModelRelation("User", "profile"),
- },
- ],
- },
- {
- description: "nested create array in create",
- rootParams: createParams("User", "create", {
- data: {
- email: faker.internet.email(),
- posts: {
- create: [
- {
- title: faker.lorem.sentence(),
- content: faker.lorem.paragraph(),
- },
- {
- title: faker.lorem.sentence(),
- content: faker.lorem.paragraph(),
- },
- ],
- },
- },
- }),
- calls: [
- {
- action: "create",
- model: "Post",
- argsPath: "args.data.posts.create.0",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "create",
- model: "Post",
- argsPath: "args.data.posts.create.1",
- relation: getModelRelation("User", "posts"),
- },
- ],
- },
- {
- description: "nested create array in update",
- rootParams: createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- create: [
- {
- title: faker.lorem.sentence(),
- content: faker.lorem.paragraph(),
- },
- {
- title: faker.lorem.sentence(),
- content: faker.lorem.paragraph(),
- },
- ],
- },
- },
- }),
- calls: [
- {
- action: "create",
- model: "Post",
- argsPath: "args.data.posts.create.0",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "create",
- model: "Post",
- argsPath: "args.data.posts.create.1",
- relation: getModelRelation("User", "posts"),
- },
- ],
- },
- {
- description: "nested create and update in update",
- rootParams: createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- profile: {
- update: { bio: faker.lorem.paragraph() },
- },
- posts: {
- create: [
- {
- title: faker.lorem.sentence(),
- content: faker.lorem.paragraph(),
- },
- ],
- },
- },
- }),
- calls: [
- {
- action: "update",
- model: "Profile",
- argsPath: "args.data.profile.update",
- relation: getModelRelation("User", "profile"),
- },
- {
- action: "create",
- model: "Post",
- argsPath: "args.data.posts.create.0",
- relation: getModelRelation("User", "posts"),
- },
- ],
- },
- {
- description: "nested create array in upsert",
- rootParams: createParams("User", "upsert", {
- where: { id: faker.datatype.number() },
- create: {
- email: faker.internet.email(),
- posts: {
- create: [
- {
- title: faker.lorem.sentence(),
- content: faker.lorem.paragraph(),
- },
- {
- title: faker.lorem.sentence(),
- content: faker.lorem.paragraph(),
- },
- ],
- },
- },
- update: {
- email: faker.internet.email(),
- posts: {
- create: [
- {
- title: faker.lorem.sentence(),
- content: faker.lorem.paragraph(),
- },
- {
- title: faker.lorem.sentence(),
- content: faker.lorem.paragraph(),
- },
- ],
- },
- },
- }),
- calls: [
- {
- action: "create",
- model: "Post",
- argsPath: "args.create.posts.create.0",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "create",
- model: "Post",
- argsPath: "args.create.posts.create.1",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "create",
- model: "Post",
- argsPath: "args.update.posts.create.0",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "create",
- model: "Post",
- argsPath: "args.update.posts.create.1",
- relation: getModelRelation("User", "posts"),
- },
- ],
- },
- {
- description: "nested update in update",
- rootParams: createParams("User", "update", {
- where: { id: faker.datatype.number() },
-
- data: {
- email: faker.internet.email(),
- profile: { update: { bio: faker.lorem.paragraph() } },
- },
- }),
- calls: [
- {
- action: "update",
- model: "Profile",
- argsPath: "args.data.profile.update",
- relation: getModelRelation("User", "profile"),
- },
- ],
- },
- {
- description: "nested update in upsert",
- rootParams: createParams("User", "upsert", {
- where: { id: faker.datatype.number() },
- create: {
- email: faker.internet.email(),
- },
- update: {
- email: faker.internet.email(),
- profile: { update: { bio: faker.lorem.paragraph() } },
- },
- }),
- calls: [
- {
- action: "update",
- model: "Profile",
- argsPath: "args.update.profile.update",
- relation: getModelRelation("User", "profile"),
- },
- ],
- },
- {
- description: "nested update array in update",
- rootParams: createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- update: [
- {
- where: { id: faker.datatype.number() },
- data: {
- title: faker.lorem.sentence(),
- content: faker.lorem.paragraph(),
- },
- },
- {
- where: { id: faker.datatype.number() },
- data: {
- title: faker.lorem.sentence(),
- content: faker.lorem.paragraph(),
- },
- },
- ],
- },
- },
- }),
- calls: [
- {
- action: "update",
- model: "Post",
- argsPath: "args.data.posts.update.0",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "update",
- model: "Post",
- argsPath: "args.data.posts.update.1",
- relation: getModelRelation("User", "posts"),
- },
- ],
- },
- {
- description: "nested update array in upsert",
- rootParams: createParams("User", "upsert", {
- where: { id: faker.datatype.number() },
- create: {
- email: faker.internet.email(),
- },
- update: {
- email: faker.internet.email(),
- posts: {
- update: [
- {
- where: { id: faker.datatype.number() },
- data: {
- title: faker.lorem.sentence(),
- content: faker.lorem.paragraph(),
- },
- },
- {
- where: { id: faker.datatype.number() },
- data: {
- title: faker.lorem.sentence(),
- content: faker.lorem.paragraph(),
- },
- },
- ],
- },
- },
- }),
- calls: [
- {
- action: "update",
- model: "Post",
- argsPath: "args.update.posts.update.0",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "update",
- model: "Post",
- argsPath: "args.update.posts.update.1",
- relation: getModelRelation("User", "posts"),
- },
- ],
- },
- {
- description: "nested upsert in update",
- rootParams: createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- profile: {
- upsert: {
- create: { bio: faker.lorem.paragraph() },
- update: { bio: faker.lorem.paragraph() },
- },
- },
- },
- }),
- calls: [
- {
- action: "upsert",
- model: "Profile",
- argsPath: "args.data.profile.upsert",
- relation: getModelRelation("User", "profile"),
- },
- ],
- },
- {
- description: "nested upsert list in update",
- rootParams: createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- upsert: [
- {
- where: { id: faker.datatype.number() },
- update: { title: faker.lorem.sentence() },
- create: {
- title: faker.lorem.sentence(),
- content: faker.lorem.paragraph(),
- },
- },
- {
- where: { id: faker.datatype.number() },
- update: { title: faker.lorem.sentence() },
- create: {
- title: faker.lorem.sentence(),
- content: faker.lorem.paragraph(),
- },
- },
- ],
- },
- },
- }),
- calls: [
- {
- action: "upsert",
- model: "Post",
- argsPath: "args.data.posts.upsert.0",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "upsert",
- model: "Post",
- argsPath: "args.data.posts.upsert.1",
- relation: getModelRelation("User", "posts"),
- },
- ],
- },
- {
- description: "nested upsert in upsert",
- rootParams: createParams("User", "upsert", {
- where: { id: faker.datatype.number() },
- create: {
- email: faker.internet.email(),
- },
- update: {
- email: faker.internet.email(),
- profile: {
- upsert: {
- create: { bio: faker.lorem.paragraph() },
- update: { bio: faker.lorem.paragraph() },
- },
- },
- },
- }),
- calls: [
- {
- action: "upsert",
- model: "Profile",
- argsPath: "args.update.profile.upsert",
- relation: getModelRelation("User", "profile"),
- },
- ],
- },
- {
- description: "nested delete in update",
- rootParams: createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- profile: { delete: true },
- },
- }),
- calls: [
- {
- action: "delete",
- model: "Profile",
- argsPath: "args.data.profile.delete",
- relation: getModelRelation("User", "profile"),
- },
- ],
- },
- {
- description: "nested delete in upsert",
- rootParams: createParams("User", "upsert", {
- where: { id: faker.datatype.number() },
- create: {
- email: faker.internet.email(),
- },
- update: {
- email: faker.internet.email(),
- profile: { delete: true },
- },
- }),
- calls: [
- {
- action: "delete",
- model: "Profile",
- argsPath: "args.update.profile.delete",
- relation: getModelRelation("User", "profile"),
- },
- ],
- },
- {
- description: "nested delete array in update",
- rootParams: createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- delete: [
- { id: faker.datatype.number() },
- { id: faker.datatype.number() },
- ],
- },
- },
- }),
- calls: [
- {
- action: "delete",
- model: "Post",
- argsPath: "args.data.posts.delete.0",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "delete",
- model: "Post",
- argsPath: "args.data.posts.delete.1",
- relation: getModelRelation("User", "posts"),
- },
- ],
- },
- {
- description: "nested delete array in upsert",
- rootParams: createParams("User", "upsert", {
- where: { id: faker.datatype.number() },
- create: {
- email: faker.internet.email(),
- },
- update: {
- email: faker.internet.email(),
- posts: {
- delete: [
- { id: faker.datatype.number() },
- { id: faker.datatype.number() },
- ],
- },
- },
- }),
- calls: [
- {
- action: "delete",
- model: "Post",
- argsPath: "args.update.posts.delete.0",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "delete",
- model: "Post",
- argsPath: "args.update.posts.delete.1",
- relation: getModelRelation("User", "posts"),
- },
- ],
- },
- {
- description: "nested createMany in create",
- rootParams: createParams("User", "create", {
- data: {
- email: faker.internet.email(),
- posts: {
- createMany: {
- data: [
- {
- title: faker.lorem.sentence(),
- content: faker.lorem.paragraph(),
- },
- {
- title: faker.lorem.sentence(),
- content: faker.lorem.paragraph(),
- },
- ],
- },
- },
- },
- }),
- calls: [
- {
- action: "createMany",
- model: "Post",
- argsPath: "args.data.posts.createMany",
- relation: getModelRelation("User", "posts"),
- },
- ],
- },
- {
- description: "nested createMany in update",
- rootParams: createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- createMany: {
- data: [
- {
- title: faker.lorem.sentence(),
- content: faker.lorem.paragraph(),
- },
- {
- title: faker.lorem.sentence(),
- content: faker.lorem.paragraph(),
- },
- ],
- },
- },
- },
- }),
- calls: [
- {
- action: "createMany",
- model: "Post",
- argsPath: "args.data.posts.createMany",
- relation: getModelRelation("User", "posts"),
- },
- ],
- },
- {
- description: "nested createMany in upsert",
- rootParams: createParams("User", "upsert", {
- where: { id: faker.datatype.number() },
- create: {
- email: faker.internet.email(),
- },
- update: {
- email: faker.internet.email(),
- posts: {
- createMany: {
- data: [
- {
- title: faker.lorem.sentence(),
- content: faker.lorem.paragraph(),
- },
- {
- title: faker.lorem.sentence(),
- content: faker.lorem.paragraph(),
- },
- ],
- },
- },
- },
- }),
- calls: [
- {
- action: "createMany",
- model: "Post",
- argsPath: "args.update.posts.createMany",
- relation: getModelRelation("User", "posts"),
- },
- ],
- },
- {
- description: "nested updateMany in update",
- rootParams: createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- updateMany: {
- data: {
- title: faker.lorem.sentence(),
- content: faker.lorem.paragraph(),
- },
- where: { id: faker.datatype.number() },
- },
- },
- },
- }),
- calls: [
- {
- action: "updateMany",
- model: "Post",
- argsPath: "args.data.posts.updateMany",
- relation: getModelRelation("User", "posts"),
- },
- ],
- },
-
- {
- description: "nested updateMany array in update",
- rootParams: createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- updateMany: [
- {
- where: {
- id: faker.datatype.number(),
- },
- data: {
- title: faker.lorem.sentence(),
- content: faker.lorem.paragraph(),
- },
- },
- {
- where: {
- id: faker.datatype.number(),
- },
- data: {
- title: faker.lorem.sentence(),
- content: faker.lorem.paragraph(),
- },
- },
- ],
- },
- },
- }),
- calls: [
- {
- action: "updateMany",
- model: "Post",
- argsPath: "args.data.posts.updateMany.0",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "updateMany",
- model: "Post",
- argsPath: "args.data.posts.updateMany.1",
- relation: getModelRelation("User", "posts"),
- },
- ],
- },
- {
- description: "nested updateMany in upsert",
- rootParams: createParams("User", "upsert", {
- where: { id: faker.datatype.number() },
- create: {
- email: faker.internet.email(),
- },
- update: {
- email: faker.internet.email(),
- posts: {
- updateMany: {
- data: {
- title: faker.lorem.sentence(),
- content: faker.lorem.paragraph(),
- },
- where: { id: faker.datatype.number() },
- },
- },
- },
- }),
- calls: [
- {
- action: "updateMany",
- model: "Post",
- argsPath: "args.update.posts.updateMany",
- relation: getModelRelation("User", "posts"),
- },
- ],
- },
- {
- description: "nested updateMany list in upsert",
- rootParams: createParams("User", "upsert", {
- where: { id: faker.datatype.number() },
- create: {
- email: faker.internet.email(),
- },
- update: {
- email: faker.internet.email(),
- posts: {
- updateMany: [
- {
- where: { id: faker.datatype.number() },
- data: { title: faker.lorem.sentence() },
- },
- {
- where: { id: faker.datatype.number() },
- data: { title: faker.lorem.sentence() },
- },
- ],
- },
- },
- }),
- calls: [
- {
- action: "updateMany",
- model: "Post",
- argsPath: "args.update.posts.updateMany.0",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "updateMany",
- model: "Post",
- argsPath: "args.update.posts.updateMany.1",
- relation: getModelRelation("User", "posts"),
- },
- ],
- },
- {
- description: "nested deleteMany in update",
- rootParams: createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- deleteMany: { id: faker.datatype.number() },
- },
- },
- }),
- calls: [
- {
- action: "deleteMany",
- model: "Post",
- argsPath: "args.data.posts.deleteMany",
- relation: getModelRelation("User", "posts"),
- },
- ],
- },
- {
- description: "nested deleteMany list in update",
- rootParams: createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- deleteMany: [
- { id: faker.datatype.number() },
- { id: faker.datatype.number() },
- ],
- },
- },
- }),
- calls: [
- {
- action: "deleteMany",
- model: "Post",
- argsPath: "args.data.posts.deleteMany.0",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "deleteMany",
- model: "Post",
- argsPath: "args.data.posts.deleteMany.1",
- relation: getModelRelation("User", "posts"),
- },
- ],
- },
- {
- description: "nested deleteMany in upsert",
- rootParams: createParams("User", "upsert", {
- where: { id: faker.datatype.number() },
- create: {
- email: faker.internet.email(),
- },
- update: {
- email: faker.internet.email(),
- posts: {
- deleteMany: { id: faker.datatype.number() },
- },
- },
- }),
- calls: [
- {
- action: "deleteMany",
- model: "Post",
- argsPath: "args.update.posts.deleteMany",
- relation: getModelRelation("User", "posts"),
- },
- ],
- },
- {
- description: "nested deleteMany list in upsert",
- rootParams: createParams("User", "upsert", {
- where: { id: faker.datatype.number() },
- create: {
- email: faker.internet.email(),
- },
- update: {
- email: faker.internet.email(),
- posts: {
- deleteMany: [
- { id: faker.datatype.number() },
- { id: faker.datatype.number() },
- ],
- },
- },
- }),
- calls: [
- {
- action: "deleteMany",
- model: "Post",
- argsPath: "args.update.posts.deleteMany.0",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "deleteMany",
- model: "Post",
- argsPath: "args.update.posts.deleteMany.1",
- relation: getModelRelation("User", "posts"),
- },
- ],
- },
- {
- description: "nested connectOrCreate in update",
- rootParams: createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- profile: {
- connectOrCreate: {
- where: { id: faker.datatype.number() },
- create: { bio: faker.lorem.paragraph() },
- },
- },
- },
- }),
- calls: [
- {
- action: "connectOrCreate",
- model: "Profile",
- argsPath: "args.data.profile.connectOrCreate",
- relation: getModelRelation("User", "profile"),
- },
- ],
- },
- {
- description: "nested connectOrCreate in upsert",
- rootParams: createParams("User", "upsert", {
- where: { id: faker.datatype.number() },
- create: {
- email: faker.internet.email(),
- },
- update: {
- email: faker.internet.email(),
- profile: {
- connectOrCreate: {
- where: { id: faker.datatype.number() },
- create: { bio: faker.lorem.paragraph() },
- },
- },
- },
- }),
- calls: [
- {
- action: "connectOrCreate",
- model: "Profile",
- argsPath: "args.update.profile.connectOrCreate",
- relation: getModelRelation("User", "profile"),
- },
- ],
- },
- {
- description: "deeply nested creates",
- rootParams: createParams("User", "create", {
- data: {
- email: faker.internet.email(),
- posts: {
- create: {
- title: faker.lorem.sentence(),
- content: faker.lorem.paragraph(),
- comments: {
- create: {
- authorId: faker.datatype.number(),
- content: faker.lorem.paragraph(),
- },
- },
- },
- },
- },
- }),
- calls: [
- {
- action: "create",
- model: "Post",
- argsPath: "args.data.posts.create",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "create",
- model: "Comment",
- argsPath: "args.data.posts.create.comments.create",
- relation: getModelRelation("Post", "comments"),
- scope: {
- action: "create",
- model: "Post",
- argsPath: "args.data.posts.create",
- relation: getModelRelation("User", "posts"),
- },
- },
- ],
- },
- {
- description: "deeply nested update",
- rootParams: createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- update: {
- where: { id: faker.datatype.number() },
- data: {
- title: faker.lorem.sentence(),
- content: faker.lorem.paragraph(),
- comments: {
- update: {
- where: { id: faker.datatype.number() },
- data: {
- authorId: faker.datatype.number(),
- content: faker.lorem.paragraph(),
- },
- },
- },
- },
- },
- },
- },
- }),
- calls: [
- {
- action: "update",
- model: "Post",
- argsPath: "args.data.posts.update",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "update",
- model: "Comment",
- argsPath: "args.data.posts.update.data.comments.update",
- relation: getModelRelation("Post", "comments"),
- scope: {
- action: "update",
- model: "Post",
- argsPath: "args.data.posts.update",
- relation: getModelRelation("User", "posts"),
- },
- },
- ],
- },
- {
- description: "deeply nested delete",
- rootParams: createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- update: {
- where: { id: faker.datatype.number() },
- data: {
- title: faker.lorem.sentence(),
- content: faker.lorem.paragraph(),
- comments: {
- delete: [
- { id: faker.datatype.number() },
- { id: faker.datatype.number() },
- ],
- },
- },
- },
- },
- },
- }),
- calls: [
- {
- action: "update",
- model: "Post",
- argsPath: "args.data.posts.update",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "delete",
- model: "Comment",
- argsPath: "args.data.posts.update.data.comments.delete.0",
- relation: getModelRelation("Post", "comments"),
- scope: {
- action: "update",
- model: "Post",
- argsPath: "args.data.posts.update",
- relation: getModelRelation("User", "posts"),
- },
- },
- {
- action: "delete",
- model: "Comment",
- argsPath: "args.data.posts.update.data.comments.delete.1",
- relation: getModelRelation("Post", "comments"),
- scope: {
- action: "update",
- model: "Post",
- argsPath: "args.data.posts.update",
- relation: getModelRelation("User", "posts"),
- },
- },
- ],
- },
- {
- description: "deeply nested upsert",
- rootParams: createParams("User", "upsert", {
- where: { id: faker.datatype.number() },
- create: {
- email: faker.internet.email(),
- },
- update: {
- email: faker.internet.email(),
- posts: {
- update: {
- where: { id: faker.datatype.number() },
- data: {
- title: faker.lorem.sentence(),
- content: faker.lorem.paragraph(),
- comments: {
- upsert: {
- where: { id: faker.datatype.number() },
- create: {
- authorId: faker.datatype.number(),
- content: faker.lorem.paragraph(),
- },
- update: {
- authorId: faker.datatype.number(),
- content: faker.lorem.paragraph(),
- },
- },
- },
- },
- },
- },
- },
- }),
- calls: [
- {
- action: "update",
- model: "Post",
- argsPath: "args.update.posts.update",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "upsert",
- model: "Comment",
- argsPath: "args.update.posts.update.data.comments.upsert",
- relation: getModelRelation("Post", "comments"),
- scope: {
- action: "update",
- model: "Post",
- argsPath: "args.update.posts.update",
- relation: getModelRelation("User", "posts"),
- },
- },
- ],
- },
- {
- description: "deeply nested createMany",
- rootParams: createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- update: {
- where: { id: faker.datatype.number() },
- data: {
- title: faker.lorem.sentence(),
- content: faker.lorem.paragraph(),
- comments: {
- createMany: {
- data: [
- {
- authorId: faker.datatype.number(),
- content: faker.lorem.paragraph(),
- },
- {
- authorId: faker.datatype.number(),
- content: faker.lorem.paragraph(),
- },
- ],
- },
- },
- },
- },
- },
- },
- }),
- calls: [
- {
- action: "update",
- model: "Post",
- argsPath: "args.data.posts.update",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "createMany",
- model: "Comment",
- argsPath: "args.data.posts.update.data.comments.createMany",
- relation: getModelRelation("Post", "comments"),
- scope: {
- action: "update",
- model: "Post",
- argsPath: "args.data.posts.update",
- relation: getModelRelation("User", "posts"),
- },
- },
- ],
- },
- {
- description: "deeply nested updateMany",
- rootParams: createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- update: {
- where: { id: faker.datatype.number() },
- data: {
- title: faker.lorem.sentence(),
- content: faker.lorem.paragraph(),
- comments: {
- updateMany: {
- where: { id: faker.datatype.number() },
- data: {
- content: faker.lorem.paragraph(),
- },
- },
- },
- },
- },
- },
- },
- }),
- calls: [
- {
- action: "update",
- model: "Post",
- argsPath: "args.data.posts.update",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "updateMany",
- model: "Comment",
- argsPath: "args.data.posts.update.data.comments.updateMany",
- relation: getModelRelation("Post", "comments"),
- scope: {
- action: "update",
- model: "Post",
- argsPath: "args.data.posts.update",
- relation: getModelRelation("User", "posts"),
- },
- },
- ],
- },
- {
- description: "deeply nested updateMany array",
- rootParams: createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- update: {
- where: { id: faker.datatype.number() },
- data: {
- title: faker.lorem.sentence(),
- content: faker.lorem.paragraph(),
- comments: {
- updateMany: [
- {
- where: { id: faker.datatype.number() },
- data: { content: faker.lorem.paragraph() },
- },
- {
- where: { id: faker.datatype.number() },
- data: { content: faker.lorem.paragraph() },
- },
- ],
- },
- },
- },
- },
- },
- }),
- calls: [
- {
- action: "update",
- model: "Post",
- argsPath: "args.data.posts.update",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "updateMany",
- model: "Comment",
- argsPath: "args.data.posts.update.data.comments.updateMany.0",
- relation: getModelRelation("Post", "comments"),
- scope: {
- action: "update",
- model: "Post",
- argsPath: "args.data.posts.update",
- relation: getModelRelation("User", "posts"),
- },
- },
- {
- action: "updateMany",
- model: "Comment",
- argsPath: "args.data.posts.update.data.comments.updateMany.1",
- relation: getModelRelation("Post", "comments"),
- scope: {
- action: "update",
- model: "Post",
- argsPath: "args.data.posts.update",
- relation: getModelRelation("User", "posts"),
- },
- },
- ],
- },
- {
- description: "deeply nested deleteMany",
- rootParams: createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- update: {
- where: { id: faker.datatype.number() },
- data: {
- title: faker.lorem.sentence(),
- content: faker.lorem.paragraph(),
- comments: {
- deleteMany: { id: faker.datatype.number() },
- },
- },
- },
- },
- },
- }),
- calls: [
- {
- action: "update",
- model: "Post",
- argsPath: "args.data.posts.update",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "deleteMany",
- model: "Comment",
- argsPath: "args.data.posts.update.data.comments.deleteMany",
- relation: getModelRelation("Post", "comments"),
- scope: {
- action: "update",
- model: "Post",
- argsPath: "args.data.posts.update",
- relation: getModelRelation("User", "posts"),
- },
- },
- ],
- },
- {
- description: "deeply nested deleteMany array",
- rootParams: createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- update: {
- where: { id: faker.datatype.number() },
- data: {
- title: faker.lorem.sentence(),
- content: faker.lorem.paragraph(),
- comments: {
- deleteMany: [
- { id: faker.datatype.number() },
- { id: faker.datatype.number() },
- ],
- },
- },
- },
- },
- },
- }),
- calls: [
- {
- action: "update",
- model: "Post",
- argsPath: "args.data.posts.update",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "deleteMany",
- model: "Comment",
- argsPath: "args.data.posts.update.data.comments.deleteMany.0",
- relation: getModelRelation("Post", "comments"),
- scope: {
- action: "update",
- model: "Post",
- argsPath: "args.data.posts.update",
- relation: getModelRelation("User", "posts"),
- },
- },
- {
- action: "deleteMany",
- model: "Comment",
- argsPath: "args.data.posts.update.data.comments.deleteMany.1",
- relation: getModelRelation("Post", "comments"),
- scope: {
- action: "update",
- model: "Post",
- argsPath: "args.data.posts.update",
- relation: getModelRelation("User", "posts"),
- },
- },
- ],
- },
- {
- description: "deeply nested connectOrCreate",
- rootParams: createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- posts: {
- update: {
- where: { id: faker.datatype.number() },
- data: {
- title: faker.lorem.sentence(),
- content: faker.lorem.paragraph(),
- comments: {
- connectOrCreate: {
- where: { id: faker.datatype.number() },
- create: {
- authorId: faker.datatype.number(),
- content: faker.lorem.paragraph(),
- },
- },
- },
- },
- },
- },
- },
- }),
- calls: [
- {
- action: "update",
- model: "Post",
- argsPath: "args.data.posts.update",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "connectOrCreate",
- model: "Comment",
- argsPath: "args.data.posts.update.data.comments.connectOrCreate",
- relation: getModelRelation("Post", "comments"),
- scope: {
- action: "update",
- model: "Post",
- argsPath: "args.data.posts.update",
- relation: getModelRelation("User", "posts"),
- },
- },
- ],
- },
- {
- description: "include in findUnique",
- rootParams: createParams("User", "findUnique", {
- where: { id: faker.datatype.number() },
- include: {
- posts: true,
- },
- }),
- calls: [
- {
- action: "include",
- model: "Post",
- argsPath: "args.include.posts",
- relation: getModelRelation("User", "posts"),
- },
- ],
- },
- {
- description: "include in findFirst",
- rootParams: createParams("User", "findFirst", {
- where: { id: faker.datatype.number() },
- include: { posts: true },
- }),
- calls: [
- {
- action: "include",
- model: "Post",
- argsPath: "args.include.posts",
- relation: getModelRelation("User", "posts"),
- },
- ],
- },
- {
- description: "include in findMany",
- rootParams: createParams("User", "findMany", {
- where: { id: faker.datatype.number() },
- include: { posts: true },
- }),
- calls: [
- {
- action: "include",
- model: "Post",
- argsPath: "args.include.posts",
- relation: getModelRelation("User", "posts"),
- },
- ],
- },
- {
- description: "include in create",
- rootParams: createParams("User", "create", {
- data: {
- email: faker.internet.email(),
- posts: { create: { title: faker.lorem.sentence() } },
- },
- include: { posts: true },
- }),
- calls: [
- {
- action: "create",
- model: "Post",
- argsPath: "args.data.posts.create",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "include",
- model: "Post",
- argsPath: "args.include.posts",
- relation: getModelRelation("User", "posts"),
- },
- ],
- },
- {
- description: "include in update",
- rootParams: createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- },
- include: { posts: true },
- }),
- calls: [
- {
- action: "include",
- model: "Post",
- argsPath: "args.include.posts",
- relation: getModelRelation("User", "posts"),
- },
- ],
- },
- {
- description: "include in upsert",
- rootParams: createParams("User", "upsert", {
- where: { id: faker.datatype.number() },
- create: {
- email: faker.internet.email(),
- },
- update: {
- email: faker.internet.email(),
- },
- include: { posts: true },
- }),
- calls: [
- {
- action: "include",
- model: "Post",
- argsPath: "args.include.posts",
- relation: getModelRelation("User", "posts"),
- },
- ],
- },
- {
- description: "nested includes",
- rootParams: createParams("User", "findUnique", {
- where: { id: faker.datatype.number() },
- include: {
- posts: {
- include: {
- comments: true,
- },
- },
- },
- }),
- calls: [
- {
- action: "include",
- model: "Post",
- argsPath: "args.include.posts",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "include",
- model: "Comment",
- argsPath: "args.include.posts.include.comments",
- relation: getModelRelation("Post", "comments"),
- scope: {
- action: "include",
- model: "Post",
- argsPath: "args.include.posts",
- relation: getModelRelation("User", "posts"),
- },
- },
- ],
- },
- {
- description: "deeply nested includes",
- rootParams: createParams("User", "findUnique", {
- where: { id: faker.datatype.number() },
- include: {
- posts: {
- include: {
- comments: {
- include: {
- replies: true,
- },
- },
- },
- },
- },
- }),
- calls: [
- {
- action: "include",
- model: "Post",
- argsPath: "args.include.posts",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "include",
- model: "Comment",
- argsPath: "args.include.posts.include.comments",
- relation: getModelRelation("Post", "comments"),
- scope: {
- action: "include",
- model: "Post",
- argsPath: "args.include.posts",
- relation: getModelRelation("User", "posts"),
- },
- },
- {
- action: "include",
- model: "Comment",
- argsPath: "args.include.posts.include.comments.include.replies",
- relation: getModelRelation("Comment", "replies"),
- scope: {
- action: "include",
- model: "Comment",
- argsPath: "args.include.posts.include.comments",
- relation: getModelRelation("Post", "comments"),
- scope: {
- action: "include",
- model: "Post",
- argsPath: "args.include.posts",
- relation: getModelRelation("User", "posts"),
- },
- },
- },
- ],
- },
- {
- description: "select in findUnique",
- rootParams: createParams("User", "findUnique", {
- where: { id: faker.datatype.number() },
- select: {
- posts: true,
- },
- }),
- calls: [
- {
- action: "select",
- model: "Post",
- argsPath: "args.select.posts",
- relation: getModelRelation("User", "posts"),
- },
- ],
- },
- {
- description: "select in findFirst",
- rootParams: createParams("User", "findFirst", {
- where: { id: faker.datatype.number() },
- select: { posts: true },
- }),
- calls: [
- {
- action: "select",
- model: "Post",
- argsPath: "args.select.posts",
- relation: getModelRelation("User", "posts"),
- },
- ],
- },
- {
- description: "select in findMany",
- rootParams: createParams("User", "findMany", {
- where: { id: faker.datatype.number() },
- select: { posts: true },
- }),
- calls: [
- {
- action: "select",
- model: "Post",
- argsPath: "args.select.posts",
- relation: getModelRelation("User", "posts"),
- },
- ],
- },
- {
- description: "select in create",
- rootParams: createParams("User", "create", {
- data: {
- email: faker.internet.email(),
- posts: { create: { title: faker.lorem.sentence() } },
- },
- select: { posts: true },
- }),
- calls: [
- {
- action: "create",
- model: "Post",
- argsPath: "args.data.posts.create",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "select",
- model: "Post",
- argsPath: "args.select.posts",
- relation: getModelRelation("User", "posts"),
- },
- ],
- },
- {
- description: "select in update",
- rootParams: createParams("User", "update", {
- where: { id: faker.datatype.number() },
- data: {
- email: faker.internet.email(),
- },
- select: { posts: true },
- }),
- calls: [
- {
- action: "select",
- model: "Post",
- argsPath: "args.select.posts",
- relation: getModelRelation("User", "posts"),
- },
- ],
- },
- {
- description: "select in upsert",
- rootParams: createParams("User", "upsert", {
- where: { id: faker.datatype.number() },
- create: {
- email: faker.internet.email(),
- },
- update: {
- email: faker.internet.email(),
- },
- select: { posts: true },
- }),
- calls: [
- {
- action: "select",
- model: "Post",
- argsPath: "args.select.posts",
- relation: getModelRelation("User", "posts"),
- },
- ],
- },
- {
- description: "nested selects",
- rootParams: createParams("User", "findUnique", {
- where: { id: faker.datatype.number() },
- select: {
- posts: {
- select: {
- comments: true,
- },
- },
- },
- }),
- calls: [
- {
- action: "select",
- model: "Post",
- argsPath: "args.select.posts",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "select",
- model: "Comment",
- argsPath: "args.select.posts.select.comments",
- relation: getModelRelation("Post", "comments"),
- scope: {
- action: "select",
- model: "Post",
- argsPath: "args.select.posts",
- relation: getModelRelation("User", "posts"),
- },
- },
- ],
- },
- {
- description: "deeply nested selects",
- rootParams: createParams("User", "findUnique", {
- where: { id: faker.datatype.number() },
- select: {
- posts: {
- select: {
- comments: {
- select: {
- replies: true,
- },
- },
- },
- },
- },
- }),
- calls: [
- {
- action: "select",
- model: "Post",
- argsPath: "args.select.posts",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "select",
- model: "Comment",
- argsPath: "args.select.posts.select.comments",
- relation: getModelRelation("Post", "comments"),
- scope: {
- action: "select",
- model: "Post",
- argsPath: "args.select.posts",
- relation: getModelRelation("User", "posts"),
- },
- },
- {
- action: "select",
- model: "Comment",
- argsPath: "args.select.posts.select.comments.select.replies",
- relation: getModelRelation("Comment", "replies"),
- scope: {
- action: "select",
- model: "Comment",
- argsPath: "args.select.posts.select.comments",
- relation: getModelRelation("Post", "comments"),
- scope: {
- action: "select",
- model: "Post",
- argsPath: "args.select.posts",
- relation: getModelRelation("User", "posts"),
- },
- },
- },
- ],
- },
- {
- description: "deeply nested selects with custom fields",
- rootParams: createParams("User", "findUnique", {
- where: { id: faker.datatype.number() },
- select: {
- posts: {
- select: {
- title: true,
- comments: {
- select: {
- content: true,
- replies: {
- select: {
- content: true,
- createdAt: true,
- },
- },
- },
- },
- },
- },
- },
- }),
- calls: [
- {
- action: "select",
- model: "Post",
- argsPath: "args.select.posts",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "select",
- model: "Comment",
- argsPath: "args.select.posts.select.comments",
- relation: getModelRelation("Post", "comments"),
- scope: {
- action: "select",
- model: "Post",
- argsPath: "args.select.posts",
- relation: getModelRelation("User", "posts"),
- },
- },
- {
- action: "select",
- model: "Comment",
- argsPath: "args.select.posts.select.comments.select.replies",
- relation: getModelRelation("Comment", "replies"),
- scope: {
- action: "select",
- model: "Comment",
- argsPath: "args.select.posts.select.comments",
- relation: getModelRelation("Post", "comments"),
- scope: {
- action: "select",
- model: "Post",
- argsPath: "args.select.posts",
- relation: getModelRelation("User", "posts"),
- },
- },
- },
- ],
- },
- {
- description: "nested select in include",
- rootParams: createParams("User", "findUnique", {
- where: { id: faker.datatype.number() },
- include: {
- posts: {
- select: {
- comments: true,
- },
- },
- },
- }),
- calls: [
- {
- action: "include",
- model: "Post",
- argsPath: "args.include.posts",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "select",
- model: "Post",
- argsPath: "args.include.posts.select",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "select",
- model: "Comment",
- argsPath: "args.include.posts.select.comments",
- relation: getModelRelation("Post", "comments"),
- scope: {
- action: "include",
- model: "Post",
- argsPath: "args.include.posts",
- relation: getModelRelation("User", "posts"),
- },
- },
- ],
- },
- {
- description: "nested include in select",
- rootParams: createParams("User", "findUnique", {
- where: { id: faker.datatype.number() },
- select: {
- posts: {
- include: {
- comments: true,
- },
- },
- },
- }),
- calls: [
- {
- action: "select",
- model: "Post",
- argsPath: "args.select.posts",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "include",
- model: "Comment",
- argsPath: "args.select.posts.include.comments",
- relation: getModelRelation("Post", "comments"),
- scope: {
- action: "select",
- model: "Post",
- argsPath: "args.select.posts",
- relation: getModelRelation("User", "posts"),
- },
- },
- ],
- },
- {
- description: "nested select in nested include",
- rootParams: createParams("User", "findUnique", {
- where: { id: faker.datatype.number() },
- include: {
- posts: {
- include: {
- comments: {
- select: {
- content: true,
- },
- },
- },
- },
- },
- }),
- calls: [
- {
- action: "include",
- model: "Post",
- argsPath: "args.include.posts",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "include",
- model: "Comment",
- argsPath: "args.include.posts.include.comments",
- relation: getModelRelation("Post", "comments"),
- scope: {
- action: "include",
- model: "Post",
- argsPath: "args.include.posts",
- relation: getModelRelation("User", "posts"),
- },
- },
- {
- action: "select",
- model: "Comment",
- argsPath: "args.include.posts.include.comments.select",
- relation: getModelRelation("Post", "comments"),
- scope: {
- action: "include",
- model: "Post",
- argsPath: "args.include.posts",
- relation: getModelRelation("User", "posts"),
- },
- },
- ],
- },
- {
- description: "nested include in nested select",
- rootParams: createParams("User", "findUnique", {
- where: { id: faker.datatype.number() },
- select: {
- posts: {
- select: {
- comments: {
- include: {
- author: true,
- },
- },
- },
- },
- },
- }),
- calls: [
- {
- action: "select",
- model: "Post",
- argsPath: "args.select.posts",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "select",
- model: "Comment",
- argsPath: "args.select.posts.select.comments",
- relation: getModelRelation("Post", "comments"),
- scope: {
- action: "select",
- model: "Post",
- argsPath: "args.select.posts",
- relation: getModelRelation("User", "posts"),
- },
- },
- {
- action: "include",
- model: "User",
- argsPath: "args.select.posts.select.comments.include.author",
- relation: getModelRelation("Comment", "author"),
- scope: {
- action: "select",
- model: "Comment",
- argsPath: "args.select.posts.select.comments",
- relation: getModelRelation("Post", "comments"),
- scope: {
- action: "select",
- model: "Post",
- argsPath: "args.select.posts",
- relation: getModelRelation("User", "posts"),
- },
- },
- },
- ],
- },
- {
- description: "nested includes with nested create",
- rootParams: createParams("User", "create", {
- data: {
- email: faker.internet.email(),
- posts: {
- create: {
- title: faker.lorem.sentence(),
- comments: {
- create: {
- authorId: faker.datatype.number(),
- content: faker.lorem.sentence(),
- },
- },
- },
- },
- },
- include: {
- posts: {
- include: {
- comments: true,
- },
- },
- },
- }),
- calls: [
- {
- action: "create",
- model: "Post",
- argsPath: "args.data.posts.create",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "create",
- model: "Comment",
- argsPath: "args.data.posts.create.comments.create",
- relation: getModelRelation("Post", "comments"),
- scope: {
- action: "create",
- model: "Post",
- argsPath: "args.data.posts.create",
- relation: getModelRelation("User", "posts"),
- },
- },
- {
- action: "include",
- model: "Post",
- argsPath: "args.include.posts",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "include",
- model: "Comment",
- argsPath: "args.include.posts.include.comments",
- relation: getModelRelation("Post", "comments"),
- scope: {
- action: "include",
- model: "Post",
- argsPath: "args.include.posts",
- relation: getModelRelation("User", "posts"),
- },
- },
- ],
- },
- {
- description: "nested selects with nested create",
- rootParams: createParams("User", "create", {
- data: {
- email: faker.internet.email(),
- posts: {
- create: {
- title: faker.lorem.sentence(),
- comments: {
- create: {
- authorId: faker.datatype.number(),
- content: faker.lorem.sentence(),
- },
- },
- },
- },
- },
- select: {
- posts: {
- select: {
- comments: true,
- },
- },
- },
- }),
- calls: [
- {
- action: "create",
- model: "Post",
- argsPath: "args.data.posts.create",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "create",
- model: "Comment",
- argsPath: "args.data.posts.create.comments.create",
- relation: getModelRelation("Post", "comments"),
- scope: {
- action: "create",
- model: "Post",
- argsPath: "args.data.posts.create",
- relation: getModelRelation("User", "posts"),
- },
- },
- {
- action: "select",
- model: "Post",
- argsPath: "args.select.posts",
- relation: getModelRelation("User", "posts"),
- },
- {
- action: "select",
- model: "Comment",
- argsPath: "args.select.posts.select.comments",
- relation: getModelRelation("Post", "comments"),
- scope: {
- action: "select",
- model: "Post",
- argsPath: "args.select.posts",
- relation: getModelRelation("User", "posts"),
- },
- },
- ],
- },
- ])("calls middleware with $description", async ({ rootParams, calls }) => {
- const middleware = jest.fn((params, next) => next(params));
- const nestedMiddleware = createNestedMiddleware(middleware);
-
- const next = (_: any) => Promise.resolve({});
- await nestedMiddleware(rootParams, next);
-
- expect(middleware).toHaveBeenCalledTimes(calls.length + 1);
- expect(middleware).toHaveBeenCalledWith(rootParams, next);
- calls.forEach((call) => {
- expect(middleware).toHaveBeenCalledWith(
- nestedParamsFromCall(rootParams, call),
- expect.any(Function)
- );
- });
- });
-});
diff --git a/test/e2e/client.ts b/test/e2e/client.ts
new file mode 100644
index 0000000..2c3e98f
--- /dev/null
+++ b/test/e2e/client.ts
@@ -0,0 +1,3 @@
+import { PrismaClient } from "@prisma/client";
+
+export default new PrismaClient();
diff --git a/test/e2e/smoke.test.ts b/test/e2e/smoke.test.ts
new file mode 100644
index 0000000..d2c97f3
--- /dev/null
+++ b/test/e2e/smoke.test.ts
@@ -0,0 +1,136 @@
+import { Post, PrismaClient, User } from "@prisma/client";
+import faker from "faker";
+
+import { createNestedMiddleware } from "../../src";
+import client from "./client";
+
+describe("smoke", () => {
+ let testClient: PrismaClient;
+ let firstUser: User;
+ let secondUser: User;
+ let post: Post;
+
+ beforeAll(() => {
+ testClient = new PrismaClient();
+ });
+ beforeEach(async () => {
+ firstUser = await client.user.create({
+ data: {
+ email: faker.internet.email(),
+ name: "Jack",
+ },
+ });
+ secondUser = await client.user.create({
+ data: {
+ email: faker.internet.email(),
+ name: "John",
+ },
+ });
+ await client.profile.create({
+ data: {
+ bio: faker.lorem.sentence(),
+ user: {
+ connect: {
+ id: firstUser.id,
+ },
+ },
+ },
+ });
+ post = await client.post.create({
+ data: {
+ title: faker.lorem.sentence(),
+ authorId: firstUser.id,
+ },
+ });
+ await client.comment.create({
+ data: {
+ content: faker.lorem.sentence(),
+ author: {
+ connect: {
+ id: firstUser.id,
+ },
+ },
+ post: {
+ connect: {
+ id: post.id,
+ },
+ },
+ },
+ });
+ });
+ afterEach(async () => {
+ await client.comment.deleteMany({ where: {} });
+ await client.post.deleteMany({ where: {} });
+ await client.profile.deleteMany({ where: {} });
+ await client.user.deleteMany({ where: {} });
+ });
+ afterAll(async () => {
+ await testClient.$disconnect();
+ });
+
+ describe("vanilla", () => {
+ beforeAll(() => {
+ testClient.$use(
+ createNestedMiddleware((params, next) => {
+ return next(params);
+ })
+ );
+ });
+
+ it("does not break client when middleware does nothing", async () => {
+ const user = await testClient.user.findFirst({
+ where: { id: firstUser.id },
+ });
+ expect(user).not.toBeNull();
+
+ const users = await testClient.user.findMany({
+ where: { id: { in: [firstUser.id, secondUser.id] } },
+ });
+ expect(users).toHaveLength(2);
+
+ const dbProfile = await testClient.profile.findFirst({
+ where: { user: { id: firstUser.id } },
+ });
+ expect(dbProfile).not.toBeNull();
+
+ const dbComment = await testClient.comment.findFirst({
+ where: { post: { author: { id: firstUser.id } } },
+ });
+ expect(dbComment).not.toBeNull();
+
+ const dbComments = await testClient.comment.findMany({
+ where: { post: { author: { id: firstUser.id } } },
+ include: {
+ post: {
+ include: {
+ comments: {
+ select: {
+ id: true,
+ },
+ },
+ },
+ },
+ },
+ });
+ expect(dbComments).toHaveLength(1);
+ expect(dbComments[0].post).not.toBeNull();
+ expect(dbComments[0].post!.comments).toHaveLength(1);
+
+ const createdComment = await testClient.comment.create({
+ data: {
+ content: faker.lorem.sentence(),
+ authorId: firstUser.id,
+ },
+ });
+ expect(createdComment).not.toBeNull();
+
+ const updatedComment = await testClient.comment.update({
+ where: { id: createdComment.id },
+ data: {
+ content: faker.lorem.sentence(),
+ },
+ });
+ expect(updatedComment).not.toBeNull();
+ });
+ });
+});
diff --git a/test/results.test.ts b/test/results.test.ts
deleted file mode 100644
index 3ff97f0..0000000
--- a/test/results.test.ts
+++ /dev/null
@@ -1,1282 +0,0 @@
-import faker from "faker";
-
-import { createNestedMiddleware } from "../src";
-import { createParams } from "./utils/createParams";
-import { wait } from "./utils/wait";
-
-function addReturnedDate(result: any) {
- if (typeof result === "undefined") return;
- const returned = new Date();
-
- if (Array.isArray(result)) {
- return result.map((item) => ({ ...item, returned }));
- }
-
- return { ...result, returned };
-}
-
-describe("modifying results", () => {
- it("returns correct result by default", async () => {
- const nestedMiddleware = createNestedMiddleware((params, next) => {
- return next(params);
- });
-
- const params = createParams("User", "create", {
- data: { email: faker.internet.email() },
- });
- const next = jest.fn(() =>
- Promise.resolve({
- id: faker.datatype.number(),
- email: params.args.data.email,
- })
- );
- const result = await nestedMiddleware(params, next);
-
- expect(result).toEqual({
- id: expect.any(Number),
- email: params.args.data.email,
- });
- });
-
- it("returns correct result when relations are included", async () => {
- const nestedMiddleware = createNestedMiddleware((params, next) => {
- return next(params);
- });
-
- const params = createParams("Post", "findMany", {
- where: { title: faker.lorem.sentence() },
- include: {
- author: { include: { profile: true, posts: true } },
- comments: { include: { author: true } },
- },
- });
- const clientResult = [
- {
- id: faker.datatype.number(),
- title: faker.lorem.sentence(),
- author: {
- id: faker.datatype.number(),
- email: faker.internet.email(),
- profile: null,
- posts: [
- {
- id: faker.datatype.number(),
- title: faker.lorem.sentence(),
- comments: [
- { id: faker.datatype.number(), content: faker.lorem.text() },
- { id: faker.datatype.number(), content: faker.lorem.text() },
- { id: faker.datatype.number(), content: faker.lorem.text() },
- ],
- },
- {
- id: faker.datatype.number(),
- title: faker.lorem.sentence(),
- comments: null,
- },
- ],
- },
- comments: [
- {
- id: faker.datatype.number(),
- content: faker.lorem.paragraph(),
- author: {
- id: faker.datatype.number(),
- email: faker.internet.email(),
- },
- },
- {
- id: faker.datatype.number(),
- content: faker.lorem.paragraph(),
- author: null,
- },
- ],
- },
- {
- id: faker.datatype.number(),
- title: faker.lorem.sentence(),
- author: null,
- comments: null,
- },
- ];
- const next = jest.fn(() => Promise.resolve(clientResult));
- const result = await nestedMiddleware(params, next);
-
- expect(result).toEqual(clientResult);
- });
-
- it("returns correct result when relations are selected", async () => {
- const nestedMiddleware = createNestedMiddleware((params, next) => {
- return next(params);
- });
-
- const params = createParams("Post", "findMany", {
- where: { title: faker.lorem.sentence() },
- select: {
- content: true,
- author: {
- select: {
- email: true,
- profile: { select: { bio: true } },
- posts: {
- select: {
- title: true,
- comments: {
- select: { content: true },
- },
- },
- },
- },
- },
- comments: {
- select: { author: true },
- },
- },
- });
-
- const clientResult = [
- {
- title: faker.lorem.sentence(),
- author: {
- email: faker.internet.email(),
- profile: null,
- posts: [
- {
- title: faker.lorem.sentence(),
- comments: [
- { content: faker.lorem.text() },
- { content: faker.lorem.text() },
- { content: faker.lorem.text() },
- ],
- },
- { title: faker.lorem.sentence(), comments: null },
- ],
- },
- comments: [
- {
- author: {
- id: faker.datatype.number(),
- email: faker.internet.email(),
- },
- },
- { author: null },
- ],
- },
- {
- title: faker.lorem.sentence(),
- author: null,
- comments: null,
- },
- ];
- const next = jest.fn(() => Promise.resolve(clientResult));
- const result = await nestedMiddleware(params, next);
-
- expect(result).toEqual(clientResult);
- });
-
- it("returns correct result when relations are included and selected", async () => {
- const nestedMiddleware = createNestedMiddleware((params, next) => {
- return next(params);
- });
-
- const params = createParams("Post", "findMany", {
- where: { title: faker.lorem.sentence() },
- include: {
- author: {
- select: {
- email: true,
- profile: { select: { bio: true } },
- posts: {
- include: {
- comments: true,
- },
- },
- },
- },
- comments: {
- select: { author: true },
- },
- },
- });
-
- const clientResult = [
- {
- id: faker.datatype.number(),
- title: faker.lorem.sentence(),
- author: {
- email: faker.internet.email(),
- profile: null,
- posts: [
- {
- id: faker.datatype.number(),
- title: faker.lorem.sentence(),
- comments: [
- { content: faker.lorem.text() },
- { content: faker.lorem.text() },
- { content: faker.lorem.text() },
- ],
- },
- {
- id: faker.datatype.number(),
- title: faker.lorem.sentence(),
- comments: null,
- },
- ],
- },
- comments: [
- {
- id: faker.datatype.number(),
- content: faker.lorem.text(),
- author: {
- id: faker.datatype.number(),
- email: faker.internet.email(),
- },
- },
- {
- id: faker.datatype.number(),
- content: faker.lorem.text(),
- author: null,
- },
- ],
- },
- {
- id: faker.datatype.number(),
- title: faker.lorem.sentence(),
- author: null,
- comments: null,
- },
- ];
- const next = jest.fn(() => Promise.resolve(clientResult));
- const result = await nestedMiddleware(params, next);
-
- expect(result).toEqual(clientResult);
- });
-
- it("allows middleware to modify root result", async () => {
- const nestedMiddleware = createNestedMiddleware(async (params, next) => {
- const result = await next(params);
- return addReturnedDate(result);
- });
-
- const params = createParams("User", "create", {
- data: { email: faker.internet.email() },
- });
- const next = jest.fn(() =>
- Promise.resolve({
- id: faker.datatype.number(),
- email: params.args.data.email,
- })
- );
- const result = await nestedMiddleware(params, next);
-
- expect(result).toEqual({
- id: expect.any(Number),
- email: params.args.data.email,
- returned: expect.any(Date),
- });
- });
-
- it("allows middleware to modify root result asynchronously", async () => {
- const nestedMiddleware = createNestedMiddleware(async (params, next) => {
- const result = await next(params);
- await wait(100);
- return addReturnedDate(result);
- });
-
- const params = createParams("User", "create", {
- data: { email: faker.internet.email() },
- });
- const next = jest.fn(() =>
- Promise.resolve({
- id: faker.datatype.number(),
- email: params.args.data.email,
- })
- );
- const result = await nestedMiddleware(params, next);
-
- expect(result).toEqual({
- id: expect.any(Number),
- email: params.args.data.email,
- returned: expect.any(Date),
- });
- });
-
- it("allows middleware to modify included results through include action", async () => {
- const nestedMiddleware = createNestedMiddleware(async (params, next) => {
- const result = await next(params);
- if (!result) return;
-
- if (params.action === "include" && params.model === "Post") {
- return addReturnedDate(result);
- }
-
- return result;
- });
-
- const next = jest.fn((params) =>
- Promise.resolve({
- id: faker.datatype.number(),
- email: params.args.data.email,
- posts: [
- {
- id: faker.datatype.number(),
- title: params.args.data.posts.create.title,
- },
- ],
- })
- );
- const params = createParams("User", "create", {
- data: {
- email: faker.internet.email(),
- posts: { create: { title: faker.lorem.sentence() } },
- },
- include: {
- posts: true,
- },
- });
- const result = await nestedMiddleware(params, next);
-
- expect(result).toEqual({
- id: expect.any(Number),
- email: params.args.data.email,
- posts: [
- {
- id: expect.any(Number),
- title: params.args.data.posts.create.title,
- returned: expect.any(Date),
- },
- ],
- });
- });
-
- it("allows middleware to modify deeply included results through include action", async () => {
- const nestedMiddleware = createNestedMiddleware(async (params, next) => {
- const result = await next(params);
- if (!result) return;
-
- if (params.action === "include" && params.model === "Comment") {
- return addReturnedDate(result);
- }
-
- return result;
- });
-
- const next = jest.fn((params) =>
- Promise.resolve({
- id: faker.datatype.number(),
- email: params.args.data.email,
- posts: [
- {
- id: faker.datatype.number(),
- title: params.args.data.posts.create.title,
- comments: [
- {
- id: faker.datatype.number(),
- content: faker.lorem.sentence(),
- replies: [
- {
- id: faker.datatype.number(),
- content: faker.lorem.sentence(),
- },
- {
- id: faker.datatype.number(),
- content: faker.lorem.sentence(),
- },
- ],
- },
- ],
- },
- ],
- })
- );
- const params = createParams("User", "create", {
- data: {
- email: faker.internet.email(),
- posts: { create: { title: faker.lorem.sentence() } },
- },
- include: {
- posts: {
- include: {
- comments: {
- include: {
- replies: true,
- },
- },
- },
- },
- },
- });
- const result = await nestedMiddleware(params, next);
-
- expect(result).toEqual({
- id: expect.any(Number),
- email: params.args.data.email,
- posts: [
- {
- id: expect.any(Number),
- title: params.args.data.posts.create.title,
- comments: [
- {
- id: expect.any(Number),
- content: expect.any(String),
- returned: expect.any(Date),
- replies: [
- {
- id: expect.any(Number),
- content: expect.any(String),
- returned: expect.any(Date),
- },
- {
- id: expect.any(Number),
- content: expect.any(String),
- returned: expect.any(Date),
- },
- ],
- },
- ],
- },
- ],
- });
- });
-
- it("allows middleware to modify selected results through select action", async () => {
- const nestedMiddleware = createNestedMiddleware(async (params, next) => {
- const result = await next(params);
- if (!result) return;
-
- if (params.action === "select" && params.model === "Post") {
- return addReturnedDate(result);
- }
-
- return result;
- });
-
- const next = jest.fn((params) =>
- Promise.resolve({
- id: faker.datatype.number(),
- email: params.args.data.email,
- posts: [
- {
- id: faker.datatype.number(),
- title: params.args.data.posts.create.title,
- },
- ],
- })
- );
- const params = createParams("User", "create", {
- data: {
- email: faker.internet.email(),
- posts: { create: { title: faker.lorem.sentence() } },
- },
- select: {
- posts: true,
- },
- });
- const result = await nestedMiddleware(params, next);
-
- expect(result).toEqual({
- id: expect.any(Number),
- email: params.args.data.email,
- posts: [
- {
- id: expect.any(Number),
- title: params.args.data.posts.create.title,
- returned: expect.any(Date),
- },
- ],
- });
- });
-
- it("allows middleware to modify deeply selected results through select action", async () => {
- const nestedMiddleware = createNestedMiddleware(async (params, next) => {
- const result = await next(params);
- if (!result) return;
-
- if (params.action === "select" && params.model === "Comment") {
- return addReturnedDate(result);
- }
-
- return result;
- });
-
- const next = jest.fn((params) =>
- Promise.resolve({
- id: faker.datatype.number(),
- email: params.args.data.email,
- posts: [
- {
- id: faker.datatype.number(),
- title: params.args.data.posts.create.title,
- comments: [
- {
- id: faker.datatype.number(),
- content: faker.lorem.sentence(),
- replies: [
- {
- id: faker.datatype.number(),
- content: faker.lorem.sentence(),
- },
- {
- id: faker.datatype.number(),
- content: faker.lorem.sentence(),
- },
- ],
- },
- ],
- },
- ],
- })
- );
- const params = createParams("User", "create", {
- data: {
- email: faker.internet.email(),
- posts: { create: { title: faker.lorem.sentence() } },
- },
- include: {
- posts: {
- select: {
- comments: {
- select: {
- replies: true,
- },
- },
- },
- },
- },
- });
- const result = await nestedMiddleware(params, next);
-
- expect(result).toEqual({
- id: expect.any(Number),
- email: params.args.data.email,
- posts: [
- {
- id: expect.any(Number),
- title: params.args.data.posts.create.title,
- comments: [
- {
- id: expect.any(Number),
- content: expect.any(String),
- returned: expect.any(Date),
- replies: [
- {
- id: expect.any(Number),
- content: expect.any(String),
- returned: expect.any(Date),
- },
- {
- id: expect.any(Number),
- content: expect.any(String),
- returned: expect.any(Date),
- },
- ],
- },
- ],
- },
- ],
- });
- });
-
- it("allows middleware to modify selected in nested include through select action", async () => {
- const nestedMiddleware = createNestedMiddleware(async (params, next) => {
- const result = await next(params);
- if (!result) return;
-
- if (params.action === "select" && params.model === "Comment") {
- return addReturnedDate(result);
- }
-
- return result;
- });
-
- const next = jest.fn((params) =>
- Promise.resolve({
- id: faker.datatype.number(),
- email: params.args.data.email,
- posts: [
- {
- id: faker.datatype.number(),
- title: params.args.data.posts.create.title,
- comments: [
- {
- id: faker.datatype.number(),
- content: faker.lorem.sentence(),
- },
- ],
- },
- ],
- })
- );
- const params = createParams("User", "create", {
- data: {
- email: faker.internet.email(),
- posts: { create: { title: faker.lorem.sentence() } },
- },
- include: {
- posts: {
- select: {
- comments: true,
- },
- },
- },
- });
- const result = await nestedMiddleware(params, next);
-
- expect(result).toEqual({
- id: expect.any(Number),
- email: params.args.data.email,
- posts: [
- {
- id: expect.any(Number),
- title: params.args.data.posts.create.title,
- comments: [
- {
- id: expect.any(Number),
- content: expect.any(String),
- returned: expect.any(Date),
- },
- ],
- },
- ],
- });
- });
-
- it("allows middleware to modify deeply included results in nested select through include action", async () => {
- const nestedMiddleware = createNestedMiddleware(async (params, next) => {
- const result = await next(params);
- if (!result) return;
-
- if (params.action === "include" && params.model === "Comment") {
- return addReturnedDate(result);
- }
-
- return result;
- });
-
- const next = jest.fn((params) =>
- Promise.resolve({
- id: faker.datatype.number(),
- email: params.args.data.email,
-
- posts: [
- {
- id: faker.datatype.number(),
- title: params.args.data.posts.create.title,
- comments: [
- {
- id: faker.datatype.number(),
- content: faker.lorem.sentence(),
- replies: [
- {
- id: faker.datatype.number(),
- content: faker.lorem.sentence(),
- },
- ],
- },
- ],
- },
- ],
- })
- );
- const params = createParams("User", "create", {
- data: {
- email: faker.internet.email(),
- posts: { create: { title: faker.lorem.sentence() } },
- },
- include: {
- posts: {
- select: {
- comments: {
- include: {
- replies: true,
- },
- },
- },
- },
- },
- });
- const result = await nestedMiddleware(params, next);
-
- expect(result).toEqual({
- id: expect.any(Number),
- email: params.args.data.email,
- posts: [
- {
- id: expect.any(Number),
- title: params.args.data.posts.create.title,
- comments: [
- {
- id: expect.any(Number),
- content: expect.any(String),
- replies: [
- {
- id: expect.any(Number),
- content: expect.any(String),
- returned: expect.any(Date),
- },
- ],
- },
- ],
- },
- ],
- });
- });
-
- it("allows middleware to modify nested results", async () => {
- const nestedMiddleware = createNestedMiddleware(async (params, next) => {
- const result = await next(params);
- if (!result) return;
-
- if (params.model === "Post") {
- return addReturnedDate(result);
- }
-
- return result;
- });
-
- const next = jest.fn(() =>
- Promise.resolve({
- id: faker.datatype.number(),
- email: params.args.data.email,
- posts: [
- {
- id: faker.datatype.number(),
- title: params.args.data.posts.create.title,
- },
- ],
- })
- );
- const params = createParams("User", "create", {
- data: {
- email: faker.internet.email(),
- posts: { create: { title: faker.lorem.sentence() } },
- },
- });
- const result = await nestedMiddleware(params, next);
-
- expect(result).toEqual({
- id: expect.any(Number),
- email: params.args.data.email,
- posts: [
- {
- id: expect.any(Number),
- title: params.args.data.posts.create.title,
- returned: expect.any(Date),
- },
- ],
- });
- });
-
- it("allows middleware to modify nested results asynchronously", async () => {
- const nestedMiddleware = createNestedMiddleware(async (params, next) => {
- const result = await next(params);
- if (!result) return;
-
- if (params.model === "Post") {
- await wait(100);
- return addReturnedDate(result);
- }
-
- return result;
- });
-
- const next = jest.fn(() =>
- Promise.resolve({
- id: faker.datatype.number(),
- email: params.args.data.email,
- posts: [
- {
- id: faker.datatype.number(),
- title: params.args.data.posts.create.title,
- },
- ],
- })
- );
- const params = createParams("User", "create", {
- data: {
- email: faker.internet.email(),
- posts: { create: { title: faker.lorem.sentence() } },
- },
- });
- const result = await nestedMiddleware(params, next);
-
- expect(result).toEqual({
- id: expect.any(Number),
- email: params.args.data.email,
- posts: [
- {
- id: expect.any(Number),
- title: params.args.data.posts.create.title,
- returned: expect.any(Date),
- },
- ],
- });
- });
-
- it("allows middleware to modify results within nested results", async () => {
- const nestedMiddleware = createNestedMiddleware(async (params, next) => {
- const result = await next(params);
- if (typeof result === "undefined") return;
-
- if (params.model === "Profile") {
- return addReturnedDate(result);
- }
-
- // modify profile first to check it is not overwritten by other calls
- await wait(100);
- return addReturnedDate(result);
- });
-
- const params = createParams("Post", "create", {
- data: {
- title: faker.lorem.sentence(),
- author: {
- create: {
- email: faker.internet.email(),
- profile: {
- create: { bio: faker.lorem.sentence() },
- },
- },
- },
- },
- });
- const next = jest.fn(() =>
- Promise.resolve({
- id: faker.datatype.number(),
- title: params.args.data.title,
- author: {
- id: faker.datatype.number(),
- email: params.args.data.author.create.email,
- profile: {
- id: faker.datatype.number(),
- bio: params.args.data.author.create.profile.create.bio,
- },
- },
- })
- );
- const result = await nestedMiddleware(params, next);
-
- expect(result).toEqual({
- id: expect.any(Number),
- title: params.args.data.title,
- returned: expect.any(Date),
- author: {
- id: expect.any(Number),
- email: params.args.data.author.create.email,
- returned: expect.any(Date),
- profile: {
- id: expect.any(Number),
- bio: params.args.data.author.create.profile.create.bio,
- returned: expect.any(Date),
- },
- },
- });
- });
-
- it("allows middleware to modify results within nested list results", async () => {
- const nestedMiddleware = createNestedMiddleware(async (params, next) => {
- const result = await next(params);
- if (typeof result === "undefined") return;
-
- if (params.model === "User") {
- return addReturnedDate(result);
- }
-
- // modify author first to check it is not overwritten by other calls
- await wait(100);
- return addReturnedDate(result);
- });
-
- const params = createParams("Post", "create", {
- data: {
- title: faker.lorem.sentence(),
- authorId: faker.datatype.number(),
- comments: {
- create: {
- content: faker.lorem.sentence(),
- author: {
- create: {
- email: faker.internet.email(),
- },
- },
- },
- },
- },
- });
- const next = jest.fn(() =>
- Promise.resolve({
- id: faker.datatype.number(),
- title: params.args.data.title,
- authorId: params.args.data.authorId,
- comments: [
- {
- id: faker.datatype.number(),
- content: params.args.data.comments.create.content,
- author: {
- id: faker.datatype.number(),
- email: params.args.data.comments.create.author.create.email,
- },
- },
- ],
- })
- );
- const result = await nestedMiddleware(params, next);
-
- expect(result).toEqual({
- id: expect.any(Number),
- title: params.args.data.title,
- authorId: params.args.data.authorId,
- returned: expect.any(Date),
- comments: [
- {
- id: expect.any(Number),
- content: params.args.data.comments.create.content,
- returned: expect.any(Date),
- author: {
- id: expect.any(Number),
- email: params.args.data.comments.create.author.create.email,
- returned: expect.any(Date),
- },
- },
- ],
- });
- });
-
- it("allows middleware to modify results within doubly nested list results", async () => {
- const nestedMiddleware = createNestedMiddleware(async (params, next) => {
- const result = await next(params);
- if (typeof result === "undefined") return;
-
- if (params.model === "User") {
- return addReturnedDate(result);
- }
-
- // modify author first to check it is not overwritten by other calls
- await wait(100);
- return addReturnedDate(result);
- });
-
- const params = createParams("Post", "create", {
- data: {
- title: faker.lorem.sentence(),
- authorId: faker.datatype.number(),
- comments: {
- create: {
- content: faker.lorem.sentence(),
- authorId: faker.datatype.number(),
- replies: {
- create: {
- content: faker.lorem.sentence(),
- author: {
- create: {
- email: faker.internet.email(),
- },
- },
- },
- },
- },
- },
- },
- });
- const next = jest.fn(() =>
- Promise.resolve({
- id: faker.datatype.number(),
- title: params.args.data.title,
- authorId: params.args.data.authorId,
- comments: [
- {
- id: faker.datatype.number(),
- content: params.args.data.comments.create.content,
- authorId: params.args.data.comments.create.authorId,
- replies: [
- {
- id: faker.datatype.number(),
- content:
- params.args.data.comments.create.replies.create.content,
- author: {
- id: faker.datatype.number(),
- email:
- params.args.data.comments.create.replies.create.author
- .create.email,
- },
- },
- ],
- },
- ],
- })
- );
- const result = await nestedMiddleware(params, next);
-
- expect(result).toEqual({
- id: expect.any(Number),
- title: params.args.data.title,
- authorId: params.args.data.authorId,
- returned: expect.any(Date),
- comments: [
- {
- id: expect.any(Number),
- content: params.args.data.comments.create.content,
- authorId: params.args.data.comments.create.authorId,
- returned: expect.any(Date),
- replies: [
- {
- id: expect.any(Number),
- content: params.args.data.comments.create.replies.create.content,
- returned: expect.any(Date),
- author: {
- id: expect.any(Number),
- email:
- params.args.data.comments.create.replies.create.author.create
- .email,
- returned: expect.any(Date),
- },
- },
- ],
- },
- ],
- });
- });
-
- it("allows middleware to modify list results within nested results", async () => {
- const nestedMiddleware = createNestedMiddleware(async (params, next) => {
- const result = await next(params);
- if (typeof result === "undefined") return;
-
- if (params.model === "Post") {
- return addReturnedDate(result);
- }
-
- // modify posts first to check they are not overwritten by other calls
- await wait(100);
- return addReturnedDate(result);
- });
-
- const params = createParams("Profile", "create", {
- data: {
- bio: faker.lorem.sentence(),
- user: {
- create: {
- email: faker.internet.email(),
- posts: {
- create: {
- title: faker.lorem.sentence(),
- },
- },
- },
- },
- },
- });
- const next = jest.fn(() =>
- Promise.resolve({
- id: faker.datatype.number(),
- bio: params.args.data.bio,
- user: {
- id: faker.datatype.number(),
- email: params.args.data.user.create.email,
- posts: [
- {
- id: faker.datatype.number(),
- title: params.args.data.user.create.posts.create.title,
- },
- ],
- },
- })
- );
- const result = await nestedMiddleware(params, next);
-
- expect(result).toEqual({
- id: expect.any(Number),
- bio: params.args.data.bio,
- returned: expect.any(Date),
- user: {
- id: expect.any(Number),
- email: params.args.data.user.create.email,
- returned: expect.any(Date),
- posts: [
- {
- id: expect.any(Number),
- title: params.args.data.user.create.posts.create.title,
- returned: expect.any(Date),
- },
- ],
- },
- });
- });
-
- it("allows middleware to modify list results within nested list results", async () => {
- const nestedMiddleware = createNestedMiddleware(async (params, next) => {
- const result = await next(params);
- if (typeof result === "undefined") return;
-
- if (params.model === "Comment") {
- return addReturnedDate(result);
- }
-
- // modify comments first to check they are not overwritten by other calls
- await wait(100);
- return addReturnedDate(result);
- });
-
- const params = createParams("User", "create", {
- data: {
- email: faker.internet.email(),
- posts: {
- create: {
- title: faker.lorem.sentence(),
- comments: {
- create: [
- {
- content: faker.lorem.sentence(),
- authorId: faker.datatype.number(),
- },
- {
- content: faker.lorem.sentence(),
- authorId: faker.datatype.number(),
- },
- ],
- },
- },
- },
- },
- });
- const next = jest.fn(() =>
- Promise.resolve({
- id: faker.datatype.number(),
- email: params.args.data.email,
- posts: [
- {
- id: faker.datatype.number(),
- title: params.args.data.posts.create.title,
- comments: params.args.data.posts.create.comments.create,
- },
- ],
- })
- );
- const result = await nestedMiddleware(params, next);
-
- expect(result).toEqual({
- id: expect.any(Number),
- email: params.args.data.email,
- returned: expect.any(Date),
- posts: [
- {
- id: expect.any(Number),
- title: params.args.data.posts.create.title,
- returned: expect.any(Date),
- comments: params.args.data.posts.create.comments.create.map(
- (comment: any) => ({
- ...comment,
- returned: expect.any(Date),
- })
- ),
- },
- ],
- });
- });
-
- it("waits for all middleware to finish modifying result before resolving", async () => {
- const nestedMiddleware = createNestedMiddleware(async (params, next) => {
- const result = await next(params);
- if (typeof result === "undefined") return;
-
- if (params.model === "Post") {
- await wait(100);
- return addReturnedDate(result);
- }
- if (params.model === "Profile") {
- await wait(200);
- return addReturnedDate(result);
- }
- await wait(300);
- return addReturnedDate(result);
- });
-
- const params = createParams("User", "create", {
- data: {
- email: faker.internet.email(),
- posts: {
- create: {
- title: faker.lorem.sentence(),
- },
- },
- profile: {
- create: {
- bio: faker.lorem.sentence(),
- },
- },
- },
- });
- const next = jest.fn(() =>
- Promise.resolve({
- id: faker.datatype.number(),
- email: params.args.data.email,
- posts: [
- {
- id: faker.datatype.number(),
- title: params.args.data.posts.create.title,
- },
- ],
- profile: {
- id: faker.datatype.number(),
- bio: params.args.data.profile.create.bio,
- },
- })
- );
- const result = await nestedMiddleware(params, next);
-
- expect(result).toEqual({
- id: expect.any(Number),
- email: params.args.data.email,
- returned: expect.any(Date),
- posts: [
- {
- id: expect.any(Number),
- title: params.args.data.posts.create.title,
- returned: expect.any(Date),
- },
- ],
- profile: {
- id: expect.any(Number),
- bio: params.args.data.profile.create.bio,
- returned: expect.any(Date),
- },
- });
- });
-
- it("nested middleware next functions return undefined when nested model is not included", async () => {
- const nestedMiddleware = createNestedMiddleware(async (params, next) => {
- const result = await next(params);
- if (typeof result === "undefined") return;
-
- if (params.model === "Post") {
- const returned = new Date();
-
- if (Array.isArray(result)) {
- return result.map((post) => ({ ...post, returned }));
- }
-
- return { ...result, returned };
- }
-
- return result;
- });
-
- const next = jest.fn(() =>
- Promise.resolve({
- id: faker.datatype.number(),
- email: params.args.data.email,
- })
- );
- const params = createParams("User", "create", {
- data: {
- email: faker.internet.email(),
- posts: { create: { title: faker.lorem.sentence() } },
- },
- });
- const result = await nestedMiddleware(params, next);
-
- expect(result).toEqual({
- id: expect.any(Number),
- email: params.args.data.email,
- });
- });
-});
diff --git a/test/scripts/run-with-postgres.sh b/test/scripts/run-with-postgres.sh
new file mode 100755
index 0000000..d70f5ce
--- /dev/null
+++ b/test/scripts/run-with-postgres.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+export DATABASE_URL=postgres://postgres:123@localhost:5432/test
+
+trap "docker compose down" EXIT
+
+docker compose up -d && sleep 1
+npx prisma db push
+
+$@
diff --git a/test/unit/actions.test.ts b/test/unit/actions.test.ts
new file mode 100644
index 0000000..e5c77c6
--- /dev/null
+++ b/test/unit/actions.test.ts
@@ -0,0 +1,3161 @@
+import faker from "faker";
+import { get, set } from "lodash";
+
+import { createNestedMiddleware } from "../../src";
+import { createParams } from "./helpers/createParams";
+import { wait } from "./helpers/wait";
+
+describe("actions", () => {
+ it("allows middleware to modify root params action", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "findUnique") {
+ return next({
+ ...params,
+ action: "findFirst",
+ });
+ }
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "findUnique", {
+ where: { id: faker.datatype.number() },
+ });
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(set(params, "action", "findFirst"));
+ });
+
+ it("allows middleware to modify root params action asynchronously", async () => {
+ const nestedMiddleware = createNestedMiddleware(async (params, next) => {
+ if (params.action === "findUnique") {
+ await wait(100);
+ return next({
+ ...params,
+ action: "findFirst",
+ });
+ }
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "findUnique", {
+ where: { id: faker.datatype.number() },
+ });
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(set(params, "action", "findFirst"));
+ });
+
+ it("applies middleware to operations moved to new action type", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "update" && params.scope) {
+ return next({
+ ...params,
+ action: "upsert",
+ args: {
+ where: { id: params.args.where.id },
+ create: params.args.data,
+ update: params.args.data,
+ },
+ });
+ }
+
+ if (params.action === "upsert") {
+ return next({
+ ...params,
+ args: {
+ ...params.args,
+ update: {
+ ...params.args.update,
+ number: faker.datatype.number(),
+ },
+ },
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ upsert: {
+ where: { id: faker.datatype.number() },
+ create: { title: faker.lorem.sentence() },
+ update: { title: faker.lorem.sentence() },
+ },
+ update: {
+ where: { id: faker.datatype.number() },
+ data: { title: faker.lorem.sentence() },
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ upsert: [
+ {
+ where: params.args.data.posts.upsert.where,
+ create: params.args.data.posts.upsert.create,
+ update: {
+ title: params.args.data.posts.upsert.update.title,
+ number: expect.any(Number),
+ },
+ },
+ {
+ where: params.args.data.posts.update.where,
+ create: params.args.data.posts.update.data,
+ update: {
+ title: params.args.data.posts.update.data.title,
+ // this should also be applied
+ number: expect.any(Number),
+ },
+ },
+ ],
+ })
+ );
+ });
+
+ it("merges operation converted to existing operation correctly when converted operation defined before target operation", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "update" && params.scope) {
+ return next({
+ ...params,
+ action: "upsert",
+ args: {
+ where: { id: params.args.where.id },
+ create: params.args.data,
+ update: params.args.data,
+ },
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ update: {
+ where: { id: faker.datatype.number() },
+ data: { title: faker.lorem.sentence() },
+ },
+ upsert: {
+ where: { id: faker.datatype.number() },
+ create: { title: faker.lorem.sentence() },
+ update: { title: faker.lorem.sentence() },
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ upsert: [
+ {
+ where: params.args.data.posts.upsert.where,
+ create: params.args.data.posts.upsert.create,
+ update: params.args.data.posts.upsert.update,
+ },
+ {
+ where: params.args.data.posts.update.where,
+ create: params.args.data.posts.update.data,
+ update: params.args.data.posts.update.data,
+ },
+ ],
+ })
+ );
+ });
+
+ it("replaces existing boolean action with new action when existing is defined before source action", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "delete") {
+ return next({
+ ...params,
+ action: "disconnect",
+ args: true,
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ profile: {
+ delete: true,
+ disconnect: false,
+ },
+ },
+ });
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.profile", {
+ disconnect: true,
+ })
+ );
+ });
+
+ it("waits for all middleware to finish before calling next when modifying nested action", async () => {
+ const nestedMiddleware = createNestedMiddleware(async (params, next) => {
+ if (params.action === "create" && params.scope) {
+ await wait(100);
+ return next({
+ ...params,
+ action: "update",
+ args: {
+ data: params.args,
+ where: { id: params.args.id },
+ },
+ });
+ }
+
+ if (params.action === "update") {
+ await wait(200);
+ return next({
+ ...params,
+ action: "upsert",
+ args: {
+ where: params.args.where,
+ create: params.args.data,
+ update: params.args.data,
+ },
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "create", {
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ create: {
+ id: faker.datatype.number(),
+ title: faker.lorem.sentence(),
+ comments: {
+ create: {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ authorId: faker.datatype.number(),
+ },
+ },
+ },
+ },
+ },
+ });
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ upsert: {
+ where: { id: params.args.data.posts.create.id },
+ create: {
+ ...params.args.data.posts.create,
+ comments: {
+ upsert: {
+ where: {
+ id: params.args.data.posts.create.comments.create.id,
+ },
+ create: params.args.data.posts.create.comments.create,
+ update: params.args.data.posts.create.comments.create,
+ },
+ },
+ },
+ update: {
+ ...params.args.data.posts.create,
+ comments: {
+ upsert: {
+ where: {
+ id: params.args.data.posts.create.comments.create.id,
+ },
+ create: params.args.data.posts.create.comments.create,
+ update: params.args.data.posts.create.comments.create,
+ },
+ },
+ },
+ },
+ })
+ );
+ });
+
+ it("allows middleware to modify deeply nested actions", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "create") {
+ if (params.scope) {
+ return next({
+ ...params,
+ action: "upsert",
+ args: {
+ where: { id: params.args.id },
+ create: params.args,
+ update: params.args,
+ },
+ });
+ }
+
+ return next({
+ ...params,
+ action: "upsert",
+ args: {
+ where: { id: params.args.data.id },
+ create: params.args.data,
+ update: params.args.data,
+ },
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ create: {
+ id: faker.datatype.number(),
+ title: faker.lorem.sentence(),
+ comments: {
+ create: {
+ id: faker.datatype.number(),
+ authorId: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ },
+ },
+ },
+ },
+ },
+ });
+ const createCommentData = params.args.data.posts.create.comments.create;
+ const createPostData = {
+ ...params.args.data.posts.create,
+ comments: {
+ upsert: {
+ where: { id: createCommentData.id },
+ create: createCommentData,
+ update: createCommentData,
+ },
+ },
+ };
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ upsert: {
+ where: { id: createPostData.id },
+ create: createPostData,
+ update: createPostData,
+ },
+ })
+ );
+ });
+
+ it("supports converting a nested action into two different actions", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "delete") {
+ return next([
+ {
+ ...params,
+ action: "update",
+ args: { deleted: true },
+ },
+ {
+ ...params,
+ action: "disconnect",
+ args: true,
+ },
+ ]);
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ profile: {
+ delete: true,
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.profile", {
+ disconnect: true,
+ update: {
+ deleted: true,
+ },
+ })
+ );
+ });
+
+ describe("create", () => {
+ it("allows middleware to modify nested create action to be an update", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "create") {
+ return next({
+ ...params,
+ action: "update",
+ args: {
+ where: { id: params.args.id },
+ data: params.args,
+ },
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ create: {
+ id: faker.datatype.number(),
+ title: faker.lorem.sentence(),
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ update: {
+ where: { id: params.args.data.posts.create.id },
+ data: params.args.data.posts.create,
+ },
+ })
+ );
+ });
+
+ it("allows middleware to modify nested create action to be an upsert", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "create") {
+ return next({
+ ...params,
+ action: "upsert",
+ args: {
+ where: { id: params.args.id },
+ create: params.args,
+ update: params.args,
+ },
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ create: {
+ title: faker.lorem.sentence(),
+ },
+ },
+ },
+ });
+ const createData = params.args.data.posts.create;
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ upsert: {
+ where: { id: createData.id },
+ create: createData,
+ update: createData,
+ },
+ })
+ );
+ });
+
+ it("allows middleware to modify nested create action to be a createMany", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "create") {
+ return next({
+ ...params,
+ action: "createMany",
+ args: {
+ data: [params.args],
+ },
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ create: { title: faker.lorem.sentence() },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ createMany: { data: [params.args.data.posts.create] },
+ })
+ );
+ });
+
+ it("allows middleware to modify nested create action array to be a createMany", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "create") {
+ return next({
+ ...params,
+ action: "createMany",
+ args: { data: [params.args] },
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ create: [
+ { title: faker.lorem.sentence() },
+ { title: faker.lorem.sentence() },
+ ],
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ createMany: { data: params.args.data.posts.create },
+ })
+ );
+ });
+
+ it("allows middleware to modify nested create action to be a connectOrCreate", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "create") {
+ return next({
+ ...params,
+ action: "connectOrCreate",
+ args: {
+ where: { id: params.args.id },
+ create: params.args,
+ },
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ create: {
+ id: faker.datatype.number(),
+ title: faker.lorem.sentence(),
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ connectOrCreate: {
+ where: { id: params.args.data.posts.create.id },
+ create: params.args.data.posts.create,
+ },
+ })
+ );
+ });
+ });
+
+ describe("update", () => {
+ it("allows middleware to modify nested update action to be a create", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "update" && params.model === "Post") {
+ return next({
+ ...params,
+ action: "create",
+ args: params.args.data,
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ update: {
+ where: { id: faker.datatype.number() },
+ data: { title: faker.lorem.sentence() },
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ create: params.args.data.posts.update.data,
+ })
+ );
+ });
+
+ it("allows middleware to modify nested update action to be a createMany", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "update" && params.model === "Post") {
+ return next({
+ ...params,
+ action: "createMany",
+ args: {
+ data: [params.args.data],
+ },
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ update: {
+ where: { id: faker.datatype.number() },
+ data: { title: faker.lorem.sentence() },
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ createMany: { data: [params.args.data.posts.update.data] },
+ })
+ );
+ });
+
+ it("allows middleware to modify nested update action to be a connectOrCreate", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "update" && params.model === "Post") {
+ return next({
+ ...params,
+ action: "connectOrCreate",
+ args: {
+ where: params.args.where,
+ create: params.args.data,
+ },
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ update: {
+ where: { id: faker.datatype.number() },
+ data: {
+ title: faker.lorem.sentence(),
+ },
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ connectOrCreate: {
+ where: params.args.data.posts.update.where,
+ create: params.args.data.posts.update.data,
+ },
+ })
+ );
+ });
+
+ it("allows middleware to modify nested update action to be an updateMany", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "update" && params.model === "Post") {
+ return next({
+ ...params,
+ action: "updateMany",
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ update: {
+ where: { id: faker.datatype.number() },
+ data: {
+ title: faker.lorem.sentence(),
+ },
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ updateMany: params.args.data.posts.update,
+ })
+ );
+ });
+
+ it("allows middleware to modify nested update action to be an upsert", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "update" && params.model === "Post") {
+ return next({
+ ...params,
+ action: "upsert",
+ args: {
+ where: { id: params.args.where.id },
+ create: params.args.data,
+ update: params.args.data,
+ },
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ update: {
+ where: { id: faker.datatype.number() },
+ data: {
+ title: faker.lorem.sentence(),
+ },
+ },
+ },
+ },
+ });
+ const updateId = params.args.data.posts.update.where.id;
+ const updateData = params.args.data.posts.update.data;
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ upsert: {
+ where: { id: updateId },
+ create: updateData,
+ update: updateData,
+ },
+ })
+ );
+ });
+
+ it("allows middleware to modify nested update action to be a delete", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "update" && params.model === "Post") {
+ return next({
+ ...params,
+ action: "delete",
+ args: {
+ where: { id: params.args.where.id },
+ },
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ update: {
+ where: { id: faker.datatype.number() },
+ data: {
+ title: faker.lorem.sentence(),
+ },
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ delete: {
+ where: params.args.data.posts.update.where,
+ },
+ })
+ );
+ });
+
+ it("allows middleware to modify nested update action to be a deleteMany", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "update" && params.model === "Post") {
+ return next({
+ ...params,
+ action: "deleteMany",
+ args: {
+ where: { id: params.args.where.id },
+ },
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ update: {
+ where: { id: faker.datatype.number() },
+ data: {
+ title: faker.lorem.sentence(),
+ },
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ deleteMany: {
+ where: params.args.data.posts.update.where,
+ },
+ })
+ );
+ });
+ });
+
+ describe("upsert", () => {
+ it("allows middleware to modify nested upsert action to be a create", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "upsert") {
+ return next({
+ ...params,
+ action: "create",
+ args: params.args.create,
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ upsert: {
+ where: { id: faker.datatype.number() },
+ create: {
+ title: faker.lorem.sentence(),
+ },
+ update: {
+ title: faker.lorem.sentence(),
+ },
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ create: params.args.data.posts.upsert.create,
+ })
+ );
+ });
+
+ it("allows middleware to modify nested upsert action array to be a create array", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "upsert") {
+ return next({
+ ...params,
+ action: "create",
+ args: params.args.create,
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ upsert: [
+ {
+ where: { id: faker.datatype.number() },
+ create: { title: faker.lorem.sentence() },
+ update: { title: faker.lorem.sentence() },
+ },
+ {
+ where: { id: faker.datatype.number() },
+ create: { title: faker.lorem.sentence() },
+ update: { title: faker.lorem.sentence() },
+ },
+ ],
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ create: params.args.data.posts.upsert.map((u: any) => u.create),
+ })
+ );
+ });
+
+ it("allows middleware to modify nested upsert action to be a createMany", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "upsert") {
+ return next({
+ ...params,
+ action: "createMany",
+ args: { data: [params.args.create] },
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ upsert: [
+ {
+ where: { id: faker.datatype.number() },
+ create: { title: faker.lorem.sentence() },
+ update: { title: faker.lorem.sentence() },
+ },
+ {
+ where: { id: faker.datatype.number() },
+ create: { title: faker.lorem.sentence() },
+ update: { title: faker.lorem.sentence() },
+ },
+ ],
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ createMany: {
+ data: params.args.data.posts.upsert.map(
+ ({ create }: any) => create
+ ),
+ },
+ })
+ );
+ });
+
+ it("allows middleware to modify nested upsert action to be a update", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "upsert") {
+ return next({
+ ...params,
+ action: "update",
+ args: { where: params.args.where, data: params.args.update },
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ upsert: [
+ {
+ where: { id: faker.datatype.number() },
+ create: { title: faker.lorem.sentence() },
+ update: { title: faker.lorem.sentence() },
+ },
+ {
+ where: { id: faker.datatype.number() },
+ create: { title: faker.lorem.sentence() },
+ update: { title: faker.lorem.sentence() },
+ },
+ ],
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ update: params.args.data.posts.upsert.map(
+ ({ where, update }: any) => ({ where, data: update })
+ ),
+ })
+ );
+ });
+
+ it("allows middleware to modify nested upsert action to be a updateMany", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "upsert") {
+ return next({
+ ...params,
+ action: "updateMany",
+ args: { where: params.args.where, data: params.args.update },
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ upsert: [
+ {
+ where: { id: faker.datatype.number() },
+ create: { title: faker.lorem.sentence() },
+ update: { title: faker.lorem.sentence() },
+ },
+ {
+ where: { id: faker.datatype.number() },
+ create: { title: faker.lorem.sentence() },
+ update: { title: faker.lorem.sentence() },
+ },
+ ],
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ updateMany: params.args.data.posts.upsert.map(
+ ({ where, update }: any) => ({ where, data: update })
+ ),
+ })
+ );
+ });
+
+ it("allows middleware to modify nested upsert action to be a connectOrCreate", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "upsert") {
+ return next({
+ ...params,
+ action: "connectOrCreate",
+ args: { where: params.args.where, create: params.args.create },
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ upsert: [
+ {
+ where: { id: faker.datatype.number() },
+ create: { title: faker.lorem.sentence() },
+ update: { title: faker.lorem.sentence() },
+ },
+ {
+ where: { id: faker.datatype.number() },
+ create: { title: faker.lorem.sentence() },
+ update: { title: faker.lorem.sentence() },
+ },
+ ],
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ connectOrCreate: params.args.data.posts.upsert.map(
+ ({ where, create }: any) => ({ where, create })
+ ),
+ })
+ );
+ });
+ });
+
+ describe("createMany", () => {
+ it("allows middleware to modify nested createMany action to be a create list", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "createMany") {
+ return next(
+ params.args.data.map((data: any) => ({
+ ...params,
+ action: "create",
+ args: data,
+ }))
+ );
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ createMany: {
+ data: [
+ { title: faker.lorem.sentence() },
+ { title: faker.lorem.sentence() },
+ ],
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ create: params.args.data.posts.createMany.data,
+ })
+ );
+ });
+
+ it("allows middleware to modify nested createMany action to be an update list", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "createMany") {
+ return next(
+ params.args.data.map((data: any) => ({
+ ...params,
+ action: "update",
+ args: { where: { id: data.id }, data },
+ }))
+ );
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ createMany: {
+ data: [
+ { id: 1, title: faker.lorem.sentence() },
+ { id: 2, title: faker.lorem.sentence() },
+ ],
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ update: params.args.data.posts.createMany.data.map((data: any) => ({
+ where: { id: data.id },
+ data,
+ })),
+ })
+ );
+ });
+
+ it("allows middleware to modify nested createMany action to be an upsert list", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "createMany") {
+ return next(
+ params.args.data.map((data: any) => ({
+ ...params,
+ action: "upsert",
+ args: { where: { id: data.id }, create: data, update: data },
+ }))
+ );
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ createMany: {
+ data: [
+ { id: 1, title: faker.lorem.sentence() },
+ { id: 2, title: faker.lorem.sentence() },
+ ],
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ upsert: params.args.data.posts.createMany.data.map((data: any) => ({
+ where: { id: data.id },
+ create: data,
+ update: data,
+ })),
+ })
+ );
+ });
+
+ it("allows middleware to modify nested createMany action to be an connect list", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "createMany") {
+ return next(
+ params.args.data.map((data: any) => ({
+ ...params,
+ action: "connect",
+ args: { id: data.id },
+ }))
+ );
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ createMany: {
+ data: [
+ { id: 1, title: faker.lorem.sentence() },
+ { id: 2, title: faker.lorem.sentence() },
+ ],
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ connect: params.args.data.posts.createMany.data.map((data: any) => ({
+ id: data.id,
+ })),
+ })
+ );
+ });
+
+ it("allows middleware to modify nested createMany action to be a connectOrCreate list", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "createMany") {
+ return next(
+ params.args.data.map((data: any) => ({
+ ...params,
+ action: "connectOrCreate",
+ args: { where: { id: data.id }, create: data },
+ }))
+ );
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ createMany: {
+ data: [
+ { id: 1, title: faker.lorem.sentence() },
+ { id: 2, title: faker.lorem.sentence() },
+ ],
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ connectOrCreate: params.args.data.posts.createMany.data.map(
+ (data: any) => ({
+ where: { id: data.id },
+ create: data,
+ })
+ ),
+ })
+ );
+ });
+ });
+
+ describe("updateMany", () => {});
+
+ describe("delete", () => {
+ it("allows middleware to modify nested delete action to be an update", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "delete") {
+ return next({
+ ...params,
+ action: "update",
+ args: {
+ where: { id: params.args.id },
+ data: { deleted: true },
+ },
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ delete: { id: faker.datatype.number() },
+ },
+ },
+ });
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ update: {
+ where: { id: params.args.data.posts.delete.id },
+ data: { deleted: true },
+ },
+ })
+ );
+ });
+
+ it("allows middleware to modify nested delete action list to be an update list", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "delete") {
+ return next({
+ ...params,
+ action: "update",
+ args: {
+ where: { id: params.args.id },
+ data: { deleted: true },
+ },
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: {
+ id: faker.datatype.number(),
+ },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ delete: [
+ { id: faker.datatype.number() },
+ { id: faker.datatype.number() },
+ ],
+ },
+ },
+ });
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ update: params.args.data.posts.delete.map((del: any) => ({
+ where: del,
+ data: { deleted: true },
+ })),
+ })
+ );
+ });
+
+ it("allows middleware to modify nested boolean delete action to be an update", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "delete" && params.args === true) {
+ return next({
+ ...params,
+ action: "update",
+ args: {
+ ...params.args,
+ deleted: true,
+ },
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: {
+ id: faker.datatype.number(),
+ },
+ data: {
+ email: faker.internet.email(),
+ profile: {
+ delete: true,
+ },
+ },
+ });
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.profile", { update: { deleted: true } })
+ );
+ });
+
+ it("allows middleware to modify deeply nested delete action to be an update", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "delete") {
+ return next({
+ ...params,
+ action: "update",
+ args: {
+ where: { id: params.args.id },
+ data: { deleted: true },
+ },
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ update: {
+ where: { id: faker.datatype.number() },
+ data: {
+ title: faker.lorem.sentence(),
+ comments: {
+ delete: { id: faker.datatype.number() },
+ },
+ },
+ },
+ },
+ },
+ });
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts.update.data.comments", {
+ update: {
+ where: params.args.data.posts.update.data.comments.delete,
+ data: { deleted: true },
+ },
+ })
+ );
+ });
+
+ it("allows middleware to modify deeply nested delete action to be an updateMany", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "delete") {
+ return next({
+ ...params,
+ action: "updateMany",
+ args: {
+ where: { id: params.args.id },
+ data: { deleted: true },
+ },
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ update: {
+ where: { id: faker.datatype.number() },
+ data: {
+ title: faker.lorem.sentence(),
+ comments: {
+ delete: [
+ { id: faker.datatype.number() },
+ { id: faker.datatype.number() },
+ ],
+ },
+ },
+ },
+ },
+ },
+ });
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts.update.data.comments", {
+ updateMany: params.args.data.posts.update.data.comments.delete.map(
+ (del: any) => ({
+ where: del,
+ data: { deleted: true },
+ })
+ ),
+ })
+ );
+ });
+
+ it("allows middleware to modify deeply nested delete action list to be a deleteMany", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "delete") {
+ return next({
+ ...params,
+ action: "deleteMany",
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ update: {
+ where: { id: faker.datatype.number() },
+ data: {
+ title: faker.lorem.sentence(),
+ comments: {
+ delete: [
+ { id: faker.datatype.number() },
+ { id: faker.datatype.number() },
+ ],
+ },
+ },
+ },
+ },
+ },
+ });
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts.update.data.comments", {
+ deleteMany: params.args.data.posts.update.data.comments.delete,
+ })
+ );
+ });
+ });
+
+ describe("deleteMany", () => {
+ it("allows middleware to modify nested deleteMany action to be a delete", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "deleteMany") {
+ return next(set(params, "action", "delete"));
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ delete: [
+ {
+ id: faker.datatype.number(),
+ },
+ ],
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ delete: params.args.data.posts.delete,
+ })
+ );
+ });
+
+ it("allows middleware to modify nested deleteMany action to be an update", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "deleteMany") {
+ return next({
+ ...params,
+ action: "update",
+ args: {
+ where: params.args,
+ data: { deleted: true },
+ },
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ deleteMany: [
+ { id: faker.datatype.number() },
+ { id: faker.datatype.number() },
+ ],
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ update: params.args.data.posts.deleteMany.map((del: any) => ({
+ where: del,
+ data: { deleted: true },
+ })),
+ })
+ );
+ });
+
+ it("allows middleware to modify nested deleteMany action to be an updateMany", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "deleteMany") {
+ return next({
+ ...params,
+ action: "updateMany",
+ args: {
+ where: params.args,
+ data: { deleted: true },
+ },
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ deleteMany: [
+ { id: faker.datatype.number() },
+ { id: faker.datatype.number() },
+ ],
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ updateMany: params.args.data.posts.deleteMany.map((del: any) => ({
+ where: del,
+ data: { deleted: true },
+ })),
+ })
+ );
+ });
+ });
+
+ describe("connect", () => {
+ it("allows middleware to modify nested connect action to be a create", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "connect") {
+ return next({
+ ...params,
+ action: "create",
+ });
+ }
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ connect: {
+ id: faker.datatype.number(),
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ create: params.args.data.posts.connect,
+ })
+ );
+ });
+
+ it("allows middleware to modify nested connect action to be a createMany", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "connect") {
+ return next({
+ ...params,
+ action: "createMany",
+ args: {
+ data: [params.args],
+ },
+ });
+ }
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ connect: {
+ id: faker.datatype.number(),
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ createMany: {
+ data: [params.args.data.posts.connect],
+ },
+ })
+ );
+ });
+
+ it("allows middleware to modify nested connect action to be an update", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "connect") {
+ return next({
+ ...params,
+ action: "update",
+ args: {
+ where: { id: params.args.id },
+ data: params.args,
+ },
+ });
+ }
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ connect: {
+ id: faker.datatype.number(),
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ update: {
+ where: { id: params.args.data.posts.connect.id },
+ data: params.args.data.posts.connect,
+ },
+ })
+ );
+ });
+
+ it("allows middleware to modify nested connect action to be an updateMany", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "connect") {
+ return next({
+ ...params,
+ action: "updateMany",
+ args: {
+ where: { id: params.args.id },
+ data: params.args,
+ },
+ });
+ }
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ connect: {
+ id: faker.datatype.number(),
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ updateMany: {
+ where: { id: params.args.data.posts.connect.id },
+ data: params.args.data.posts.connect,
+ },
+ })
+ );
+ });
+
+ it("allows middleware to modify nested connect action to be an upsert", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "connect") {
+ return next({
+ ...params,
+ action: "upsert",
+ args: {
+ where: { id: params.args.id },
+ update: params.args,
+ create: params.args,
+ },
+ });
+ }
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ connect: {
+ id: faker.datatype.number(),
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ upsert: {
+ where: { id: params.args.data.posts.connect.id },
+ create: params.args.data.posts.connect,
+ update: params.args.data.posts.connect,
+ },
+ })
+ );
+ });
+
+ it("allows middleware to modify nested connect action to be an connectOrCreate", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "connect") {
+ return next({
+ ...params,
+ action: "connectOrCreate",
+ args: {
+ where: { id: params.args.id },
+ create: params.args,
+ },
+ });
+ }
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ connect: {
+ id: faker.datatype.number(),
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ connectOrCreate: {
+ where: { id: params.args.data.posts.connect.id },
+ create: params.args.data.posts.connect,
+ },
+ })
+ );
+ });
+ });
+
+ describe("connectOrCreate", () => {
+ it("allows middleware to modify nested connectOrCreate action to be a create", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "connectOrCreate") {
+ return next({
+ ...params,
+ action: "create",
+ args: params.args.create,
+ });
+ }
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ connectOrCreate: {
+ where: { id: faker.datatype.number() },
+ create: {
+ id: faker.datatype.number(),
+ title: faker.lorem.sentence(),
+ },
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ create: params.args.data.posts.connectOrCreate.create,
+ })
+ );
+ });
+
+ it("allows middleware to modify nested connectOrCreate action to be a createMany", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "connectOrCreate") {
+ return next({
+ ...params,
+ action: "createMany",
+ args: {
+ data: [params.args.create],
+ },
+ });
+ }
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ connectOrCreate: {
+ where: { id: faker.datatype.number() },
+ create: {
+ id: faker.datatype.number(),
+ title: faker.lorem.sentence(),
+ },
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ createMany: {
+ data: [params.args.data.posts.connectOrCreate.create],
+ },
+ })
+ );
+ });
+
+ it("allows middleware to modify nested connectOrCreate action to be an update", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "connectOrCreate") {
+ return next({
+ ...params,
+ action: "update",
+ args: {
+ where: params.args.where,
+ data: params.args.create,
+ },
+ });
+ }
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ connectOrCreate: {
+ where: { id: faker.datatype.number() },
+ create: {
+ id: faker.datatype.number(),
+ title: faker.lorem.sentence(),
+ },
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ update: {
+ where: params.args.data.posts.connectOrCreate.where,
+ data: params.args.data.posts.connectOrCreate.create,
+ },
+ })
+ );
+ });
+
+ it("allows middleware to modify nested connectOrCreate action to be an updateMany", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "connectOrCreate") {
+ return next({
+ ...params,
+ action: "updateMany",
+ args: {
+ where: params.args.where,
+ data: params.args.create,
+ },
+ });
+ }
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ connectOrCreate: {
+ where: { id: faker.datatype.number() },
+ create: {
+ id: faker.datatype.number(),
+ title: faker.lorem.sentence(),
+ },
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ updateMany: {
+ where: params.args.data.posts.connectOrCreate.where,
+ data: params.args.data.posts.connectOrCreate.create,
+ },
+ })
+ );
+ });
+
+ it("allows middleware to modify nested connectOrCreate action to be an upsert", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "connectOrCreate") {
+ return next({
+ ...params,
+ action: "upsert",
+ args: {
+ where: params.args.where,
+ create: params.args.create,
+ update: params.args.create,
+ },
+ });
+ }
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ connectOrCreate: {
+ where: { id: faker.datatype.number() },
+ create: {
+ id: faker.datatype.number(),
+ title: faker.lorem.sentence(),
+ },
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ upsert: {
+ where: params.args.data.posts.connectOrCreate.where,
+ create: params.args.data.posts.connectOrCreate.create,
+ update: params.args.data.posts.connectOrCreate.create,
+ },
+ })
+ );
+ });
+
+ it("allows middleware to modify nested connectOrCreate action to be an connect", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "connectOrCreate") {
+ return next({
+ ...params,
+ action: "connect",
+ args: params.args.where,
+ });
+ }
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ connectOrCreate: {
+ where: { id: faker.datatype.number() },
+ create: {
+ id: faker.datatype.number(),
+ title: faker.lorem.sentence(),
+ },
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ connect: params.args.data.posts.connectOrCreate.where,
+ })
+ );
+ });
+ });
+
+ describe("include", () => {
+ it("allows middleware to modify nested include action to be a select", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "include") {
+ return next({
+ ...params,
+ action: "select",
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "findUnique", {
+ where: { id: faker.datatype.number() },
+ include: {
+ posts: true,
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith({
+ ...params,
+ args: {
+ where: params.args.where,
+ select: {
+ posts: true,
+ },
+ },
+ });
+ });
+ });
+
+ describe("select", () => {
+ it("allows middleware to modify nested select action to be an include", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "select") {
+ return next({
+ ...params,
+ action: "include",
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "findUnique", {
+ where: { id: faker.datatype.number() },
+ include: {
+ posts: {
+ select: {
+ author: true,
+ },
+ },
+ },
+ });
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.include.posts", { include: { author: true } })
+ );
+ });
+ });
+
+ describe("merging", () => {
+ it("merges converted write action args with existing write action args", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "delete") {
+ return next({
+ ...params,
+ action: "update",
+ args: {
+ where: { id: params.args.id },
+ data: { deleted: true },
+ },
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: {
+ id: faker.datatype.number(),
+ },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ update: {
+ where: { id: faker.datatype.number() },
+ data: { title: faker.lorem.sentence() },
+ },
+ delete: {
+ id: faker.datatype.number(),
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ update: [
+ {
+ where: { id: params.args.data.posts.update.where.id },
+ data: { title: params.args.data.posts.update.data.title },
+ },
+ {
+ where: { id: params.args.data.posts.delete.id },
+ data: { deleted: true },
+ },
+ ],
+ })
+ );
+ });
+
+ it("merges converted write array args with existing write action args", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "delete") {
+ return next({
+ ...params,
+ action: "update",
+ args: {
+ where: { id: params.args.id },
+ data: { deleted: true },
+ },
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: {
+ id: faker.datatype.number(),
+ },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ update: {
+ where: { id: faker.datatype.number() },
+ data: { title: faker.lorem.sentence() },
+ },
+ delete: [
+ { id: faker.datatype.number() },
+ { id: faker.datatype.number() },
+ ],
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ update: [
+ {
+ where: { id: params.args.data.posts.update.where.id },
+ data: params.args.data.posts.update.data,
+ },
+ {
+ where: { id: params.args.data.posts.delete[0].id },
+ data: { deleted: true },
+ },
+ {
+ where: { id: params.args.data.posts.delete[1].id },
+ data: { deleted: true },
+ },
+ ],
+ })
+ );
+ });
+
+ it("merges converted write args with existing write action array args", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "delete") {
+ return next({
+ ...params,
+ action: "update",
+ args: {
+ where: { id: params.args.id },
+ data: { deleted: true },
+ },
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ update: [
+ {
+ where: { id: faker.datatype.number() },
+ data: { title: faker.lorem.sentence() },
+ },
+ ],
+ delete: { id: faker.datatype.number() },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ update: [
+ {
+ where: { id: params.args.data.posts.update[0].where.id },
+ data: params.args.data.posts.update[0].data,
+ },
+ {
+ where: { id: params.args.data.posts.delete.id },
+ data: { deleted: true },
+ },
+ ],
+ })
+ );
+ });
+
+ it("merges converted write array args with existing write action array args", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "delete") {
+ return next({
+ ...params,
+ action: "update",
+ args: {
+ where: { id: params.args.id },
+ data: { deleted: true },
+ },
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: {
+ id: faker.datatype.number(),
+ },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ update: [
+ {
+ where: { id: faker.datatype.number() },
+ data: { title: faker.lorem.sentence() },
+ },
+ ],
+ delete: [
+ { id: faker.datatype.number() },
+ { id: faker.datatype.number() },
+ ],
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ update: [
+ {
+ where: { id: params.args.data.posts.update[0].where.id },
+ data: params.args.data.posts.update[0].data,
+ },
+ {
+ where: { id: params.args.data.posts.delete[0].id },
+ data: { deleted: true },
+ },
+ {
+ where: { id: params.args.data.posts.delete[1].id },
+ data: { deleted: true },
+ },
+ ],
+ })
+ );
+ });
+
+ it("merges converted write args with existing write action args when nested in action array", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "delete") {
+ return next({
+ ...params,
+ action: "update",
+ args: {
+ where: { id: params.args.id },
+ data: { deleted: true },
+ },
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: {
+ id: faker.datatype.number(),
+ },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ update: [
+ {
+ where: { id: faker.datatype.number() },
+ data: {
+ title: faker.lorem.sentence(),
+ comments: {
+ update: {
+ where: { id: faker.datatype.number() },
+ data: { content: "test comment content" },
+ },
+ delete: { id: faker.datatype.number() },
+ },
+ },
+ },
+ ],
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts.update.0.data.comments", {
+ update: [
+ params.args.data.posts.update[0].data.comments.update,
+ {
+ where: params.args.data.posts.update[0].data.comments.delete,
+ data: { deleted: true },
+ },
+ ],
+ })
+ );
+ });
+
+ it("merges operations converted to createMany with existing createMany", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "upsert") {
+ return next({
+ ...params,
+ action: "createMany",
+ args: {
+ data: [
+ {
+ title: params.args.create.title,
+ number: params.args.create.title.includes("first") ? 1 : 2,
+ },
+ ],
+ },
+ });
+ }
+
+ if (params.action === "create") {
+ return next({
+ ...params,
+ action: "createMany",
+ args: {
+ data: [
+ {
+ title: params.args.title,
+ number: params.args.title.includes("first") ? 1 : 2,
+ },
+ ],
+ },
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ create: [{ title: "first-create" }, { title: "second-create" }],
+ createMany: {
+ data: [{ title: "pre-existing" }],
+ },
+ upsert: [
+ {
+ where: { id: faker.datatype.number() },
+ create: { title: "first-upsert" },
+ update: { title: "first-upsert" },
+ },
+ {
+ where: { id: faker.datatype.number() },
+ create: { title: "second-upsert" },
+ update: { title: "second-upsert" },
+ },
+ ],
+ },
+ },
+ });
+ await nestedMiddleware(params, next);
+
+ // spread data here as a fix to: https://github.com/facebook/jest/issues/8475
+ expect(next).toHaveBeenCalledTimes(1);
+ expect([
+ ...next.mock.calls[0][0].args.data.posts.createMany.data,
+ ]).toEqual([
+ { title: "pre-existing" },
+ { title: "first-create", number: 1 },
+ { title: "second-create", number: 2 },
+ { title: "first-upsert", number: 1 },
+ { title: "second-upsert", number: 2 },
+ ]);
+ });
+
+ it("merges operations converted to upsert action on toOne relation with existing upsert action", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "create" && params.scope) {
+ return next({
+ ...params,
+ action: "upsert",
+ args: {
+ create: params.args,
+ },
+ });
+ }
+ if (params.action === "update" && params.scope) {
+ return next({
+ ...params,
+ action: "upsert",
+ args: {
+ update: params.args,
+ },
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ profile: {
+ create: { bio: faker.lorem.sentence(), age: 20 },
+ update: { bio: faker.lorem.sentence(), age: 30 },
+ upsert: {
+ create: { bio: faker.lorem.sentence() },
+ update: { bio: faker.lorem.sentence(), age: 40 },
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.profile", {
+ upsert: {
+ create: {
+ ...params.args.data.profile.upsert.create,
+ ...params.args.data.profile.create,
+ },
+ update: {
+ ...params.args.data.profile.upsert.update,
+ ...params.args.data.profile.update,
+ },
+ },
+ })
+ );
+ });
+
+ it("merges operations converted to create action on toOne relation with existing create action", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "upsert") {
+ return next({
+ ...params,
+ action: "create",
+ args: params.args.create,
+ });
+ }
+ if (params.action === "update" && params.scope) {
+ return next({
+ ...params,
+ action: "create",
+ args: params.args,
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ profile: {
+ update: { bio: faker.lorem.sentence(), age: 30 },
+ create: { bio: faker.lorem.sentence(), age: 20 },
+ upsert: {
+ create: { bio: faker.lorem.sentence() },
+ update: { bio: faker.lorem.sentence(), age: 40 },
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.profile", {
+ create: {
+ ...params.args.data.profile.create,
+ ...params.args.data.profile.update,
+ ...params.args.data.profile.upsert.create,
+ },
+ })
+ );
+ });
+
+ it("merges operations converted to update action on toOne relation with existing update action", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "upsert") {
+ return next({
+ ...params,
+ action: "update",
+ args: params.args.update,
+ });
+ }
+
+ if (params.action === "create" && params.scope) {
+ return next({
+ ...params,
+ action: "update",
+ args: params.args.data,
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ profile: {
+ create: { bio: faker.lorem.sentence(), age: 20 },
+ update: { bio: faker.lorem.sentence(), age: 30 },
+ upsert: {
+ create: { bio: faker.lorem.sentence() },
+ update: { bio: faker.lorem.sentence(), age: 40 },
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.profile", {
+ update: {
+ ...params.args.data.profile.update,
+ ...params.args.data.profile.create,
+ ...params.args.data.profile.upsert.update,
+ },
+ })
+ );
+ });
+
+ it("merges connect action on toOne relation with existing connect action", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "create") {
+ return next({
+ ...params,
+ action: "connect",
+ args: {
+ id: params.args.id,
+ },
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ profile: {
+ create: { id: faker.datatype.number() },
+ connect: { id: faker.datatype.number() },
+ },
+ },
+ });
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.profile", {
+ connect: { id: params.args.data.profile.create.id },
+ })
+ );
+ });
+
+ it("merges connectOrCreate action on toOne relation with existing connectOrCreate action", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "create") {
+ return next({
+ ...params,
+ action: "connectOrCreate",
+ args: {
+ create: params.args,
+ },
+ });
+ }
+
+ if (params.action === "update" && params.scope) {
+ return next({
+ ...params,
+ action: "connectOrCreate",
+ args: {
+ where: params.args,
+ },
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ profile: {
+ create: {
+ bio: faker.lorem.sentence(),
+ age: 20,
+ },
+ connectOrCreate: {
+ where: { id: faker.datatype.number() },
+ create: { bio: faker.lorem.sentence() },
+ },
+ update: {
+ id: faker.datatype.number(),
+ },
+ },
+ },
+ });
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.profile", {
+ connectOrCreate: {
+ where: params.args.data.profile.update,
+ create: {
+ ...params.args.data.profile.connectOrCreate.create,
+ ...params.args.data.profile.create,
+ },
+ },
+ })
+ );
+ });
+
+ it("merges disconnect where action with existing disconnect action", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "upsert") {
+ return next({
+ ...params,
+ action: "disconnect",
+ args: params.args.where,
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ disconnect: {
+ id: faker.datatype.number(),
+ },
+ upsert: [
+ {
+ where: { id: faker.datatype.number() },
+ create: { title: "first-upsert" },
+ update: { title: "first-upsert" },
+ },
+ {
+ where: { id: faker.datatype.number() },
+ create: { title: "second-upsert" },
+ update: { title: "second-upsert" },
+ },
+ ],
+ },
+ },
+ });
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.posts", {
+ disconnect: [
+ params.args.data.posts.disconnect,
+ get(params, "args.data.posts.upsert.0.where"),
+ get(params, "args.data.posts.upsert.1.where"),
+ ],
+ })
+ );
+ });
+
+ it("replaces existing disconnect true action with new disconnect action", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "update" && params.scope) {
+ return next({
+ ...params,
+ action: "disconnect",
+ args: false,
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ profile: {
+ disconnect: true,
+ update: {
+ bio: faker.lorem.sentence(),
+ },
+ },
+ },
+ });
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.profile", { disconnect: false })
+ );
+ });
+
+ it("replaces existing disconnect false action with new disconnect action", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "update" && params.scope) {
+ return next({
+ ...params,
+ action: "disconnect",
+ args: true,
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ profile: {
+ disconnect: false,
+ update: {
+ bio: faker.lorem.sentence(),
+ },
+ },
+ },
+ });
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.profile", { disconnect: true })
+ );
+ });
+
+ it("replaces existing delete true action with new delete action", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "update" && params.scope) {
+ return next({
+ ...params,
+ action: "delete",
+ args: true,
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ profile: {
+ delete: false,
+ update: {
+ bio: faker.lorem.sentence(),
+ },
+ },
+ },
+ });
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.profile", { delete: true })
+ );
+ });
+
+ it("replaces existing delete false action with new delete action", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "update" && params.scope) {
+ return next({
+ ...params,
+ action: "delete",
+ args: false,
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ profile: {
+ delete: true,
+ update: {
+ bio: faker.lorem.sentence(),
+ },
+ },
+ },
+ });
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.data.profile", { delete: false })
+ );
+ });
+
+ it("replaces existing include with select changed to include", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "select") {
+ return next({
+ ...params,
+ action: "include",
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "findUnique", {
+ where: { id: faker.datatype.number() },
+ include: {
+ posts: {
+ select: { deleted: true },
+ include: { author: true },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.include.posts", {
+ include: { deleted: true },
+ })
+ );
+ });
+ });
+});
diff --git a/test/args.test.ts b/test/unit/args.test.ts
similarity index 64%
rename from test/args.test.ts
rename to test/unit/args.test.ts
index 5a8a1f5..55c5c9f 100644
--- a/test/args.test.ts
+++ b/test/unit/args.test.ts
@@ -1,10 +1,32 @@
import faker from "faker";
+import { set } from "lodash";
-import { createNestedMiddleware } from "../src";
-import { createParams } from "./utils/createParams";
-import { wait } from "./utils/wait";
+import { createNestedMiddleware } from "../../src";
+import { createParams } from "./helpers/createParams";
+import { wait } from "./helpers/wait";
describe("params", () => {
+ it("does not mutate passed params object", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ params.args.test = "test";
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "create", {
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ create: { title: faker.lorem.sentence() },
+ },
+ },
+ });
+ await nestedMiddleware(params, next);
+
+ expect(params.args).not.toHaveProperty("test");
+ expect(params.args.data.posts.create).not.toHaveProperty("test");
+ });
+
it("allows middleware to modify root args", async () => {
const nestedMiddleware = createNestedMiddleware((params, next) => {
return next({
@@ -75,10 +97,8 @@ describe("params", () => {
return next({
...params,
args: {
- data: {
- ...params.args.data,
- number: faker.datatype.number(),
- },
+ ...params.args,
+ number: faker.datatype.number(),
},
});
}
@@ -120,10 +140,8 @@ describe("params", () => {
return next({
...params,
args: {
- data: {
- ...params.args.data,
- number: faker.datatype.number(),
- },
+ ...params.args,
+ number: faker.datatype.number(),
},
});
}
@@ -164,10 +182,8 @@ describe("params", () => {
return next({
...params,
args: {
- data: {
- ...params.args.data,
- number: params.args.data.title === "first" ? 1 : 2,
- },
+ ...params.args,
+ number: params.args.title === "first" ? 1 : 2,
},
});
}
@@ -202,6 +218,90 @@ describe("params", () => {
});
});
+ it("allows middleware to modify deeply nested toOne update args", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.model === "Comment") {
+ if (params.scope && !params.scope.relations.to.isList) {
+ return next({
+ ...params,
+ args: { ...params.args, number: parseInt(params.args.content, 10) },
+ });
+ }
+
+ return next({
+ ...params,
+ args: {
+ ...params.args,
+ data: {
+ ...params.args.data,
+ number: parseInt(params.args.data.content, 10),
+ },
+ },
+ });
+ }
+
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ comments: {
+ update: {
+ where: { id: faker.datatype.number() },
+ data: {
+ content: "1",
+ repliedTo: {
+ update: {
+ content: "2",
+ repliedTo: {
+ update: {
+ content: "3",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ });
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith({
+ ...params,
+ args: {
+ ...params.args,
+ data: {
+ ...params.args.data,
+ comments: {
+ update: {
+ where: params.args.data.comments.update.where,
+ data: {
+ content: "1",
+ number: 1,
+ repliedTo: {
+ update: {
+ content: "2",
+ number: 2,
+ repliedTo: {
+ update: {
+ content: "3",
+ number: 3,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ });
+ });
+
it("allows middleware to modify nested update list args", async () => {
const nestedMiddleware = createNestedMiddleware((params, next) => {
if (params.model === "Post") {
@@ -306,10 +406,8 @@ describe("params", () => {
return next({
...params,
args: {
- data: {
- ...params.args.data,
- number: params.args.data.content === "first post comment" ? 1 : 2,
- },
+ ...params.args,
+ number: params.args.content === "first post comment" ? 1 : 2,
},
});
}
@@ -397,9 +495,20 @@ describe("params", () => {
it("allows middleware to modify args of deeply nested lists of create operations", async () => {
const nestedMiddleware = createNestedMiddleware((params, next) => {
if (params.action === "create" && params.model === "Comment") {
+ if (params.scope) {
+ return next({
+ ...params,
+ args: {
+ ...params.args,
+ number: params.args.content === "first post comment" ? 1 : 2,
+ },
+ });
+ }
+
return next({
...params,
args: {
+ ...params.args,
data: {
...params.args.data,
number: params.args.data.content === "first post comment" ? 1 : 2,
@@ -715,6 +824,17 @@ describe("params", () => {
},
});
}
+ if (params.action === "select" && params.model === "Comment") {
+ return next({
+ ...params,
+ args: {
+ ...params.args,
+ select: {
+ content: true,
+ },
+ },
+ });
+ }
return next(params);
});
@@ -1026,6 +1146,444 @@ describe("params", () => {
});
});
+ it("allows user to modify nested where args", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "where" && params.model === "Comment") {
+ return next({
+ ...params,
+ args: {
+ ...params.args,
+ content: "bar",
+ },
+ });
+ }
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "findMany", {
+ where: {
+ posts: {
+ some: {
+ comments: {
+ some: {
+ content: "foo",
+ },
+ },
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.where.posts.some.comments.some.content", "bar")
+ );
+ });
+
+ it("allows user to modify nested where args by removing a field", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "where" && params.model === "Comment") {
+ return next({
+ ...params,
+ args: {
+ // remove content and replace it with updatedAt
+ updatedAt: {
+ gt: new Date(),
+ },
+ },
+ });
+ }
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "findMany", {
+ where: {
+ posts: {
+ some: {
+ comments: {
+ some: {
+ content: "foo",
+ },
+ },
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.where.posts.some.comments.some", {
+ updatedAt: {
+ gt: expect.any(Date),
+ },
+ })
+ );
+ });
+
+ it("allows user to modify nested where args with nested where", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "where" && params.model === "Comment") {
+ return next({
+ ...params,
+ args: {
+ ...params.args,
+ content: {
+ contains: "bar",
+ },
+ },
+ });
+ }
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "findMany", {
+ where: {
+ posts: {
+ some: {
+ comments: {
+ some: {
+ content: "foo",
+ },
+ },
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.where.posts.some.comments.some.content", {
+ contains: "bar",
+ })
+ );
+ });
+
+ it("allows user to modify nested where args with nested where in logical operation", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "where" && params.model === "Comment") {
+ return next({
+ ...params,
+ args: {
+ ...params.args,
+ content: {
+ contains: "bar",
+ },
+ },
+ });
+ }
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "findMany", {
+ where: {
+ posts: {
+ some: {
+ AND: [
+ {
+ author: {
+ id: 1,
+ },
+ },
+ {
+ comments: {
+ some: {
+ content: "foo",
+ },
+ },
+ },
+ ],
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.where.posts.some.AND.1.comments.some.content", {
+ contains: "bar",
+ })
+ );
+ });
+
+ it("allows user to modify where args deeply nested in logical operations", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (
+ params.action === "where" &&
+ params.model === "User" &&
+ params.scope
+ ) {
+ return next({
+ ...params,
+ args: {
+ ...params.args,
+ ...(params.args.id ? { id: params.args.id + 1 } : {}),
+ },
+ });
+ }
+
+ if (params.action === "where" && params.model === "Comment") {
+ return next({
+ ...params,
+ args: {
+ ...params.args,
+ content: "bar",
+ },
+ });
+ }
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "findMany", {
+ where: {
+ posts: {
+ some: {
+ AND: [
+ {
+ NOT: {
+ OR: [
+ {
+ AND: [
+ {
+ NOT: {
+ OR: [
+ {
+ id: 1,
+ author: {
+ id: 2,
+ },
+ },
+ ],
+ },
+ },
+ ],
+ comments: {
+ some: {
+ content: "foo",
+ },
+ },
+ },
+ ],
+ },
+ },
+ ],
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ set(
+ params,
+ "args.where.posts.some.AND.0.NOT.OR.0.AND.0.NOT.OR.0.author.id",
+ 3
+ );
+ set(
+ params,
+ "args.where.posts.some.AND.0.NOT.OR.0.comments.some.content",
+ "bar"
+ );
+
+ expect(next).toHaveBeenCalledWith(params);
+ });
+
+ it("allows user to modify nested include where args", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "where" && params.model === "Post") {
+ return next({
+ ...params,
+ args: {
+ ...params.args,
+ title: "bar",
+ },
+ });
+ }
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "findUnique", {
+ where: { id: 1 },
+ include: {
+ posts: {
+ where: {
+ title: "foo",
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.include.posts.where.title", "bar")
+ );
+ });
+
+ it("allows user to modify nested select where args", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "where" && params.model === "Post") {
+ return next({
+ ...params,
+ args: {
+ ...params.args,
+ title: "bar",
+ },
+ });
+ }
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "findUnique", {
+ where: { id: 1 },
+ select: {
+ posts: {
+ where: {
+ title: "foo",
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(
+ set(params, "args.select.posts.where.title", "bar")
+ );
+ });
+
+ it("allows user to modify nested where relation args in nested include where", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "where" && params.model === "Post") {
+ return next({
+ ...params,
+ args: {
+ ...params.args,
+ title: "bar",
+ },
+ });
+ }
+ if (params.action === "where" && params.model === "Comment") {
+ return next({
+ ...params,
+ args: {
+ ...params.args,
+ content: "bar",
+ },
+ });
+ }
+ if (params.action === "where" && params.model === "User") {
+ return next({
+ ...params,
+ args: {
+ ...params.args,
+ email: "bar",
+ },
+ });
+ }
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "findUnique", {
+ where: { id: 1 },
+ include: {
+ posts: {
+ where: {
+ title: "foo",
+ AND: [
+ { author: { id: 1, email: "foo" } },
+ { comments: { every: { content: "foo" } } },
+ ],
+ OR: [{ NOT: { author: { id: 1, email: "foo" } } }],
+ NOT: { comments: { some: { content: "foo" } } },
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ set(params, "args.include.posts.where.title", "bar");
+ set(params, "args.include.posts.where.AND.0.author.email", "bar");
+ set(params, "args.include.posts.where.AND.1.comments.every.content", "bar");
+ set(params, "args.include.posts.where.OR.0.NOT.author.email", "bar");
+ set(params, "args.include.posts.where.NOT.comments.some.content", "bar");
+
+ expect(next).toHaveBeenCalledWith(params);
+ });
+
+ it("allows user to modify nested where relation args in nested select where", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ if (params.action === "where" && params.model === "Post") {
+ return next({
+ ...params,
+ args: {
+ ...params.args,
+ title: "bar",
+ },
+ });
+ }
+ if (params.action === "where" && params.model === "Comment") {
+ return next({
+ ...params,
+ args: {
+ ...params.args,
+ content: "bar",
+ },
+ });
+ }
+ if (params.action === "where" && params.model === "User") {
+ return next({
+ ...params,
+ args: {
+ ...params.args,
+ email: "bar",
+ },
+ });
+ }
+ return next(params);
+ });
+
+ const next = jest.fn((_: any) => Promise.resolve(null));
+ const params = createParams("User", "findUnique", {
+ where: { id: 1 },
+ select: {
+ posts: {
+ where: {
+ title: "foo",
+ AND: [
+ { author: { id: 1, email: "foo" } },
+ { comments: { every: { content: "foo" } } },
+ ],
+ OR: [{ NOT: { author: { id: 1, email: "foo" } } }],
+ NOT: { comments: { some: { content: "foo" } } },
+ },
+ },
+ },
+ });
+
+ await nestedMiddleware(params, next);
+
+ set(params, "args.select.posts.where.title", "bar");
+ set(params, "args.select.posts.where.AND.0.author.email", "bar");
+ set(params, "args.select.posts.where.AND.1.comments.every.content", "bar");
+ set(params, "args.select.posts.where.OR.0.NOT.author.email", "bar");
+ set(params, "args.select.posts.where.NOT.comments.some.content", "bar");
+
+ expect(next).toHaveBeenCalledWith(params);
+ });
+
it("waits for all middleware to finish before calling next when modifying args", async () => {
const nestedMiddleware = createNestedMiddleware(async (params, next) => {
if (params.model === "Post") {
@@ -1033,10 +1591,8 @@ describe("params", () => {
return next({
...params,
args: {
- data: {
- ...params.args.data,
- number: faker.datatype.number(),
- },
+ ...params.args,
+ number: faker.datatype.number(),
},
});
}
@@ -1046,10 +1602,8 @@ describe("params", () => {
return next({
...params,
args: {
- data: {
- ...params.args.data,
- number: faker.datatype.number(),
- },
+ ...params.args,
+ number: faker.datatype.number(),
},
});
}
diff --git a/test/unit/calls.test.ts b/test/unit/calls.test.ts
new file mode 100644
index 0000000..9b91210
--- /dev/null
+++ b/test/unit/calls.test.ts
@@ -0,0 +1,4089 @@
+import { Prisma } from "@prisma/client";
+import faker from "faker";
+import { get } from "lodash";
+
+import { createNestedMiddleware, NestedParams } from "../../src";
+import { relationsByModel } from "../../src/lib/utils/relations";
+import { LogicalOperator, Modifier } from "../../src/lib/types";
+import { createParams } from "./helpers/createParams";
+
+type MiddlewareCall = {
+ model: Model;
+ action:
+ | "create"
+ | "update"
+ | "upsert"
+ | "delete"
+ | "createMany"
+ | "updateMany"
+ | "deleteMany"
+ | "connectOrCreate"
+ | "findUnique"
+ | "findFirst"
+ | "findMany"
+ | "include"
+ | "select"
+ | "where";
+ argsPath: string;
+ scope?: MiddlewareCall;
+ relations: {
+ to: Prisma.DMMF.Field;
+ from: Prisma.DMMF.Field;
+ };
+ modifier?: Modifier;
+ logicalOperators?: LogicalOperator[];
+};
+
+function nestedParamsFromCall(
+ rootParams: Prisma.MiddlewareParams,
+ call: MiddlewareCall
+): NestedParams {
+ const params = createParams(
+ call.model,
+ call.action,
+ get(rootParams, call.argsPath)
+ );
+ return {
+ ...params,
+ scope: {
+ modifier: call.modifier,
+ logicalOperators: call.logicalOperators,
+ relations: call.relations,
+ parentParams: call.scope
+ ? nestedParamsFromCall(rootParams, call.scope)
+ : rootParams,
+ },
+ };
+}
+
+function getModelRelation(
+ model: Model,
+ relationName: string
+): Prisma.DMMF.Field {
+ const modelRelation = relationsByModel[model].find(
+ (relation) => relation.name === relationName
+ );
+ if (!modelRelation) {
+ throw new Error(
+ `Unable to find relation ${relationName} on model ${model}`
+ );
+ }
+ return modelRelation;
+}
+
+describe("calls", () => {
+ it("calls middleware once when there are no nested operations", async () => {
+ const middleware = jest.fn((params, next) => next(params));
+ const nestedMiddleware = createNestedMiddleware(middleware);
+
+ const next = jest.fn((params: any) => params);
+ const params = createParams("User", "create", {
+ data: { email: faker.internet.email() },
+ });
+ await nestedMiddleware(params, next);
+
+ // middleware is called with params and next
+ expect(middleware).toHaveBeenCalledTimes(1);
+ expect(middleware).toHaveBeenCalledWith(params, next);
+ });
+
+ it.each<{
+ description: string;
+ rootParams: Prisma.MiddlewareParams;
+ nestedCalls?: MiddlewareCall[];
+ }>([
+ {
+ description: "count",
+ rootParams: createParams("User", "count", undefined),
+ },
+ {
+ description: "aggregate",
+ rootParams: createParams("User", "aggregate", {}),
+ },
+ {
+ description: "nested create in create",
+ rootParams: createParams("User", "create", {
+ data: {
+ email: faker.internet.email(),
+ profile: { create: { bio: faker.lorem.paragraph() } },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "create",
+ model: "Profile",
+ argsPath: "args.data.profile.create",
+ relations: {
+ to: getModelRelation("User", "profile"),
+ from: getModelRelation("Profile", "user"),
+ },
+ },
+ ],
+ },
+ {
+ description: "nested create in update",
+ rootParams: createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ profile: { create: { bio: faker.lorem.paragraph() } },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "create",
+ model: "Profile",
+ argsPath: "args.data.profile.create",
+ relations: {
+ to: getModelRelation("User", "profile"),
+ from: getModelRelation("Profile", "user"),
+ },
+ },
+ ],
+ },
+ {
+ description: "nested creates in upsert",
+ rootParams: createParams("User", "upsert", {
+ where: { id: faker.datatype.number() },
+ create: {
+ email: faker.internet.email(),
+ profile: { create: { bio: faker.lorem.paragraph() } },
+ },
+ update: {
+ email: faker.internet.email(),
+ profile: { create: { bio: faker.lorem.paragraph() } },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "create",
+ model: "Profile",
+ argsPath: "args.create.profile.create",
+ relations: {
+ to: getModelRelation("User", "profile"),
+ from: getModelRelation("Profile", "user"),
+ },
+ },
+ {
+ action: "create",
+ model: "Profile",
+ argsPath: "args.update.profile.create",
+ relations: {
+ to: getModelRelation("User", "profile"),
+ from: getModelRelation("Profile", "user"),
+ },
+ },
+ ],
+ },
+ {
+ description: "nested create array in create",
+ rootParams: createParams("User", "create", {
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ create: [
+ {
+ title: faker.lorem.sentence(),
+ content: faker.lorem.paragraph(),
+ },
+ {
+ title: faker.lorem.sentence(),
+ content: faker.lorem.paragraph(),
+ },
+ ],
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "create",
+ model: "Post",
+ argsPath: "args.data.posts.create.0",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "create",
+ model: "Post",
+ argsPath: "args.data.posts.create.1",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ ],
+ },
+ {
+ description: "nested create array in update",
+ rootParams: createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ create: [
+ {
+ title: faker.lorem.sentence(),
+ content: faker.lorem.paragraph(),
+ },
+ {
+ title: faker.lorem.sentence(),
+ content: faker.lorem.paragraph(),
+ },
+ ],
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "create",
+ model: "Post",
+ argsPath: "args.data.posts.create.0",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "create",
+ model: "Post",
+ argsPath: "args.data.posts.create.1",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ ],
+ },
+ {
+ description: "nested create and update in update",
+ rootParams: createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ profile: {
+ update: { bio: faker.lorem.paragraph() },
+ },
+ posts: {
+ create: [
+ {
+ title: faker.lorem.sentence(),
+ content: faker.lorem.paragraph(),
+ },
+ ],
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "update",
+ model: "Profile",
+ argsPath: "args.data.profile.update",
+ relations: {
+ to: getModelRelation("User", "profile"),
+ from: getModelRelation("Profile", "user"),
+ },
+ },
+ {
+ action: "create",
+ model: "Post",
+ argsPath: "args.data.posts.create.0",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ ],
+ },
+ {
+ description: "nested create array in upsert",
+ rootParams: createParams("User", "upsert", {
+ where: { id: faker.datatype.number() },
+ create: {
+ email: faker.internet.email(),
+ posts: {
+ create: [
+ {
+ title: faker.lorem.sentence(),
+ content: faker.lorem.paragraph(),
+ },
+ {
+ title: faker.lorem.sentence(),
+ content: faker.lorem.paragraph(),
+ },
+ ],
+ },
+ },
+ update: {
+ email: faker.internet.email(),
+ posts: {
+ create: [
+ {
+ title: faker.lorem.sentence(),
+ content: faker.lorem.paragraph(),
+ },
+ {
+ title: faker.lorem.sentence(),
+ content: faker.lorem.paragraph(),
+ },
+ ],
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "create",
+ model: "Post",
+ argsPath: "args.create.posts.create.0",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "create",
+ model: "Post",
+ argsPath: "args.create.posts.create.1",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "create",
+ model: "Post",
+ argsPath: "args.update.posts.create.0",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "create",
+ model: "Post",
+ argsPath: "args.update.posts.create.1",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ ],
+ },
+ {
+ description: "nested update in update",
+ rootParams: createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ profile: { update: { bio: faker.lorem.paragraph() } },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "update",
+ model: "Profile",
+ argsPath: "args.data.profile.update",
+ relations: {
+ to: getModelRelation("User", "profile"),
+ from: getModelRelation("Profile", "user"),
+ },
+ },
+ ],
+ },
+ {
+ description: "nested update in upsert",
+ rootParams: createParams("User", "upsert", {
+ where: { id: faker.datatype.number() },
+ create: {
+ email: faker.internet.email(),
+ },
+ update: {
+ email: faker.internet.email(),
+ profile: { update: { bio: faker.lorem.paragraph() } },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "update",
+ model: "Profile",
+ argsPath: "args.update.profile.update",
+ relations: {
+ to: getModelRelation("User", "profile"),
+ from: getModelRelation("Profile", "user"),
+ },
+ },
+ ],
+ },
+ {
+ description: "nested update array in update",
+ rootParams: createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ update: [
+ {
+ where: { id: faker.datatype.number() },
+ data: {
+ title: faker.lorem.sentence(),
+ content: faker.lorem.paragraph(),
+ },
+ },
+ {
+ where: { id: faker.datatype.number() },
+ data: {
+ title: faker.lorem.sentence(),
+ content: faker.lorem.paragraph(),
+ },
+ },
+ ],
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "update",
+ model: "Post",
+ argsPath: "args.data.posts.update.0",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "update",
+ model: "Post",
+ argsPath: "args.data.posts.update.1",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ ],
+ },
+ {
+ description: "nested update array in upsert",
+ rootParams: createParams("User", "upsert", {
+ where: { id: faker.datatype.number() },
+ create: {
+ email: faker.internet.email(),
+ },
+ update: {
+ email: faker.internet.email(),
+ posts: {
+ update: [
+ {
+ where: { id: faker.datatype.number() },
+ data: {
+ title: faker.lorem.sentence(),
+ content: faker.lorem.paragraph(),
+ },
+ },
+ {
+ where: { id: faker.datatype.number() },
+ data: {
+ title: faker.lorem.sentence(),
+ content: faker.lorem.paragraph(),
+ },
+ },
+ ],
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "update",
+ model: "Post",
+ argsPath: "args.update.posts.update.0",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "update",
+ model: "Post",
+ argsPath: "args.update.posts.update.1",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ ],
+ },
+ {
+ description: "nested upsert in update",
+ rootParams: createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ profile: {
+ upsert: {
+ create: { bio: faker.lorem.paragraph() },
+ update: { bio: faker.lorem.paragraph() },
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "upsert",
+ model: "Profile",
+ argsPath: "args.data.profile.upsert",
+ relations: {
+ to: getModelRelation("User", "profile"),
+ from: getModelRelation("Profile", "user"),
+ },
+ },
+ ],
+ },
+ {
+ description: "nested upsert list in update",
+ rootParams: createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ upsert: [
+ {
+ where: { id: faker.datatype.number() },
+ update: { title: faker.lorem.sentence() },
+ create: {
+ title: faker.lorem.sentence(),
+ content: faker.lorem.paragraph(),
+ },
+ },
+ {
+ where: { id: faker.datatype.number() },
+ update: { title: faker.lorem.sentence() },
+ create: {
+ title: faker.lorem.sentence(),
+ content: faker.lorem.paragraph(),
+ },
+ },
+ ],
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "upsert",
+ model: "Post",
+ argsPath: "args.data.posts.upsert.0",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "upsert",
+ model: "Post",
+ argsPath: "args.data.posts.upsert.1",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ ],
+ },
+ {
+ description: "nested upsert in upsert",
+ rootParams: createParams("User", "upsert", {
+ where: { id: faker.datatype.number() },
+ create: {
+ email: faker.internet.email(),
+ },
+ update: {
+ email: faker.internet.email(),
+ profile: {
+ upsert: {
+ create: { bio: faker.lorem.paragraph() },
+ update: { bio: faker.lorem.paragraph() },
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "upsert",
+ model: "Profile",
+ argsPath: "args.update.profile.upsert",
+ relations: {
+ to: getModelRelation("User", "profile"),
+ from: getModelRelation("Profile", "user"),
+ },
+ },
+ ],
+ },
+ {
+ description: "nested delete in update",
+ rootParams: createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ profile: { delete: true },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "delete",
+ model: "Profile",
+ argsPath: "args.data.profile.delete",
+ relations: {
+ to: getModelRelation("User", "profile"),
+ from: getModelRelation("Profile", "user"),
+ },
+ },
+ ],
+ },
+ {
+ description: "nested delete in upsert",
+ rootParams: createParams("User", "upsert", {
+ where: { id: faker.datatype.number() },
+ create: {
+ email: faker.internet.email(),
+ },
+ update: {
+ email: faker.internet.email(),
+ profile: { delete: true },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "delete",
+ model: "Profile",
+ argsPath: "args.update.profile.delete",
+ relations: {
+ to: getModelRelation("User", "profile"),
+ from: getModelRelation("Profile", "user"),
+ },
+ },
+ ],
+ },
+ {
+ description: "nested delete array in update",
+ rootParams: createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ delete: [
+ { id: faker.datatype.number() },
+ { id: faker.datatype.number() },
+ ],
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "delete",
+ model: "Post",
+ argsPath: "args.data.posts.delete.0",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "delete",
+ model: "Post",
+ argsPath: "args.data.posts.delete.1",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ ],
+ },
+ {
+ description: "nested delete array in upsert",
+ rootParams: createParams("User", "upsert", {
+ where: { id: faker.datatype.number() },
+ create: {
+ email: faker.internet.email(),
+ },
+ update: {
+ email: faker.internet.email(),
+ posts: {
+ delete: [
+ { id: faker.datatype.number() },
+ { id: faker.datatype.number() },
+ ],
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "delete",
+ model: "Post",
+ argsPath: "args.update.posts.delete.0",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "delete",
+ model: "Post",
+ argsPath: "args.update.posts.delete.1",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ ],
+ },
+ {
+ description: "nested createMany in create",
+ rootParams: createParams("User", "create", {
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ createMany: {
+ data: [
+ {
+ title: faker.lorem.sentence(),
+ content: faker.lorem.paragraph(),
+ },
+ {
+ title: faker.lorem.sentence(),
+ content: faker.lorem.paragraph(),
+ },
+ ],
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "createMany",
+ model: "Post",
+ argsPath: "args.data.posts.createMany",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ ],
+ },
+ {
+ description: "nested createMany in update",
+ rootParams: createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ createMany: {
+ data: [
+ {
+ title: faker.lorem.sentence(),
+ content: faker.lorem.paragraph(),
+ },
+ {
+ title: faker.lorem.sentence(),
+ content: faker.lorem.paragraph(),
+ },
+ ],
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "createMany",
+ model: "Post",
+ argsPath: "args.data.posts.createMany",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ ],
+ },
+ {
+ description: "nested createMany in upsert",
+ rootParams: createParams("User", "upsert", {
+ where: { id: faker.datatype.number() },
+ create: {
+ email: faker.internet.email(),
+ },
+ update: {
+ email: faker.internet.email(),
+ posts: {
+ createMany: {
+ data: [
+ {
+ title: faker.lorem.sentence(),
+ content: faker.lorem.paragraph(),
+ },
+ {
+ title: faker.lorem.sentence(),
+ content: faker.lorem.paragraph(),
+ },
+ ],
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "createMany",
+ model: "Post",
+ argsPath: "args.update.posts.createMany",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ ],
+ },
+ {
+ description: "nested updateMany in update",
+ rootParams: createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ updateMany: {
+ data: {
+ title: faker.lorem.sentence(),
+ content: faker.lorem.paragraph(),
+ },
+ where: { id: faker.datatype.number() },
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "updateMany",
+ model: "Post",
+ argsPath: "args.data.posts.updateMany",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ ],
+ },
+
+ {
+ description: "nested updateMany array in update",
+ rootParams: createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ updateMany: [
+ {
+ where: {
+ id: faker.datatype.number(),
+ },
+ data: {
+ title: faker.lorem.sentence(),
+ content: faker.lorem.paragraph(),
+ },
+ },
+ {
+ where: {
+ id: faker.datatype.number(),
+ },
+ data: {
+ title: faker.lorem.sentence(),
+ content: faker.lorem.paragraph(),
+ },
+ },
+ ],
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "updateMany",
+ model: "Post",
+ argsPath: "args.data.posts.updateMany.0",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "updateMany",
+ model: "Post",
+ argsPath: "args.data.posts.updateMany.1",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ ],
+ },
+ {
+ description: "nested updateMany in upsert",
+ rootParams: createParams("User", "upsert", {
+ where: { id: faker.datatype.number() },
+ create: {
+ email: faker.internet.email(),
+ },
+ update: {
+ email: faker.internet.email(),
+ posts: {
+ updateMany: {
+ data: {
+ title: faker.lorem.sentence(),
+ content: faker.lorem.paragraph(),
+ },
+ where: { id: faker.datatype.number() },
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "updateMany",
+ model: "Post",
+ argsPath: "args.update.posts.updateMany",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ ],
+ },
+ {
+ description: "nested updateMany list in upsert",
+ rootParams: createParams("User", "upsert", {
+ where: { id: faker.datatype.number() },
+ create: {
+ email: faker.internet.email(),
+ },
+ update: {
+ email: faker.internet.email(),
+ posts: {
+ updateMany: [
+ {
+ where: { id: faker.datatype.number() },
+ data: { title: faker.lorem.sentence() },
+ },
+ {
+ where: { id: faker.datatype.number() },
+ data: { title: faker.lorem.sentence() },
+ },
+ ],
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "updateMany",
+ model: "Post",
+ argsPath: "args.update.posts.updateMany.0",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "updateMany",
+ model: "Post",
+ argsPath: "args.update.posts.updateMany.1",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ ],
+ },
+ {
+ description: "nested deleteMany in update",
+ rootParams: createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ deleteMany: { id: faker.datatype.number() },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "deleteMany",
+ model: "Post",
+ argsPath: "args.data.posts.deleteMany",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ ],
+ },
+ {
+ description: "nested deleteMany list in update",
+ rootParams: createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ deleteMany: [
+ { id: faker.datatype.number() },
+ { id: faker.datatype.number() },
+ ],
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "deleteMany",
+ model: "Post",
+ argsPath: "args.data.posts.deleteMany.0",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "deleteMany",
+ model: "Post",
+ argsPath: "args.data.posts.deleteMany.1",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ ],
+ },
+ {
+ description: "nested deleteMany in upsert",
+ rootParams: createParams("User", "upsert", {
+ where: { id: faker.datatype.number() },
+ create: {
+ email: faker.internet.email(),
+ },
+ update: {
+ email: faker.internet.email(),
+ posts: {
+ deleteMany: { id: faker.datatype.number() },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "deleteMany",
+ model: "Post",
+ argsPath: "args.update.posts.deleteMany",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ ],
+ },
+ {
+ description: "nested deleteMany list in upsert",
+ rootParams: createParams("User", "upsert", {
+ where: { id: faker.datatype.number() },
+ create: {
+ email: faker.internet.email(),
+ },
+ update: {
+ email: faker.internet.email(),
+ posts: {
+ deleteMany: [
+ { id: faker.datatype.number() },
+ { id: faker.datatype.number() },
+ ],
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "deleteMany",
+ model: "Post",
+ argsPath: "args.update.posts.deleteMany.0",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "deleteMany",
+ model: "Post",
+ argsPath: "args.update.posts.deleteMany.1",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ ],
+ },
+ {
+ description: "nested connectOrCreate in update",
+ rootParams: createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ profile: {
+ connectOrCreate: {
+ where: { id: faker.datatype.number() },
+ create: { bio: faker.lorem.paragraph() },
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "connectOrCreate",
+ model: "Profile",
+ argsPath: "args.data.profile.connectOrCreate",
+ relations: {
+ to: getModelRelation("User", "profile"),
+ from: getModelRelation("Profile", "user"),
+ },
+ },
+ ],
+ },
+ {
+ description: "nested connectOrCreate in upsert",
+ rootParams: createParams("User", "upsert", {
+ where: { id: faker.datatype.number() },
+ create: {
+ email: faker.internet.email(),
+ },
+ update: {
+ email: faker.internet.email(),
+ profile: {
+ connectOrCreate: {
+ where: { id: faker.datatype.number() },
+ create: { bio: faker.lorem.paragraph() },
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "connectOrCreate",
+ model: "Profile",
+ argsPath: "args.update.profile.connectOrCreate",
+ relations: {
+ to: getModelRelation("User", "profile"),
+ from: getModelRelation("Profile", "user"),
+ },
+ },
+ ],
+ },
+ {
+ description: "deeply nested creates",
+ rootParams: createParams("User", "create", {
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ create: {
+ title: faker.lorem.sentence(),
+ content: faker.lorem.paragraph(),
+ comments: {
+ create: {
+ authorId: faker.datatype.number(),
+ content: faker.lorem.paragraph(),
+ },
+ },
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "create",
+ model: "Post",
+ argsPath: "args.data.posts.create",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "create",
+ model: "Comment",
+ argsPath: "args.data.posts.create.comments.create",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "create",
+ model: "Post",
+ argsPath: "args.data.posts.create",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ ],
+ },
+ {
+ description: "deeply nested update",
+ rootParams: createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ update: {
+ where: { id: faker.datatype.number() },
+ data: {
+ title: faker.lorem.sentence(),
+ content: faker.lorem.paragraph(),
+ comments: {
+ update: {
+ where: { id: faker.datatype.number() },
+ data: {
+ authorId: faker.datatype.number(),
+ content: faker.lorem.paragraph(),
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "update",
+ model: "Post",
+ argsPath: "args.data.posts.update",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "update",
+ model: "Comment",
+ argsPath: "args.data.posts.update.data.comments.update",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "update",
+ model: "Post",
+ argsPath: "args.data.posts.update",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ ],
+ },
+ {
+ description: "deeply nested delete",
+ rootParams: createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ update: {
+ where: { id: faker.datatype.number() },
+ data: {
+ title: faker.lorem.sentence(),
+ content: faker.lorem.paragraph(),
+ comments: {
+ delete: [
+ { id: faker.datatype.number() },
+ { id: faker.datatype.number() },
+ ],
+ },
+ },
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "update",
+ model: "Post",
+ argsPath: "args.data.posts.update",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "delete",
+ model: "Comment",
+ argsPath: "args.data.posts.update.data.comments.delete.0",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "update",
+ model: "Post",
+ argsPath: "args.data.posts.update",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ {
+ action: "delete",
+ model: "Comment",
+ argsPath: "args.data.posts.update.data.comments.delete.1",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "update",
+ model: "Post",
+ argsPath: "args.data.posts.update",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ ],
+ },
+ {
+ description: "deeply nested upsert",
+ rootParams: createParams("User", "upsert", {
+ where: { id: faker.datatype.number() },
+ create: {
+ email: faker.internet.email(),
+ },
+ update: {
+ email: faker.internet.email(),
+ posts: {
+ update: {
+ where: { id: faker.datatype.number() },
+ data: {
+ title: faker.lorem.sentence(),
+ content: faker.lorem.paragraph(),
+ comments: {
+ upsert: {
+ where: { id: faker.datatype.number() },
+ create: {
+ authorId: faker.datatype.number(),
+ content: faker.lorem.paragraph(),
+ },
+ update: {
+ authorId: faker.datatype.number(),
+ content: faker.lorem.paragraph(),
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "update",
+ model: "Post",
+ argsPath: "args.update.posts.update",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "upsert",
+ model: "Comment",
+ argsPath: "args.update.posts.update.data.comments.upsert",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "update",
+ model: "Post",
+ argsPath: "args.update.posts.update",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ ],
+ },
+ {
+ description: "deeply nested createMany",
+ rootParams: createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ update: {
+ where: { id: faker.datatype.number() },
+ data: {
+ title: faker.lorem.sentence(),
+ content: faker.lorem.paragraph(),
+ comments: {
+ createMany: {
+ data: [
+ {
+ authorId: faker.datatype.number(),
+ content: faker.lorem.paragraph(),
+ },
+ {
+ authorId: faker.datatype.number(),
+ content: faker.lorem.paragraph(),
+ },
+ ],
+ },
+ },
+ },
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "update",
+ model: "Post",
+ argsPath: "args.data.posts.update",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "createMany",
+ model: "Comment",
+ argsPath: "args.data.posts.update.data.comments.createMany",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "update",
+ model: "Post",
+ argsPath: "args.data.posts.update",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ ],
+ },
+ {
+ description: "deeply nested updateMany",
+ rootParams: createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ update: {
+ where: { id: faker.datatype.number() },
+ data: {
+ title: faker.lorem.sentence(),
+ content: faker.lorem.paragraph(),
+ comments: {
+ updateMany: {
+ where: { id: faker.datatype.number() },
+ data: {
+ content: faker.lorem.paragraph(),
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "update",
+ model: "Post",
+ argsPath: "args.data.posts.update",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "updateMany",
+ model: "Comment",
+ argsPath: "args.data.posts.update.data.comments.updateMany",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "update",
+ model: "Post",
+ argsPath: "args.data.posts.update",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ ],
+ },
+ {
+ description: "deeply nested updateMany array",
+ rootParams: createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ update: {
+ where: { id: faker.datatype.number() },
+ data: {
+ title: faker.lorem.sentence(),
+ content: faker.lorem.paragraph(),
+ comments: {
+ updateMany: [
+ {
+ where: { id: faker.datatype.number() },
+ data: { content: faker.lorem.paragraph() },
+ },
+ {
+ where: { id: faker.datatype.number() },
+ data: { content: faker.lorem.paragraph() },
+ },
+ ],
+ },
+ },
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "update",
+ model: "Post",
+ argsPath: "args.data.posts.update",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "updateMany",
+ model: "Comment",
+ argsPath: "args.data.posts.update.data.comments.updateMany.0",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "update",
+ model: "Post",
+ argsPath: "args.data.posts.update",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ {
+ action: "updateMany",
+ model: "Comment",
+ argsPath: "args.data.posts.update.data.comments.updateMany.1",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "update",
+ model: "Post",
+ argsPath: "args.data.posts.update",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ ],
+ },
+ {
+ description: "deeply nested deleteMany",
+ rootParams: createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ update: {
+ where: { id: faker.datatype.number() },
+ data: {
+ title: faker.lorem.sentence(),
+ content: faker.lorem.paragraph(),
+ comments: {
+ deleteMany: { id: faker.datatype.number() },
+ },
+ },
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "update",
+ model: "Post",
+ argsPath: "args.data.posts.update",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "deleteMany",
+ model: "Comment",
+ argsPath: "args.data.posts.update.data.comments.deleteMany",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "update",
+ model: "Post",
+ argsPath: "args.data.posts.update",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ ],
+ },
+ {
+ description: "deeply nested deleteMany array",
+ rootParams: createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ update: {
+ where: { id: faker.datatype.number() },
+ data: {
+ title: faker.lorem.sentence(),
+ content: faker.lorem.paragraph(),
+ comments: {
+ deleteMany: [
+ { id: faker.datatype.number() },
+ { id: faker.datatype.number() },
+ ],
+ },
+ },
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "update",
+ model: "Post",
+ argsPath: "args.data.posts.update",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "deleteMany",
+ model: "Comment",
+ argsPath: "args.data.posts.update.data.comments.deleteMany.0",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "update",
+ model: "Post",
+ argsPath: "args.data.posts.update",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ {
+ action: "deleteMany",
+ model: "Comment",
+ argsPath: "args.data.posts.update.data.comments.deleteMany.1",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "update",
+ model: "Post",
+ argsPath: "args.data.posts.update",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ ],
+ },
+ {
+ description: "deeply nested connectOrCreate",
+ rootParams: createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ update: {
+ where: { id: faker.datatype.number() },
+ data: {
+ title: faker.lorem.sentence(),
+ content: faker.lorem.paragraph(),
+ comments: {
+ connectOrCreate: {
+ where: { id: faker.datatype.number() },
+ create: {
+ authorId: faker.datatype.number(),
+ content: faker.lorem.paragraph(),
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "update",
+ model: "Post",
+ argsPath: "args.data.posts.update",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "connectOrCreate",
+ model: "Comment",
+ argsPath: "args.data.posts.update.data.comments.connectOrCreate",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "update",
+ model: "Post",
+ argsPath: "args.data.posts.update",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ ],
+ },
+ {
+ description: "include in findUnique",
+ rootParams: createParams("User", "findUnique", {
+ where: { id: faker.datatype.number() },
+ include: {
+ posts: true,
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "include",
+ model: "Post",
+ argsPath: "args.include.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ ],
+ },
+ {
+ description: "include in findFirst",
+ rootParams: createParams("User", "findFirst", {
+ where: { id: faker.datatype.number() },
+ include: { posts: true },
+ }),
+ nestedCalls: [
+ {
+ action: "include",
+ model: "Post",
+ argsPath: "args.include.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ ],
+ },
+ {
+ description: "include in findMany",
+ rootParams: createParams("User", "findMany", {
+ where: { id: faker.datatype.number() },
+ include: { posts: true },
+ }),
+ nestedCalls: [
+ {
+ action: "include",
+ model: "Post",
+ argsPath: "args.include.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ ],
+ },
+ {
+ description: "include in create",
+ rootParams: createParams("User", "create", {
+ data: {
+ email: faker.internet.email(),
+ posts: { create: { title: faker.lorem.sentence() } },
+ },
+ include: { posts: true },
+ }),
+ nestedCalls: [
+ {
+ action: "create",
+ model: "Post",
+ argsPath: "args.data.posts.create",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "include",
+ model: "Post",
+ argsPath: "args.include.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ ],
+ },
+ {
+ description: "include in update",
+ rootParams: createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ },
+ include: { posts: true },
+ }),
+ nestedCalls: [
+ {
+ action: "include",
+ model: "Post",
+ argsPath: "args.include.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ ],
+ },
+ {
+ description: "include in upsert",
+ rootParams: createParams("User", "upsert", {
+ where: { id: faker.datatype.number() },
+ create: {
+ email: faker.internet.email(),
+ },
+ update: {
+ email: faker.internet.email(),
+ },
+ include: { posts: true },
+ }),
+ nestedCalls: [
+ {
+ action: "include",
+ model: "Post",
+ argsPath: "args.include.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ ],
+ },
+ {
+ description: "nested includes",
+ rootParams: createParams("User", "findUnique", {
+ where: { id: faker.datatype.number() },
+ include: {
+ posts: {
+ include: {
+ comments: true,
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "include",
+ model: "Post",
+ argsPath: "args.include.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "include",
+ model: "Comment",
+ argsPath: "args.include.posts.include.comments",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "include",
+ model: "Post",
+ argsPath: "args.include.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ ],
+ },
+ {
+ description: "deeply nested includes",
+ rootParams: createParams("User", "findUnique", {
+ where: { id: faker.datatype.number() },
+ include: {
+ posts: {
+ include: {
+ comments: {
+ include: {
+ replies: true,
+ },
+ },
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "include",
+ model: "Post",
+ argsPath: "args.include.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "include",
+ model: "Comment",
+ argsPath: "args.include.posts.include.comments",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "include",
+ model: "Post",
+ argsPath: "args.include.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ {
+ action: "include",
+ model: "Comment",
+ argsPath: "args.include.posts.include.comments.include.replies",
+ relations: {
+ to: getModelRelation("Comment", "replies"),
+ from: getModelRelation("Comment", "repliedTo"),
+ },
+ scope: {
+ action: "include",
+ model: "Comment",
+ argsPath: "args.include.posts.include.comments",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "include",
+ model: "Post",
+ argsPath: "args.include.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ },
+ ],
+ },
+ {
+ description: "select in findUnique",
+ rootParams: createParams("User", "findUnique", {
+ where: { id: faker.datatype.number() },
+ select: {
+ posts: true,
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "select",
+ model: "Post",
+ argsPath: "args.select.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ ],
+ },
+ {
+ description: "select in findFirst",
+ rootParams: createParams("User", "findFirst", {
+ where: { id: faker.datatype.number() },
+ select: { posts: true },
+ }),
+ nestedCalls: [
+ {
+ action: "select",
+ model: "Post",
+ argsPath: "args.select.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ ],
+ },
+ {
+ description: "select in findMany",
+ rootParams: createParams("User", "findMany", {
+ where: { id: faker.datatype.number() },
+ select: { posts: true },
+ }),
+ nestedCalls: [
+ {
+ action: "select",
+ model: "Post",
+ argsPath: "args.select.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ ],
+ },
+ {
+ description: "select in create",
+ rootParams: createParams("User", "create", {
+ data: {
+ email: faker.internet.email(),
+ posts: { create: { title: faker.lorem.sentence() } },
+ },
+ select: { posts: true },
+ }),
+ nestedCalls: [
+ {
+ action: "create",
+ model: "Post",
+ argsPath: "args.data.posts.create",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "select",
+ model: "Post",
+ argsPath: "args.select.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ ],
+ },
+ {
+ description: "select in update",
+ rootParams: createParams("User", "update", {
+ where: { id: faker.datatype.number() },
+ data: {
+ email: faker.internet.email(),
+ },
+ select: { posts: true },
+ }),
+ nestedCalls: [
+ {
+ action: "select",
+ model: "Post",
+ argsPath: "args.select.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ ],
+ },
+ {
+ description: "select in upsert",
+ rootParams: createParams("User", "upsert", {
+ where: { id: faker.datatype.number() },
+ create: {
+ email: faker.internet.email(),
+ },
+ update: {
+ email: faker.internet.email(),
+ },
+ select: { posts: true },
+ }),
+ nestedCalls: [
+ {
+ action: "select",
+ model: "Post",
+ argsPath: "args.select.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ ],
+ },
+ {
+ description: "nested selects",
+ rootParams: createParams("User", "findUnique", {
+ where: { id: faker.datatype.number() },
+ select: {
+ posts: {
+ select: {
+ comments: true,
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "select",
+ model: "Post",
+ argsPath: "args.select.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "select",
+ model: "Comment",
+ argsPath: "args.select.posts.select.comments",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "select",
+ model: "Post",
+ argsPath: "args.select.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ ],
+ },
+ {
+ description: "deeply nested selects",
+ rootParams: createParams("User", "findUnique", {
+ where: { id: faker.datatype.number() },
+ select: {
+ posts: {
+ select: {
+ comments: {
+ select: {
+ replies: true,
+ },
+ },
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "select",
+ model: "Post",
+ argsPath: "args.select.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "select",
+ model: "Comment",
+ argsPath: "args.select.posts.select.comments",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "select",
+ model: "Post",
+ argsPath: "args.select.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ {
+ action: "select",
+ model: "Comment",
+ argsPath: "args.select.posts.select.comments.select.replies",
+ relations: {
+ to: getModelRelation("Comment", "replies"),
+ from: getModelRelation("Comment", "repliedTo"),
+ },
+ scope: {
+ action: "select",
+ model: "Comment",
+ argsPath: "args.select.posts.select.comments",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "select",
+ model: "Post",
+ argsPath: "args.select.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ },
+ ],
+ },
+ {
+ description: "deeply nested selects with custom fields",
+ rootParams: createParams("User", "findUnique", {
+ where: { id: faker.datatype.number() },
+ select: {
+ posts: {
+ select: {
+ title: true,
+ comments: {
+ select: {
+ content: true,
+ replies: {
+ select: {
+ content: true,
+ createdAt: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "select",
+ model: "Post",
+ argsPath: "args.select.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "select",
+ model: "Comment",
+ argsPath: "args.select.posts.select.comments",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "select",
+ model: "Post",
+ argsPath: "args.select.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ {
+ action: "select",
+ model: "Comment",
+ argsPath: "args.select.posts.select.comments.select.replies",
+ relations: {
+ to: getModelRelation("Comment", "replies"),
+ from: getModelRelation("Comment", "repliedTo"),
+ },
+ scope: {
+ action: "select",
+ model: "Comment",
+ argsPath: "args.select.posts.select.comments",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "select",
+ model: "Post",
+ argsPath: "args.select.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ },
+ ],
+ },
+ {
+ description: "nested select in include",
+ rootParams: createParams("User", "findUnique", {
+ where: { id: faker.datatype.number() },
+ include: {
+ posts: {
+ select: {
+ comments: true,
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "include",
+ model: "Post",
+ argsPath: "args.include.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "select",
+ model: "Post",
+ argsPath: "args.include.posts.select",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ scope: {
+ action: "include",
+ model: "Post",
+ argsPath: "args.include.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ {
+ action: "select",
+ model: "Comment",
+ argsPath: "args.include.posts.select.comments",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "include",
+ model: "Post",
+ argsPath: "args.include.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ ],
+ },
+ {
+ description: "nested include in select",
+ rootParams: createParams("User", "findUnique", {
+ where: { id: faker.datatype.number() },
+ select: {
+ posts: {
+ include: {
+ comments: true,
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "select",
+ model: "Post",
+ argsPath: "args.select.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "include",
+ model: "Comment",
+ argsPath: "args.select.posts.include.comments",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "select",
+ model: "Post",
+ argsPath: "args.select.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ ],
+ },
+ {
+ description: "nested select in nested include",
+ rootParams: createParams("User", "findUnique", {
+ where: { id: faker.datatype.number() },
+ include: {
+ posts: {
+ include: {
+ comments: {
+ select: {
+ content: true,
+ },
+ },
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "include",
+ model: "Post",
+ argsPath: "args.include.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "include",
+ model: "Comment",
+ argsPath: "args.include.posts.include.comments",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "include",
+ model: "Post",
+ argsPath: "args.include.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ {
+ action: "select",
+ model: "Comment",
+ argsPath: "args.include.posts.include.comments.select",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "include",
+ model: "Comment",
+ argsPath: "args.include.posts.include.comments",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "include",
+ model: "Post",
+ argsPath: "args.include.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ },
+ ],
+ },
+ {
+ description: "nested include in nested select",
+ rootParams: createParams("User", "findUnique", {
+ where: { id: faker.datatype.number() },
+ select: {
+ posts: {
+ select: {
+ comments: {
+ include: {
+ author: true,
+ },
+ },
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "select",
+ model: "Post",
+ argsPath: "args.select.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "select",
+ model: "Comment",
+ argsPath: "args.select.posts.select.comments",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "select",
+ model: "Post",
+ argsPath: "args.select.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ {
+ action: "include",
+ model: "User",
+ argsPath: "args.select.posts.select.comments.include.author",
+ relations: {
+ to: getModelRelation("Comment", "author"),
+ from: getModelRelation("User", "comments"),
+ },
+ scope: {
+ action: "select",
+ model: "Comment",
+ argsPath: "args.select.posts.select.comments",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "select",
+ model: "Post",
+ argsPath: "args.select.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ },
+ ],
+ },
+ {
+ description: "nested includes with nested create",
+ rootParams: createParams("User", "create", {
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ create: {
+ title: faker.lorem.sentence(),
+ comments: {
+ create: {
+ authorId: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ },
+ },
+ },
+ },
+ },
+ include: {
+ posts: {
+ include: {
+ comments: true,
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "create",
+ model: "Post",
+ argsPath: "args.data.posts.create",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "create",
+ model: "Comment",
+ argsPath: "args.data.posts.create.comments.create",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "create",
+ model: "Post",
+ argsPath: "args.data.posts.create",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ {
+ action: "include",
+ model: "Post",
+ argsPath: "args.include.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "include",
+ model: "Comment",
+ argsPath: "args.include.posts.include.comments",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "include",
+ model: "Post",
+ argsPath: "args.include.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ ],
+ },
+ {
+ description: "nested selects with nested create",
+ rootParams: createParams("User", "create", {
+ data: {
+ email: faker.internet.email(),
+ posts: {
+ create: {
+ title: faker.lorem.sentence(),
+ comments: {
+ create: {
+ authorId: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ },
+ },
+ },
+ },
+ },
+ select: {
+ posts: {
+ select: {
+ comments: true,
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "create",
+ model: "Post",
+ argsPath: "args.data.posts.create",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "create",
+ model: "Comment",
+ argsPath: "args.data.posts.create.comments.create",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "create",
+ model: "Post",
+ argsPath: "args.data.posts.create",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ {
+ action: "select",
+ model: "Post",
+ argsPath: "args.select.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "select",
+ model: "Comment",
+ argsPath: "args.select.posts.select.comments",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "select",
+ model: "Post",
+ argsPath: "args.select.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ ],
+ },
+ {
+ description: "nested where",
+ rootParams: createParams("User", "findMany", {
+ where: {
+ profile: {
+ bio: {
+ contains: "foo",
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "where",
+ model: "Profile",
+ argsPath: "args.where.profile",
+ relations: {
+ to: getModelRelation("User", "profile"),
+ from: getModelRelation("Profile", "user"),
+ },
+ },
+ ],
+ },
+ {
+ description: "nested where with list modifier",
+ rootParams: createParams("User", "findMany", {
+ where: {
+ posts: {
+ some: {
+ title: {
+ contains: "foo",
+ },
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "where",
+ model: "Post",
+ argsPath: "args.where.posts.some",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ modifier: "some",
+ },
+ ],
+ },
+ {
+ description: "nested where with multiple list modifiers",
+ rootParams: createParams("User", "findMany", {
+ where: {
+ posts: {
+ none: {
+ title: "foo",
+ },
+ every: {
+ title: "bar",
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "where",
+ model: "Post",
+ argsPath: "args.where.posts.none",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ modifier: "none",
+ },
+ {
+ action: "where",
+ model: "Post",
+ argsPath: "args.where.posts.every",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ modifier: "every",
+ },
+ ],
+ },
+ {
+ description: "nested where with 'is' modifier",
+ rootParams: createParams("User", "findMany", {
+ where: {
+ profile: {
+ is: {
+ bio: {
+ contains: "foo",
+ },
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "where",
+ model: "Profile",
+ argsPath: "args.where.profile.is",
+ modifier: "is",
+ relations: {
+ to: getModelRelation("User", "profile"),
+ from: getModelRelation("Profile", "user"),
+ },
+ },
+ ],
+ },
+ {
+ description: "nested where with 'isNot' modifier",
+ rootParams: createParams("User", "findMany", {
+ where: {
+ profile: {
+ isNot: {
+ bio: {
+ contains: "foo",
+ },
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "where",
+ model: "Profile",
+ argsPath: "args.where.profile.isNot",
+ modifier: "isNot",
+ relations: {
+ to: getModelRelation("User", "profile"),
+ from: getModelRelation("Profile", "user"),
+ },
+ },
+ ],
+ },
+ {
+ description: "two nested where calls from two relations on same parent",
+ rootParams: createParams("User", "findMany", {
+ where: {
+ profile: {
+ bio: {
+ contains: "foo",
+ },
+ },
+ posts: {
+ some: {
+ title: {
+ contains: "foo",
+ },
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "where",
+ model: "Profile",
+ argsPath: "args.where.profile",
+ relations: {
+ to: getModelRelation("User", "profile"),
+ from: getModelRelation("Profile", "user"),
+ },
+ },
+ {
+ action: "where",
+ model: "Post",
+ argsPath: "args.where.posts.some",
+ modifier: "some",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ ],
+ },
+ {
+ description: "deeply nested where",
+ rootParams: createParams("User", "findMany", {
+ where: {
+ profile: {
+ user: { name: "foo" },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "where",
+ model: "Profile",
+ argsPath: "args.where.profile",
+ relations: {
+ to: getModelRelation("User", "profile"),
+ from: getModelRelation("Profile", "user"),
+ },
+ },
+ {
+ action: "where",
+ model: "User",
+ argsPath: "args.where.profile.user",
+ relations: {
+ to: getModelRelation("Profile", "user"),
+ from: getModelRelation("User", "profile"),
+ },
+ scope: {
+ action: "where",
+ model: "Profile",
+ argsPath: "args.where.profile",
+ relations: {
+ to: getModelRelation("User", "profile"),
+ from: getModelRelation("Profile", "user"),
+ },
+ },
+ },
+ ],
+ },
+ {
+ description: "where nested in 'some' modifier",
+ rootParams: createParams("User", "findMany", {
+ where: {
+ posts: {
+ some: {
+ author: {
+ name: "foo",
+ },
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "where",
+ model: "Post",
+ argsPath: "args.where.posts.some",
+ modifier: "some",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "where",
+ model: "User",
+ argsPath: "args.where.posts.some.author",
+ relations: {
+ to: getModelRelation("Post", "author"),
+ from: getModelRelation("User", "posts"),
+ },
+ scope: {
+ action: "where",
+ model: "Post",
+ argsPath: "args.where.posts.some",
+ modifier: "some",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ ],
+ },
+ {
+ description: "where nested in 'none' modifier",
+ rootParams: createParams("User", "findMany", {
+ where: {
+ posts: {
+ none: {
+ author: {
+ name: "foo",
+ },
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "where",
+ model: "Post",
+ argsPath: "args.where.posts.none",
+ modifier: "none",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "where",
+ model: "User",
+ argsPath: "args.where.posts.none.author",
+ relations: {
+ to: getModelRelation("Post", "author"),
+ from: getModelRelation("User", "posts"),
+ },
+ scope: {
+ action: "where",
+ model: "Post",
+ argsPath: "args.where.posts.none",
+ modifier: "none",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ ],
+ },
+ {
+ description: "where nested in 'every' modifier",
+ rootParams: createParams("User", "findMany", {
+ where: {
+ posts: {
+ every: {
+ author: {
+ name: "foo",
+ },
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "where",
+ model: "Post",
+ argsPath: "args.where.posts.every",
+ modifier: "every",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "where",
+ model: "User",
+ argsPath: "args.where.posts.every.author",
+ relations: {
+ to: getModelRelation("Post", "author"),
+ from: getModelRelation("User", "posts"),
+ },
+ scope: {
+ action: "where",
+ model: "Post",
+ argsPath: "args.where.posts.every",
+ modifier: "every",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ ],
+ },
+ {
+ description: "where nested in 'is' modifier",
+ rootParams: createParams("User", "findMany", {
+ where: {
+ profile: {
+ is: {
+ user: {
+ name: "foo",
+ },
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "where",
+ model: "Profile",
+ argsPath: "args.where.profile.is",
+ modifier: "is",
+ relations: {
+ to: getModelRelation("User", "profile"),
+ from: getModelRelation("Profile", "user"),
+ },
+ },
+ {
+ action: "where",
+ model: "User",
+ argsPath: "args.where.profile.is.user",
+ relations: {
+ to: getModelRelation("Profile", "user"),
+ from: getModelRelation("User", "profile"),
+ },
+ scope: {
+ action: "where",
+ model: "Profile",
+ argsPath: "args.where.profile.is",
+ modifier: "is",
+ relations: {
+ to: getModelRelation("User", "profile"),
+ from: getModelRelation("Profile", "user"),
+ },
+ },
+ },
+ ],
+ },
+ {
+ description: "where nested in 'isNot' modifier",
+ rootParams: createParams("User", "findMany", {
+ where: {
+ profile: {
+ isNot: {
+ user: {
+ name: "foo",
+ },
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "where",
+ model: "Profile",
+ argsPath: "args.where.profile.isNot",
+ modifier: "isNot",
+ relations: {
+ to: getModelRelation("User", "profile"),
+ from: getModelRelation("Profile", "user"),
+ },
+ },
+ {
+ action: "where",
+ model: "User",
+ argsPath: "args.where.profile.isNot.user",
+ relations: {
+ to: getModelRelation("Profile", "user"),
+ from: getModelRelation("User", "profile"),
+ },
+ scope: {
+ action: "where",
+ model: "Profile",
+ argsPath: "args.where.profile.isNot",
+ modifier: "isNot",
+ relations: {
+ to: getModelRelation("User", "profile"),
+ from: getModelRelation("Profile", "user"),
+ },
+ },
+ },
+ ],
+ },
+ {
+ description: "where nested in AND logical operator",
+ rootParams: createParams("User", "findMany", {
+ where: {
+ AND: [
+ {
+ posts: {
+ some: {
+ content: "foo",
+ },
+ },
+ },
+ {
+ comments: {
+ some: {
+ content: "bar",
+ },
+ },
+ },
+ ],
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "where",
+ model: "Post",
+ argsPath: "args.where.AND.0.posts.some",
+ modifier: "some",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ logicalOperators: ["AND"],
+ },
+ {
+ action: "where",
+ model: "Comment",
+ argsPath: "args.where.AND.1.comments.some",
+ modifier: "some",
+ relations: {
+ to: getModelRelation("User", "comments"),
+ from: getModelRelation("Comment", "author"),
+ },
+ logicalOperators: ["AND"],
+ },
+ ],
+ },
+ {
+ description: "where nested in OR logical operator",
+ rootParams: createParams("User", "findMany", {
+ where: {
+ OR: [
+ {
+ posts: {
+ some: {
+ content: "foo",
+ },
+ },
+ },
+ {
+ comments: {
+ some: {
+ content: "bar",
+ },
+ },
+ },
+ ],
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "where",
+ model: "Post",
+ argsPath: "args.where.OR.0.posts.some",
+ modifier: "some",
+ logicalOperators: ["OR"],
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "where",
+ model: "Comment",
+ argsPath: "args.where.OR.1.comments.some",
+ modifier: "some",
+ logicalOperators: ["OR"],
+ relations: {
+ to: getModelRelation("User", "comments"),
+ from: getModelRelation("Comment", "author"),
+ },
+ },
+ ],
+ },
+ {
+ description: "where nested in NOT logical operator",
+ rootParams: createParams("User", "findMany", {
+ where: {
+ NOT: [
+ {
+ posts: {
+ some: {
+ content: "foo",
+ },
+ },
+ },
+ {
+ comments: {
+ some: {
+ content: "bar",
+ },
+ },
+ },
+ ],
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "where",
+ model: "Post",
+ argsPath: "args.where.NOT.0.posts.some",
+ modifier: "some",
+ logicalOperators: ["NOT"],
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "where",
+ model: "Comment",
+ argsPath: "args.where.NOT.1.comments.some",
+ modifier: "some",
+ logicalOperators: ["NOT"],
+ relations: {
+ to: getModelRelation("User", "comments"),
+ from: getModelRelation("Comment", "author"),
+ },
+ },
+ ],
+ },
+ {
+ description: "where nested in NOT, AND and OR logical operator",
+ rootParams: createParams("User", "findMany", {
+ where: {
+ NOT: [
+ {
+ posts: {
+ some: {
+ content: "foo",
+ },
+ },
+ },
+ ],
+ AND: [
+ {
+ comments: {
+ some: {
+ content: "bar",
+ },
+ },
+ },
+ ],
+ OR: [
+ {
+ profile: {
+ bio: "baz",
+ },
+ },
+ ],
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "where",
+ model: "Post",
+ argsPath: "args.where.NOT.0.posts.some",
+ modifier: "some",
+ logicalOperators: ["NOT"],
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "where",
+ model: "Comment",
+ argsPath: "args.where.AND.0.comments.some",
+ modifier: "some",
+ logicalOperators: ["AND"],
+ relations: {
+ to: getModelRelation("User", "comments"),
+ from: getModelRelation("Comment", "author"),
+ },
+ },
+ {
+ action: "where",
+ model: "Profile",
+ argsPath: "args.where.OR.0.profile",
+ logicalOperators: ["OR"],
+ relations: {
+ to: getModelRelation("User", "profile"),
+ from: getModelRelation("Profile", "user"),
+ },
+ },
+ ],
+ },
+ {
+ description: "where deeply nested in logical modifiers",
+ rootParams: createParams("User", "findMany", {
+ where: {
+ NOT: [
+ {
+ posts: {
+ some: {
+ AND: [{ author: { OR: [{ name: "foo" }, { name: "bar" }] } }],
+ },
+ },
+ },
+ ],
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "where",
+ model: "Post",
+ argsPath: "args.where.NOT.0.posts.some",
+ modifier: "some",
+ logicalOperators: ["NOT"],
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "where",
+ model: "User",
+ argsPath: "args.where.NOT.0.posts.some.AND.0.author",
+ logicalOperators: ["AND"],
+ relations: {
+ to: getModelRelation("Post", "author"),
+ from: getModelRelation("User", "posts"),
+ },
+ scope: {
+ action: "where",
+ model: "Post",
+ argsPath: "args.where.NOT.0.posts.some",
+ modifier: "some",
+ logicalOperators: ["NOT"],
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ ],
+ },
+ {
+ description:
+ "where deeply nested in logical operators with no interim relations",
+ rootParams: createParams("User", "findMany", {
+ where: {
+ NOT: {
+ AND: [
+ {
+ NOT: {
+ name: "foo",
+ profile: {
+ bio: "bar",
+ },
+ },
+ },
+ {
+ OR: [
+ {
+ name: "foo",
+ },
+ {
+ name: "bar",
+ comments: {
+ some: {
+ content: "baz",
+ },
+ },
+ },
+ ],
+ },
+ ],
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "where",
+ model: "Profile",
+ argsPath: "args.where.NOT.AND.0.NOT.profile",
+ logicalOperators: ["NOT", "AND", "NOT"],
+ relations: {
+ to: getModelRelation("User", "profile"),
+ from: getModelRelation("Profile", "user"),
+ },
+ },
+ {
+ action: "where",
+ model: "Comment",
+ argsPath: "args.where.NOT.AND.1.OR.1.comments.some",
+ modifier: "some",
+ logicalOperators: ["NOT", "AND", "OR"],
+ relations: {
+ to: getModelRelation("User", "comments"),
+ from: getModelRelation("Comment", "author"),
+ },
+ },
+ ],
+ },
+ {
+ description: "include where",
+ rootParams: createParams("User", "findMany", {
+ include: {
+ posts: {
+ where: {
+ title: "foo",
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "include",
+ model: "Post",
+ argsPath: "args.include.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "where",
+ model: "Post",
+ argsPath: "args.include.posts.where",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ scope: {
+ action: "include",
+ model: "Post",
+ argsPath: "args.include.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ ],
+ },
+ {
+ description: "select where",
+ rootParams: createParams("User", "findMany", {
+ select: {
+ posts: {
+ where: {
+ title: "foo",
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "select",
+ model: "Post",
+ argsPath: "args.select.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "where",
+ model: "Post",
+ argsPath: "args.select.posts.where",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ scope: {
+ action: "select",
+ model: "Post",
+ argsPath: "args.select.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ ],
+ },
+ {
+ description: "select in include where",
+ rootParams: createParams("User", "findMany", {
+ include: {
+ posts: {
+ select: {
+ comments: {
+ where: {
+ content: "foo",
+ },
+ },
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "include",
+ model: "Post",
+ argsPath: "args.include.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "select",
+ model: "Post",
+ argsPath: "args.include.posts.select",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ scope: {
+ action: "include",
+ model: "Post",
+ argsPath: "args.include.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ {
+ action: "select",
+ model: "Comment",
+ argsPath: "args.include.posts.select.comments",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "include",
+ model: "Post",
+ argsPath: "args.include.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ {
+ action: "where",
+ model: "Comment",
+ argsPath: "args.include.posts.select.comments.where",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "select",
+ model: "Comment",
+ argsPath: "args.include.posts.select.comments",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "include",
+ model: "Post",
+ argsPath: "args.include.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ },
+ ],
+ },
+ {
+ description: "multiple include wheres",
+ rootParams: createParams("User", "findMany", {
+ include: {
+ posts: {
+ where: {
+ title: "foo",
+ },
+ include: {
+ comments: {
+ where: {
+ content: "foo",
+ },
+ },
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "include",
+ model: "Post",
+ argsPath: "args.include.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "where",
+ model: "Post",
+ argsPath: "args.include.posts.where",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ scope: {
+ action: "include",
+ model: "Post",
+ argsPath: "args.include.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ {
+ action: "include",
+ model: "Comment",
+ argsPath: "args.include.posts.include.comments",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "include",
+ model: "Post",
+ argsPath: "args.include.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ {
+ action: "where",
+ model: "Comment",
+ argsPath: "args.include.posts.include.comments.where",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "include",
+ model: "Comment",
+ argsPath: "args.include.posts.include.comments",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "include",
+ model: "Post",
+ argsPath: "args.include.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ },
+ ],
+ },
+ {
+ description: "multiple select wheres",
+ rootParams: createParams("User", "findMany", {
+ select: {
+ posts: {
+ where: {
+ title: "foo",
+ },
+ select: {
+ comments: {
+ where: {
+ content: "foo",
+ },
+ },
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "select",
+ model: "Post",
+ argsPath: "args.select.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "where",
+ model: "Post",
+ argsPath: "args.select.posts.where",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ scope: {
+ action: "select",
+ model: "Post",
+ argsPath: "args.select.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ {
+ action: "select",
+ model: "Comment",
+ argsPath: "args.select.posts.select.comments",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "select",
+ model: "Post",
+ argsPath: "args.select.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ {
+ action: "where",
+ model: "Comment",
+ argsPath: "args.select.posts.select.comments.where",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "select",
+ model: "Comment",
+ argsPath: "args.select.posts.select.comments",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ scope: {
+ action: "select",
+ model: "Post",
+ argsPath: "args.select.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ },
+ ],
+ },
+ {
+ description: "include where with nested relations",
+ rootParams: createParams("User", "findMany", {
+ include: {
+ posts: {
+ where: {
+ title: "foo",
+ comments: {
+ some: {
+ content: "bar",
+ repliedTo: {
+ is: {
+ content: "baz",
+ author: {
+ id: 1,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "include",
+ model: "Post",
+ argsPath: "args.include.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ {
+ action: "where",
+ model: "Post",
+ argsPath: "args.include.posts.where",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ scope: {
+ action: "include",
+ model: "Post",
+ argsPath: "args.include.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ {
+ action: "where",
+ model: "Comment",
+ argsPath: "args.include.posts.where.comments.some",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ modifier: "some",
+ scope: {
+ action: "where",
+ model: "Post",
+ argsPath: "args.include.posts.where",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ scope: {
+ action: "include",
+ model: "Post",
+ argsPath: "args.include.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ },
+ {
+ action: "where",
+ model: "Comment",
+ argsPath: "args.include.posts.where.comments.some.repliedTo.is",
+ relations: {
+ to: getModelRelation("Comment", "repliedTo"),
+ from: getModelRelation("Comment", "replies"),
+ },
+ modifier: "is",
+ scope: {
+ action: "where",
+ model: "Comment",
+ argsPath: "args.include.posts.where.comments.some",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ modifier: "some",
+ scope: {
+ action: "where",
+ model: "Post",
+ argsPath: "args.include.posts.where",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ scope: {
+ action: "include",
+ model: "Post",
+ argsPath: "args.include.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ },
+ },
+ {
+ action: "where",
+ model: "User",
+ argsPath:
+ "args.include.posts.where.comments.some.repliedTo.is.author",
+ relations: {
+ to: getModelRelation("Comment", "author"),
+ from: getModelRelation("User", "comments"),
+ },
+ scope: {
+ action: "where",
+ model: "Comment",
+ argsPath: "args.include.posts.where.comments.some.repliedTo.is",
+ relations: {
+ to: getModelRelation("Comment", "repliedTo"),
+ from: getModelRelation("Comment", "replies"),
+ },
+ modifier: "is",
+ scope: {
+ action: "where",
+ model: "Comment",
+ argsPath: "args.include.posts.where.comments.some",
+ relations: {
+ to: getModelRelation("Post", "comments"),
+ from: getModelRelation("Comment", "post"),
+ },
+ modifier: "some",
+ scope: {
+ action: "where",
+ model: "Post",
+ argsPath: "args.include.posts.where",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ scope: {
+ action: "include",
+ model: "Post",
+ argsPath: "args.include.posts",
+ relations: {
+ to: getModelRelation("User", "posts"),
+ from: getModelRelation("Post", "author"),
+ },
+ },
+ },
+ },
+ },
+ },
+ ],
+ },
+ {
+ description: "correct relations for relations between same model",
+ rootParams: createParams("Comment", "findMany", {
+ include: {
+ replies: true,
+ },
+ }),
+ nestedCalls: [
+ {
+ action: "include",
+ model: "Comment",
+ argsPath: "args.include.replies",
+ relations: {
+ to: getModelRelation("Comment", "replies"),
+ from: getModelRelation("Comment", "repliedTo"),
+ },
+ },
+ ],
+ },
+ ])(
+ "calls middleware with $description",
+ async ({ rootParams, nestedCalls = [] }) => {
+ const middleware = jest.fn((params, next) => next(params));
+ const nestedMiddleware = createNestedMiddleware(middleware);
+
+ const next = (_: any) => Promise.resolve({});
+ await nestedMiddleware(rootParams, next);
+
+ expect(middleware).toHaveBeenCalledTimes(nestedCalls.length + 1);
+ expect(middleware).toHaveBeenCalledWith(rootParams, next);
+ nestedCalls.forEach((call) => {
+ expect(middleware).toHaveBeenCalledWith(
+ nestedParamsFromCall(rootParams, call),
+ expect.any(Function)
+ );
+ });
+ }
+ );
+});
diff --git a/test/errors.test.ts b/test/unit/errors.test.ts
similarity index 95%
rename from test/errors.test.ts
rename to test/unit/errors.test.ts
index 572902b..7af62b8 100644
--- a/test/errors.test.ts
+++ b/test/unit/errors.test.ts
@@ -1,8 +1,8 @@
import faker from "faker";
-import { createNestedMiddleware } from "../src";
-import { createParams } from "./utils/createParams";
-import { wait } from "./utils/wait";
+import { createNestedMiddleware } from "../../src";
+import { createParams } from "./helpers/createParams";
+import { wait } from "./helpers/wait";
async function createAsyncError() {
await wait(100);
diff --git a/test/utils/createParams.ts b/test/unit/helpers/createParams.ts
similarity index 87%
rename from test/utils/createParams.ts
rename to test/unit/helpers/createParams.ts
index b392ffc..562198c 100644
--- a/test/utils/createParams.ts
+++ b/test/unit/helpers/createParams.ts
@@ -6,6 +6,8 @@ type DelegateByModel = Model extends "User"
? Prisma.PostDelegate
: Model extends "Profile"
? Prisma.ProfileDelegate
+ : Model extends "Comment"
+ ? Prisma.CommentDelegate
: never;
type SelectByModel = Model extends "User"
@@ -14,6 +16,8 @@ type SelectByModel = Model extends "User"
? Prisma.PostSelect
: Model extends "Profile"
? Prisma.ProfileSelect
+ : Model extends "Comment"
+ ? Prisma.CommentSelect
: never;
type IncludeByModel = Model extends "User"
@@ -22,13 +26,16 @@ type IncludeByModel = Model extends "User"
? Prisma.PostInclude
: Model extends "Profile"
? Prisma.ProfileInclude
+ : Model extends "Comment"
+ ? Prisma.CommentInclude
: never;
type ActionByModel =
| keyof DelegateByModel
| "connectOrCreate"
| "select"
- | "include";
+ | "include"
+ | "where";
type ArgsByAction<
Model extends Prisma.ModelName,
@@ -53,6 +60,10 @@ type ArgsByAction<
? Parameters["findFirst"]>[0]
: Action extends "findMany"
? Parameters["findMany"]>[0]
+ : Action extends "count"
+ ? Parameters["count"]>[0]
+ : Action extends "aggregate"
+ ? Parameters["aggregate"]>[0]
: Action extends "connectOrCreate"
? {
where: Parameters["findUnique"]>[0];
diff --git a/test/utils/wait.ts b/test/unit/helpers/wait.ts
similarity index 100%
rename from test/utils/wait.ts
rename to test/unit/helpers/wait.ts
diff --git a/test/unit/results.test.ts b/test/unit/results.test.ts
new file mode 100644
index 0000000..9c7722c
--- /dev/null
+++ b/test/unit/results.test.ts
@@ -0,0 +1,2573 @@
+import faker from "faker";
+
+import { createNestedMiddleware } from "../../src";
+import { createParams } from "./helpers/createParams";
+import { wait } from "./helpers/wait";
+
+function addReturnedDate(result: any) {
+ if (typeof result === "undefined") return;
+ const returned = new Date();
+
+ if (Array.isArray(result)) {
+ return result.map((item) => ({ ...item, returned }));
+ }
+
+ return { ...result, returned };
+}
+
+describe("modifying results", () => {
+ it("returns null successfully", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ return next(params);
+ });
+
+ const params = createParams("User", "findUnique", {
+ where: { id: faker.datatype.number() },
+ });
+ const next = jest.fn(() => Promise.resolve(null));
+ const result = await nestedMiddleware(params, next);
+
+ expect(result).toBeNull();
+ });
+
+ it("returns count successfully", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ return next(params);
+ });
+
+ const params = createParams("User", "count", {});
+ const next = jest.fn(() => Promise.resolve(1));
+ const result = await nestedMiddleware(params, next);
+
+ expect(result).toEqual(1);
+ });
+
+ it("returns correct result by default", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ return next(params);
+ });
+
+ const params = createParams("User", "create", {
+ data: { email: faker.internet.email() },
+ });
+ const next = jest.fn(() =>
+ Promise.resolve({
+ id: faker.datatype.number(),
+ email: params.args.data.email,
+ })
+ );
+ const result = await nestedMiddleware(params, next);
+
+ expect(result).toEqual({
+ id: expect.any(Number),
+ email: params.args.data.email,
+ });
+ });
+
+ it("returns correct result when relations are included", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ return next(params);
+ });
+
+ const params = createParams("Post", "findMany", {
+ where: { title: faker.lorem.sentence() },
+ include: {
+ author: { include: { profile: true, posts: true } },
+ comments: { include: { author: true } },
+ },
+ });
+ const clientResult = [
+ {
+ id: faker.datatype.number(),
+ title: faker.lorem.sentence(),
+ author: {
+ id: faker.datatype.number(),
+ email: faker.internet.email(),
+ profile: null,
+ posts: [
+ {
+ id: faker.datatype.number(),
+ title: faker.lorem.sentence(),
+ comments: [
+ { id: faker.datatype.number(), content: faker.lorem.text() },
+ { id: faker.datatype.number(), content: faker.lorem.text() },
+ { id: faker.datatype.number(), content: faker.lorem.text() },
+ ],
+ },
+ {
+ id: faker.datatype.number(),
+ title: faker.lorem.sentence(),
+ comments: null,
+ },
+ ],
+ },
+ comments: [
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.paragraph(),
+ author: {
+ id: faker.datatype.number(),
+ email: faker.internet.email(),
+ },
+ },
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.paragraph(),
+ author: null,
+ },
+ ],
+ },
+ {
+ id: faker.datatype.number(),
+ title: faker.lorem.sentence(),
+ author: null,
+ comments: null,
+ },
+ ];
+ const next = jest.fn(() => Promise.resolve(clientResult));
+ const result = await nestedMiddleware(params, next);
+
+ expect(result).toEqual(clientResult);
+ });
+
+ it("returns correct result when relations are selected", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ return next(params);
+ });
+
+ const params = createParams("Post", "findMany", {
+ where: { title: faker.lorem.sentence() },
+ select: {
+ content: true,
+ author: {
+ select: {
+ email: true,
+ profile: { select: { bio: true } },
+ posts: {
+ select: {
+ title: true,
+ comments: {
+ select: { content: true },
+ },
+ },
+ },
+ },
+ },
+ comments: {
+ select: { author: true },
+ },
+ },
+ });
+
+ const clientResult = [
+ {
+ title: faker.lorem.sentence(),
+ author: {
+ email: faker.internet.email(),
+ profile: null,
+ posts: [
+ {
+ title: faker.lorem.sentence(),
+ comments: [
+ { content: faker.lorem.text() },
+ { content: faker.lorem.text() },
+ { content: faker.lorem.text() },
+ ],
+ },
+ { title: faker.lorem.sentence(), comments: null },
+ ],
+ },
+ comments: [
+ {
+ author: {
+ id: faker.datatype.number(),
+ email: faker.internet.email(),
+ },
+ },
+ { author: null },
+ ],
+ },
+ {
+ title: faker.lorem.sentence(),
+ author: null,
+ comments: null,
+ },
+ ];
+ const next = jest.fn(() => Promise.resolve(clientResult));
+ const result = await nestedMiddleware(params, next);
+
+ expect(result).toEqual(clientResult);
+ });
+
+ it("returns correct result when relations are included and selected", async () => {
+ const nestedMiddleware = createNestedMiddleware((params, next) => {
+ return next(params);
+ });
+
+ const params = createParams("Post", "findMany", {
+ where: { title: faker.lorem.sentence() },
+ include: {
+ author: {
+ select: {
+ email: true,
+ profile: { select: { bio: true } },
+ posts: {
+ include: {
+ comments: true,
+ },
+ },
+ },
+ },
+ comments: {
+ select: { author: true },
+ },
+ },
+ });
+
+ const clientResult = [
+ {
+ id: faker.datatype.number(),
+ title: faker.lorem.sentence(),
+ author: {
+ email: faker.internet.email(),
+ profile: null,
+ posts: [
+ {
+ id: faker.datatype.number(),
+ title: faker.lorem.sentence(),
+ comments: [
+ { content: faker.lorem.text() },
+ { content: faker.lorem.text() },
+ { content: faker.lorem.text() },
+ ],
+ },
+ {
+ id: faker.datatype.number(),
+ title: faker.lorem.sentence(),
+ comments: null,
+ },
+ ],
+ },
+ comments: [
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.text(),
+ author: {
+ id: faker.datatype.number(),
+ email: faker.internet.email(),
+ },
+ },
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.text(),
+ author: null,
+ },
+ ],
+ },
+ {
+ id: faker.datatype.number(),
+ title: faker.lorem.sentence(),
+ author: null,
+ comments: null,
+ },
+ ];
+ const next = jest.fn(() => Promise.resolve(clientResult));
+ const result = await nestedMiddleware(params, next);
+
+ expect(result).toEqual(clientResult);
+ });
+
+ it("supports modifying root result", async () => {
+ const nestedMiddleware = createNestedMiddleware(async (params, next) => {
+ const result = await next(params);
+ return addReturnedDate(result);
+ });
+
+ const params = createParams("User", "create", {
+ data: { email: faker.internet.email() },
+ });
+ const next = jest.fn(() =>
+ Promise.resolve({
+ id: faker.datatype.number(),
+ email: params.args.data.email,
+ })
+ );
+ const result = await nestedMiddleware(params, next);
+
+ expect(result).toEqual({
+ id: expect.any(Number),
+ email: params.args.data.email,
+ returned: expect.any(Date),
+ });
+ });
+
+ it("supports modifying root result asynchronously", async () => {
+ const nestedMiddleware = createNestedMiddleware(async (params, next) => {
+ const result = await next(params);
+ await wait(100);
+ return addReturnedDate(result);
+ });
+
+ const params = createParams("User", "create", {
+ data: { email: faker.internet.email() },
+ });
+ const next = jest.fn(() =>
+ Promise.resolve({
+ id: faker.datatype.number(),
+ email: params.args.data.email,
+ })
+ );
+ const result = await nestedMiddleware(params, next);
+
+ expect(result).toEqual({
+ id: expect.any(Number),
+ email: params.args.data.email,
+ returned: expect.any(Date),
+ });
+ });
+
+ it("supports modifying included results", async () => {
+ const nestedMiddleware = createNestedMiddleware(async (params, next) => {
+ const result = await next(params);
+ if (!result) return;
+
+ if (params.action === "include" && params.model === "Post") {
+ return addReturnedDate(result);
+ }
+
+ return result;
+ });
+
+ const next = jest.fn((params) =>
+ Promise.resolve({
+ id: faker.datatype.number(),
+ email: params.args.data.email,
+ posts: [
+ {
+ id: faker.datatype.number(),
+ title: params.args.data.posts.create.title,
+ },
+ ],
+ })
+ );
+ const params = createParams("User", "create", {
+ data: {
+ email: faker.internet.email(),
+ posts: { create: { title: faker.lorem.sentence() } },
+ },
+ include: {
+ posts: true,
+ },
+ });
+ const result = await nestedMiddleware(params, next);
+
+ expect(result).toEqual({
+ id: expect.any(Number),
+ email: params.args.data.email,
+ posts: [
+ {
+ id: expect.any(Number),
+ title: params.args.data.posts.create.title,
+ returned: expect.any(Date),
+ },
+ ],
+ });
+ });
+
+ it("supports modifying included results asynchronously", async () => {
+ const nestedMiddleware = createNestedMiddleware(async (params, next) => {
+ const result = await next(params);
+ if (!result) return;
+
+ if (params.action === "include" && params.model === "Post") {
+ await wait(100);
+ return addReturnedDate(result);
+ }
+
+ return result;
+ });
+
+ const next = jest.fn((params) =>
+ Promise.resolve({
+ id: faker.datatype.number(),
+ email: params.args.data.email,
+ posts: [
+ {
+ id: faker.datatype.number(),
+ title: params.args.data.posts.create.title,
+ },
+ ],
+ })
+ );
+ const params = createParams("User", "create", {
+ data: {
+ email: faker.internet.email(),
+ posts: { create: { title: faker.lorem.sentence() } },
+ },
+ include: {
+ posts: true,
+ },
+ });
+ const result = await nestedMiddleware(params, next);
+
+ expect(result).toEqual({
+ id: expect.any(Number),
+ email: params.args.data.email,
+ posts: [
+ {
+ id: expect.any(Number),
+ title: params.args.data.posts.create.title,
+ returned: expect.any(Date),
+ },
+ ],
+ });
+ });
+
+ it("supports modifying selected results", async () => {
+ const nestedMiddleware = createNestedMiddleware(async (params, next) => {
+ const result = await next(params);
+ if (!result) return;
+
+ if (params.action === "select" && params.model === "Post") {
+ return addReturnedDate(result);
+ }
+
+ return result;
+ });
+
+ const next = jest.fn((params) =>
+ Promise.resolve({
+ id: faker.datatype.number(),
+ email: params.args.data.email,
+ posts: [
+ {
+ id: faker.datatype.number(),
+ title: params.args.data.posts.create.title,
+ },
+ ],
+ })
+ );
+ const params = createParams("User", "create", {
+ data: {
+ email: faker.internet.email(),
+ posts: { create: { title: faker.lorem.sentence() } },
+ },
+ select: {
+ posts: true,
+ },
+ });
+ const result = await nestedMiddleware(params, next);
+
+ expect(result).toEqual({
+ id: expect.any(Number),
+ email: params.args.data.email,
+ posts: [
+ {
+ id: expect.any(Number),
+ title: params.args.data.posts.create.title,
+ returned: expect.any(Date),
+ },
+ ],
+ });
+ });
+
+ it("supports modifying selected results asynchronously", async () => {
+ const nestedMiddleware = createNestedMiddleware(async (params, next) => {
+ const result = await next(params);
+ if (!result) return;
+
+ if (params.action === "select" && params.model === "Post") {
+ await wait(100);
+ return addReturnedDate(result);
+ }
+
+ return result;
+ });
+
+ const next = jest.fn((params) =>
+ Promise.resolve({
+ id: faker.datatype.number(),
+ email: params.args.data.email,
+ posts: [
+ {
+ id: faker.datatype.number(),
+ title: params.args.data.posts.create.title,
+ },
+ ],
+ })
+ );
+ const params = createParams("User", "create", {
+ data: {
+ email: faker.internet.email(),
+ posts: { create: { title: faker.lorem.sentence() } },
+ },
+ select: {
+ posts: true,
+ },
+ });
+ const result = await nestedMiddleware(params, next);
+
+ expect(result).toEqual({
+ id: expect.any(Number),
+ email: params.args.data.email,
+ posts: [
+ {
+ id: expect.any(Number),
+ title: params.args.data.posts.create.title,
+ returned: expect.any(Date),
+ },
+ ],
+ });
+ });
+
+ it("supports modifying multiple included relations", async () => {
+ const nestedMiddleware = createNestedMiddleware(async (params, next) => {
+ const result = await next(params);
+ if (!result || !params.model) return;
+
+ if (
+ params.action === "include" &&
+ ["Post", "Profile"].includes(params.model)
+ ) {
+ return addReturnedDate(result);
+ }
+
+ return result;
+ });
+
+ const next = jest.fn(() =>
+ Promise.resolve({
+ id: faker.datatype.number(),
+ email: faker.internet.email(),
+ profile: {
+ id: faker.datatype.number(),
+ bio: faker.lorem.text(),
+ },
+ posts: [
+ {
+ id: faker.datatype.number(),
+ title: faker.lorem.sentence(),
+ },
+ {
+ id: faker.datatype.number(),
+ title: faker.lorem.sentence(),
+ },
+ ],
+ })
+ );
+ const params = createParams("User", "findUnique", {
+ where: { id: faker.datatype.number() },
+ include: {
+ profile: true,
+ posts: true,
+ },
+ });
+ const result = await nestedMiddleware(params, next);
+
+ expect(result).toEqual({
+ id: expect.any(Number),
+ email: expect.any(String),
+ profile: {
+ id: expect.any(Number),
+ bio: expect.any(String),
+ returned: expect.any(Date),
+ },
+ posts: [
+ {
+ id: expect.any(Number),
+ title: expect.any(String),
+ returned: expect.any(Date),
+ },
+ {
+ id: expect.any(Number),
+ title: expect.any(String),
+ returned: expect.any(Date),
+ },
+ ],
+ });
+ });
+
+ it("supports modifying multiple included relations asynchronously", async () => {
+ const nestedMiddleware = createNestedMiddleware(async (params, next) => {
+ const result = await next(params);
+ if (!result || !params.model) return;
+
+ if (
+ params.action === "include" &&
+ ["Post", "Profile"].includes(params.model)
+ ) {
+ await wait(100);
+ return addReturnedDate(result);
+ }
+
+ return result;
+ });
+
+ const next = jest.fn(() =>
+ Promise.resolve({
+ id: faker.datatype.number(),
+ email: faker.internet.email(),
+ profile: {
+ id: faker.datatype.number(),
+ bio: faker.lorem.text(),
+ },
+ posts: [
+ {
+ id: faker.datatype.number(),
+ title: faker.lorem.sentence(),
+ },
+ {
+ id: faker.datatype.number(),
+ title: faker.lorem.sentence(),
+ },
+ ],
+ })
+ );
+ const params = createParams("User", "findUnique", {
+ where: { id: faker.datatype.number() },
+ include: {
+ profile: true,
+ posts: true,
+ },
+ });
+ const result = await nestedMiddleware(params, next);
+
+ expect(result).toEqual({
+ id: expect.any(Number),
+ email: expect.any(String),
+ profile: {
+ id: expect.any(Number),
+ bio: expect.any(String),
+ returned: expect.any(Date),
+ },
+ posts: [
+ {
+ id: expect.any(Number),
+ title: expect.any(String),
+ returned: expect.any(Date),
+ },
+ {
+ id: expect.any(Number),
+ title: expect.any(String),
+ returned: expect.any(Date),
+ },
+ ],
+ });
+ });
+
+ it("supports modifying multiple selected relations", async () => {
+ const nestedMiddleware = createNestedMiddleware(async (params, next) => {
+ const result = await next(params);
+ if (!result || !params.model) return;
+
+ if (
+ params.action === "select" &&
+ ["Post", "Profile"].includes(params.model)
+ ) {
+ return addReturnedDate(result);
+ }
+
+ return result;
+ });
+
+ const next = jest.fn(() =>
+ Promise.resolve({
+ id: faker.datatype.number(),
+ email: faker.internet.email(),
+ profile: {
+ id: faker.datatype.number(),
+ bio: faker.lorem.text(),
+ },
+ posts: [
+ {
+ id: faker.datatype.number(),
+ title: faker.lorem.sentence(),
+ },
+ {
+ id: faker.datatype.number(),
+ title: faker.lorem.sentence(),
+ },
+ ],
+ })
+ );
+ const params = createParams("User", "findUnique", {
+ where: { id: faker.datatype.number() },
+ select: {
+ profile: true,
+ posts: true,
+ },
+ });
+ const result = await nestedMiddleware(params, next);
+
+ expect(result).toEqual({
+ id: expect.any(Number),
+ email: expect.any(String),
+ profile: {
+ id: expect.any(Number),
+ bio: expect.any(String),
+ returned: expect.any(Date),
+ },
+ posts: [
+ {
+ id: expect.any(Number),
+ title: expect.any(String),
+ returned: expect.any(Date),
+ },
+ {
+ id: expect.any(Number),
+ title: expect.any(String),
+ returned: expect.any(Date),
+ },
+ ],
+ });
+ });
+
+ it("supports modifying multiple selected relations asynchronously", async () => {
+ const nestedMiddleware = createNestedMiddleware(async (params, next) => {
+ const result = await next(params);
+ if (!result || !params.model) return;
+
+ if (
+ params.action === "select" &&
+ ["Post", "Profile"].includes(params.model)
+ ) {
+ await wait(100);
+ return addReturnedDate(result);
+ }
+
+ return result;
+ });
+
+ const next = jest.fn(() =>
+ Promise.resolve({
+ id: faker.datatype.number(),
+ email: faker.internet.email(),
+ profile: {
+ id: faker.datatype.number(),
+ bio: faker.lorem.text(),
+ },
+ posts: [
+ {
+ id: faker.datatype.number(),
+ title: faker.lorem.sentence(),
+ },
+ {
+ id: faker.datatype.number(),
+ title: faker.lorem.sentence(),
+ },
+ ],
+ })
+ );
+ const params = createParams("User", "findUnique", {
+ where: { id: faker.datatype.number() },
+ select: {
+ profile: true,
+ posts: true,
+ },
+ });
+ const result = await nestedMiddleware(params, next);
+
+ expect(result).toEqual({
+ id: expect.any(Number),
+ email: expect.any(String),
+ profile: {
+ id: expect.any(Number),
+ bio: expect.any(String),
+ returned: expect.any(Date),
+ },
+ posts: [
+ {
+ id: expect.any(Number),
+ title: expect.any(String),
+ returned: expect.any(Date),
+ },
+ {
+ id: expect.any(Number),
+ title: expect.any(String),
+ returned: expect.any(Date),
+ },
+ ],
+ });
+ });
+
+ it("supports modifying deeply included results", async () => {
+ const nestedMiddleware = createNestedMiddleware(async (params, next) => {
+ const result = await next(params);
+ if (!result) return;
+
+ if (params.action === "include" && params.model === "Comment") {
+ return addReturnedDate(result);
+ }
+
+ return result;
+ });
+
+ const next = jest.fn((params) =>
+ Promise.resolve({
+ id: faker.datatype.number(),
+ email: params.args.data.email,
+ posts: [
+ {
+ id: faker.datatype.number(),
+ title: params.args.data.posts.create.title,
+ comments: [
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ replies: [
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ },
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ })
+ );
+ const params = createParams("User", "create", {
+ data: {
+ email: faker.internet.email(),
+ posts: { create: { title: faker.lorem.sentence() } },
+ },
+ include: {
+ posts: {
+ include: {
+ comments: {
+ include: {
+ replies: true,
+ },
+ },
+ },
+ },
+ },
+ });
+ const result = await nestedMiddleware(params, next);
+
+ expect(result).toEqual({
+ id: expect.any(Number),
+ email: params.args.data.email,
+ posts: [
+ {
+ id: expect.any(Number),
+ title: params.args.data.posts.create.title,
+ comments: [
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ returned: expect.any(Date),
+ replies: [
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ returned: expect.any(Date),
+ },
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ returned: expect.any(Date),
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ });
+ });
+
+ it("supports modifying deeply included results asynchronously", async () => {
+ const nestedMiddleware = createNestedMiddleware(async (params, next) => {
+ const result = await next(params);
+ if (!result) return;
+
+ if (params.action === "include" && params.model === "Comment") {
+ await wait(100);
+ return addReturnedDate(result);
+ }
+
+ return result;
+ });
+
+ const next = jest.fn((params) =>
+ Promise.resolve({
+ id: faker.datatype.number(),
+ email: params.args.data.email,
+ posts: [
+ {
+ id: faker.datatype.number(),
+ title: params.args.data.posts.create.title,
+ comments: [
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ replies: [
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ },
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ })
+ );
+ const params = createParams("User", "create", {
+ data: {
+ email: faker.internet.email(),
+ posts: { create: { title: faker.lorem.sentence() } },
+ },
+ include: {
+ posts: {
+ include: {
+ comments: {
+ include: {
+ replies: true,
+ },
+ },
+ },
+ },
+ },
+ });
+ const result = await nestedMiddleware(params, next);
+
+ expect(result).toEqual({
+ id: expect.any(Number),
+ email: params.args.data.email,
+ posts: [
+ {
+ id: expect.any(Number),
+ title: params.args.data.posts.create.title,
+ comments: [
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ returned: expect.any(Date),
+ replies: [
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ returned: expect.any(Date),
+ },
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ returned: expect.any(Date),
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ });
+ });
+
+ it("supports modifying deeply selected results", async () => {
+ const nestedMiddleware = createNestedMiddleware(async (params, next) => {
+ const result = await next(params);
+ if (!result) return;
+
+ if (params.action === "select" && params.model === "Comment") {
+ return addReturnedDate(result);
+ }
+
+ return result;
+ });
+
+ const next = jest.fn((params) =>
+ Promise.resolve({
+ id: faker.datatype.number(),
+ email: params.args.data.email,
+ posts: [
+ {
+ id: faker.datatype.number(),
+ title: params.args.data.posts.create.title,
+ comments: [
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ replies: [
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ },
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ })
+ );
+ const params = createParams("User", "create", {
+ data: {
+ email: faker.internet.email(),
+ posts: { create: { title: faker.lorem.sentence() } },
+ },
+ include: {
+ posts: {
+ select: {
+ comments: {
+ select: {
+ replies: true,
+ },
+ },
+ },
+ },
+ },
+ });
+ const result = await nestedMiddleware(params, next);
+
+ expect(result).toEqual({
+ id: expect.any(Number),
+ email: params.args.data.email,
+ posts: [
+ {
+ id: expect.any(Number),
+ title: params.args.data.posts.create.title,
+ comments: [
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ returned: expect.any(Date),
+ replies: [
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ returned: expect.any(Date),
+ },
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ returned: expect.any(Date),
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ });
+ });
+
+ it("supports modifying deeply selected results asynchronously", async () => {
+ const nestedMiddleware = createNestedMiddleware(async (params, next) => {
+ const result = await next(params);
+ if (!result) return;
+
+ if (params.action === "select" && params.model === "Comment") {
+ await wait(100);
+ return addReturnedDate(result);
+ }
+
+ return result;
+ });
+
+ const next = jest.fn((params) =>
+ Promise.resolve({
+ id: faker.datatype.number(),
+ email: params.args.data.email,
+ posts: [
+ {
+ id: faker.datatype.number(),
+ title: params.args.data.posts.create.title,
+ comments: [
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ replies: [
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ },
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ })
+ );
+ const params = createParams("User", "create", {
+ data: {
+ email: faker.internet.email(),
+ posts: { create: { title: faker.lorem.sentence() } },
+ },
+ include: {
+ posts: {
+ select: {
+ comments: {
+ select: {
+ replies: true,
+ },
+ },
+ },
+ },
+ },
+ });
+ const result = await nestedMiddleware(params, next);
+
+ expect(result).toEqual({
+ id: expect.any(Number),
+ email: params.args.data.email,
+ posts: [
+ {
+ id: expect.any(Number),
+ title: params.args.data.posts.create.title,
+ comments: [
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ returned: expect.any(Date),
+ replies: [
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ returned: expect.any(Date),
+ },
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ returned: expect.any(Date),
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ });
+ });
+
+ it("supports modifying deeply included results through multiple relations", async () => {
+ const nestedMiddleware = createNestedMiddleware(async (params, next) => {
+ const result = await next(params);
+ if (!result || !params.model) return;
+
+ if (
+ params.action === "include" &&
+ ["Post", "Profile", "Comment"].includes(params.model)
+ ) {
+ return addReturnedDate(result);
+ }
+
+ return result;
+ });
+
+ const next = jest.fn(() =>
+ Promise.resolve({
+ id: faker.datatype.number(),
+ email: faker.internet.email(),
+ profile: {
+ id: faker.datatype.number(),
+ bio: faker.lorem.text(),
+ user: {
+ id: faker.datatype.number(),
+ email: faker.internet.email(),
+ comments: [
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ },
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ },
+ ],
+ },
+ },
+ posts: [
+ {
+ id: faker.datatype.number(),
+ title: faker.lorem.sentence(),
+ comments: [
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ replies: [
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ },
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ })
+ );
+ const params = createParams("User", "create", {
+ data: {
+ email: faker.internet.email(),
+ posts: { create: { title: faker.lorem.sentence() } },
+ },
+ include: {
+ profile: {
+ include: {
+ user: {
+ include: {
+ comments: true,
+ },
+ },
+ },
+ },
+ posts: {
+ include: {
+ comments: {
+ include: {
+ replies: true,
+ },
+ },
+ },
+ },
+ },
+ });
+ const result = await nestedMiddleware(params, next);
+
+ expect(result).toEqual({
+ id: expect.any(Number),
+ email: expect.any(String),
+ profile: {
+ id: expect.any(Number),
+ bio: expect.any(String),
+ returned: expect.any(Date),
+ user: {
+ id: expect.any(Number),
+ email: expect.any(String),
+ comments: [
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ returned: expect.any(Date),
+ },
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ returned: expect.any(Date),
+ },
+ ],
+ },
+ },
+ posts: [
+ {
+ id: expect.any(Number),
+ title: expect.any(String),
+ returned: expect.any(Date),
+ comments: [
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ returned: expect.any(Date),
+ replies: [
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ returned: expect.any(Date),
+ },
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ returned: expect.any(Date),
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ });
+ });
+
+ it("supports modifying deeply included results through multiple relations asynchronously", async () => {
+ const nestedMiddleware = createNestedMiddleware(async (params, next) => {
+ const result = await next(params);
+ if (!result || !params.model) return;
+
+ if (
+ params.action === "include" &&
+ ["Post", "Profile", "Comment"].includes(params.model)
+ ) {
+ await wait(100);
+ return addReturnedDate(result);
+ }
+
+ return result;
+ });
+
+ const next = jest.fn(() =>
+ Promise.resolve({
+ id: faker.datatype.number(),
+ email: faker.internet.email(),
+ profile: {
+ id: faker.datatype.number(),
+ bio: faker.lorem.text(),
+ user: {
+ id: faker.datatype.number(),
+ email: faker.internet.email(),
+ comments: [
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ },
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ },
+ ],
+ },
+ },
+ posts: [
+ {
+ id: faker.datatype.number(),
+ title: faker.lorem.sentence(),
+ comments: [
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ replies: [
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ },
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ })
+ );
+ const params = createParams("User", "create", {
+ data: {
+ email: faker.internet.email(),
+ posts: { create: { title: faker.lorem.sentence() } },
+ },
+ include: {
+ profile: {
+ include: {
+ user: {
+ include: {
+ comments: true,
+ },
+ },
+ },
+ },
+ posts: {
+ include: {
+ comments: {
+ include: {
+ replies: true,
+ },
+ },
+ },
+ },
+ },
+ });
+ const result = await nestedMiddleware(params, next);
+
+ expect(result).toEqual({
+ id: expect.any(Number),
+ email: expect.any(String),
+ profile: {
+ id: expect.any(Number),
+ bio: expect.any(String),
+ returned: expect.any(Date),
+ user: {
+ id: expect.any(Number),
+ email: expect.any(String),
+ comments: [
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ returned: expect.any(Date),
+ },
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ returned: expect.any(Date),
+ },
+ ],
+ },
+ },
+ posts: [
+ {
+ id: expect.any(Number),
+ title: expect.any(String),
+ returned: expect.any(Date),
+ comments: [
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ returned: expect.any(Date),
+ replies: [
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ returned: expect.any(Date),
+ },
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ returned: expect.any(Date),
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ });
+ });
+
+ it("supports modifying deeply selected results through multiple relations", async () => {
+ const nestedMiddleware = createNestedMiddleware(async (params, next) => {
+ const result = await next(params);
+ if (!result || !params.model) return;
+
+ if (
+ params.action === "select" &&
+ ["Post", "Profile", "Comment"].includes(params.model)
+ ) {
+ return addReturnedDate(result);
+ }
+
+ return result;
+ });
+
+ const next = jest.fn(() =>
+ Promise.resolve({
+ id: faker.datatype.number(),
+ email: faker.internet.email(),
+ profile: {
+ id: faker.datatype.number(),
+ bio: faker.lorem.text(),
+ user: {
+ id: faker.datatype.number(),
+ email: faker.internet.email(),
+ comments: [
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ },
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ },
+ ],
+ },
+ },
+ posts: [
+ {
+ id: faker.datatype.number(),
+ title: faker.lorem.sentence(),
+ comments: [
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ replies: [
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ },
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ })
+ );
+ const params = createParams("User", "create", {
+ data: {
+ email: faker.internet.email(),
+ posts: { create: { title: faker.lorem.sentence() } },
+ },
+ select: {
+ profile: {
+ select: {
+ user: {
+ select: {
+ comments: true,
+ },
+ },
+ },
+ },
+ posts: {
+ select: {
+ comments: {
+ select: {
+ replies: true,
+ },
+ },
+ },
+ },
+ },
+ });
+ const result = await nestedMiddleware(params, next);
+
+ expect(result).toEqual({
+ id: expect.any(Number),
+ email: expect.any(String),
+ profile: {
+ id: expect.any(Number),
+ bio: expect.any(String),
+ returned: expect.any(Date),
+ user: {
+ id: expect.any(Number),
+ email: expect.any(String),
+ comments: [
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ returned: expect.any(Date),
+ },
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ returned: expect.any(Date),
+ },
+ ],
+ },
+ },
+ posts: [
+ {
+ id: expect.any(Number),
+ title: expect.any(String),
+ returned: expect.any(Date),
+ comments: [
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ returned: expect.any(Date),
+ replies: [
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ returned: expect.any(Date),
+ },
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ returned: expect.any(Date),
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ });
+ });
+
+ it("supports modifying deeply selected results through multiple relations asynchronously", async () => {
+ const nestedMiddleware = createNestedMiddleware(async (params, next) => {
+ const result = await next(params);
+ if (!result || !params.model) return;
+
+ if (
+ params.action === "select" &&
+ ["Post", "Profile", "Comment"].includes(params.model)
+ ) {
+ await wait(100);
+ return addReturnedDate(result);
+ }
+
+ return result;
+ });
+
+ const next = jest.fn(() =>
+ Promise.resolve({
+ id: faker.datatype.number(),
+ email: faker.internet.email(),
+ profile: {
+ id: faker.datatype.number(),
+ bio: faker.lorem.text(),
+ user: {
+ id: faker.datatype.number(),
+ email: faker.internet.email(),
+ comments: [
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ },
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ },
+ ],
+ },
+ },
+ posts: [
+ {
+ id: faker.datatype.number(),
+ title: faker.lorem.sentence(),
+ comments: [
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ replies: [
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ },
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ })
+ );
+ const params = createParams("User", "create", {
+ data: {
+ email: faker.internet.email(),
+ posts: { create: { title: faker.lorem.sentence() } },
+ },
+ select: {
+ profile: {
+ select: {
+ user: {
+ select: {
+ comments: true,
+ },
+ },
+ },
+ },
+ posts: {
+ select: {
+ comments: {
+ select: {
+ replies: true,
+ },
+ },
+ },
+ },
+ },
+ });
+ const result = await nestedMiddleware(params, next);
+
+ expect(result).toEqual({
+ id: expect.any(Number),
+ email: expect.any(String),
+ profile: {
+ id: expect.any(Number),
+ bio: expect.any(String),
+ returned: expect.any(Date),
+ user: {
+ id: expect.any(Number),
+ email: expect.any(String),
+ comments: [
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ returned: expect.any(Date),
+ },
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ returned: expect.any(Date),
+ },
+ ],
+ },
+ },
+ posts: [
+ {
+ id: expect.any(Number),
+ title: expect.any(String),
+ returned: expect.any(Date),
+ comments: [
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ returned: expect.any(Date),
+ replies: [
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ returned: expect.any(Date),
+ },
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ returned: expect.any(Date),
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ });
+ });
+
+ it("supports modifying selected results in nested include", async () => {
+ const nestedMiddleware = createNestedMiddleware(async (params, next) => {
+ const result = await next(params);
+ if (!result) return;
+
+ if (params.action === "select" && params.model === "Comment") {
+ return addReturnedDate(result);
+ }
+
+ return result;
+ });
+
+ const next = jest.fn((params) =>
+ Promise.resolve({
+ id: faker.datatype.number(),
+ email: params.args.data.email,
+ posts: [
+ {
+ id: faker.datatype.number(),
+ title: params.args.data.posts.create.title,
+ comments: [
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ },
+ ],
+ },
+ ],
+ })
+ );
+ const params = createParams("User", "create", {
+ data: {
+ email: faker.internet.email(),
+ posts: { create: { title: faker.lorem.sentence() } },
+ },
+ include: {
+ posts: {
+ select: {
+ comments: true,
+ },
+ },
+ },
+ });
+ const result = await nestedMiddleware(params, next);
+
+ expect(result).toEqual({
+ id: expect.any(Number),
+ email: params.args.data.email,
+ posts: [
+ {
+ id: expect.any(Number),
+ title: params.args.data.posts.create.title,
+ comments: [
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ returned: expect.any(Date),
+ },
+ ],
+ },
+ ],
+ });
+ });
+
+ it("supports modifying selected results in nested include asynchronously", async () => {
+ const nestedMiddleware = createNestedMiddleware(async (params, next) => {
+ const result = await next(params);
+ if (!result) return;
+
+ if (params.action === "select" && params.model === "Comment") {
+ return addReturnedDate(result);
+ }
+
+ return result;
+ });
+
+ const next = jest.fn((params) =>
+ Promise.resolve({
+ id: faker.datatype.number(),
+ email: params.args.data.email,
+ posts: [
+ {
+ id: faker.datatype.number(),
+ title: params.args.data.posts.create.title,
+ comments: [
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ },
+ ],
+ },
+ ],
+ })
+ );
+ const params = createParams("User", "create", {
+ data: {
+ email: faker.internet.email(),
+ posts: { create: { title: faker.lorem.sentence() } },
+ },
+ include: {
+ posts: {
+ select: {
+ comments: true,
+ },
+ },
+ },
+ });
+ const result = await nestedMiddleware(params, next);
+
+ expect(result).toEqual({
+ id: expect.any(Number),
+ email: params.args.data.email,
+ posts: [
+ {
+ id: expect.any(Number),
+ title: params.args.data.posts.create.title,
+ comments: [
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ returned: expect.any(Date),
+ },
+ ],
+ },
+ ],
+ });
+ });
+
+ it("supports modifying deeply included results in nested select", async () => {
+ const nestedMiddleware = createNestedMiddleware(async (params, next) => {
+ const result = await next(params);
+ if (!result) return;
+
+ if (params.action === "include" && params.model === "Comment") {
+ return addReturnedDate(result);
+ }
+
+ return result;
+ });
+
+ const next = jest.fn((params) =>
+ Promise.resolve({
+ id: faker.datatype.number(),
+ email: params.args.data.email,
+ posts: [
+ {
+ id: faker.datatype.number(),
+ title: params.args.data.posts.create.title,
+ comments: [
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ replies: [
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ })
+ );
+ const params = createParams("User", "create", {
+ data: {
+ email: faker.internet.email(),
+ posts: { create: { title: faker.lorem.sentence() } },
+ },
+ include: {
+ posts: {
+ select: {
+ comments: {
+ include: {
+ replies: true,
+ },
+ },
+ },
+ },
+ },
+ });
+ const result = await nestedMiddleware(params, next);
+
+ expect(result).toEqual({
+ id: expect.any(Number),
+ email: params.args.data.email,
+ posts: [
+ {
+ id: expect.any(Number),
+ title: params.args.data.posts.create.title,
+ comments: [
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ replies: [
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ returned: expect.any(Date),
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ });
+ });
+
+ it("supports modifying deeply included results in nested select asynchronously", async () => {
+ const nestedMiddleware = createNestedMiddleware(async (params, next) => {
+ const result = await next(params);
+ if (!result) return;
+
+ if (params.action === "include" && params.model === "Comment") {
+ await wait(100);
+ return addReturnedDate(result);
+ }
+
+ return result;
+ });
+
+ const next = jest.fn((params) =>
+ Promise.resolve({
+ id: faker.datatype.number(),
+ email: params.args.data.email,
+ posts: [
+ {
+ id: faker.datatype.number(),
+ title: params.args.data.posts.create.title,
+ comments: [
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ replies: [
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.sentence(),
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ })
+ );
+ const params = createParams("User", "create", {
+ data: {
+ email: faker.internet.email(),
+ posts: { create: { title: faker.lorem.sentence() } },
+ },
+ include: {
+ posts: {
+ select: {
+ comments: {
+ include: {
+ replies: true,
+ },
+ },
+ },
+ },
+ },
+ });
+ const result = await nestedMiddleware(params, next);
+
+ expect(result).toEqual({
+ id: expect.any(Number),
+ email: params.args.data.email,
+ posts: [
+ {
+ id: expect.any(Number),
+ title: params.args.data.posts.create.title,
+ comments: [
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ replies: [
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ returned: expect.any(Date),
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ });
+ });
+
+ it("supports filtering nested toOne relations", async () => {
+ const nestedMiddleware = createNestedMiddleware(async (params, next) => {
+ if (params.model === "User" && !params.scope?.relations.to.isList) {
+ await next(params);
+ return null;
+ }
+ return next(params);
+ });
+
+ const params = createParams("Post", "findFirst", {
+ where: { id: faker.datatype.number() },
+ include: { author: true },
+ });
+
+ const next = jest.fn(() =>
+ Promise.resolve({
+ id: faker.datatype.number(),
+ title: faker.lorem.sentence(),
+ author: {
+ id: faker.datatype.number(),
+ email: faker.internet.email(),
+ },
+ })
+ );
+
+ const result = await nestedMiddleware(params, next);
+
+ expect(result).toEqual({
+ id: expect.any(Number),
+ title: expect.any(String),
+ author: null,
+ });
+ });
+
+ it("supports filtering nested toOne relations in list results", async () => {
+ const nestedMiddleware = createNestedMiddleware(async (params, next) => {
+ if (params.model === "User" && !params.scope?.relations.to.isList) {
+ const results = await next(params);
+ return results.filter((result: any) => !result?.deleted);
+ }
+ return next(params);
+ });
+
+ const params = createParams("Post", "findMany", {
+ where: { id: faker.datatype.number() },
+ include: { author: true },
+ });
+
+ const next = jest.fn(() =>
+ Promise.resolve([
+ {
+ id: faker.datatype.number(),
+ title: faker.lorem.sentence(),
+ author: {
+ id: faker.datatype.number(),
+ email: faker.internet.email(),
+ deleted: true,
+ },
+ },
+ {
+ id: faker.datatype.number(),
+ title: faker.lorem.sentence(),
+ author: {
+ id: faker.datatype.number(),
+ email: faker.internet.email(),
+ },
+ },
+ {
+ id: faker.datatype.number(),
+ title: faker.lorem.sentence(),
+ author: null,
+ },
+ ])
+ );
+
+ const result = await nestedMiddleware(params, next);
+
+ expect(result).toEqual([
+ {
+ id: expect.any(Number),
+ title: expect.any(String),
+ author: null,
+ },
+ {
+ id: expect.any(Number),
+ title: expect.any(String),
+ author: {
+ id: expect.any(Number),
+ email: expect.any(String),
+ },
+ },
+ {
+ id: expect.any(Number),
+ title: expect.any(String),
+ author: null,
+ },
+ ]);
+ });
+
+ it("supports filtering nested toOne relations nested in toOne result", async () => {
+ const nestedMiddleware = createNestedMiddleware(async (params, next) => {
+ if (params.model === "Profile") {
+ await next(params);
+ return [];
+ }
+ return next(params);
+ });
+
+ const params = createParams("Post", "findMany", {
+ where: { id: faker.datatype.number() },
+ include: {
+ author: {
+ include: { profile: true },
+ },
+ },
+ });
+
+ const next = jest.fn(() =>
+ Promise.resolve([
+ {
+ id: faker.datatype.number(),
+ title: faker.lorem.sentence(),
+ author: {
+ id: faker.datatype.number(),
+ email: faker.internet.email(),
+ profile: {
+ id: faker.datatype.number(),
+ bio: faker.lorem.paragraph(),
+ },
+ },
+ },
+ ])
+ );
+
+ const result = await nestedMiddleware(params, next);
+
+ expect(result).toEqual([
+ {
+ id: expect.any(Number),
+ title: expect.any(String),
+ author: {
+ id: expect.any(Number),
+ email: expect.any(String),
+ profile: null,
+ },
+ },
+ ]);
+ });
+
+ it("supports filtering nested toOne relations nested in toMany results", async () => {
+ const nestedMiddleware = createNestedMiddleware(async (params, next) => {
+ if (params.model === "User" && !params.scope?.relations.to.isList) {
+ await next(params);
+ return [];
+ }
+ return next(params);
+ });
+
+ const params = createParams("Post", "findUnique", {
+ where: { id: faker.datatype.number() },
+ include: {
+ comments: {
+ include: { author: true },
+ },
+ },
+ });
+
+ const next = jest.fn(() =>
+ Promise.resolve({
+ id: faker.datatype.number(),
+ title: faker.lorem.sentence(),
+ comments: [
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.paragraph(),
+ author: {
+ id: faker.datatype.number(),
+ email: faker.internet.email(),
+ },
+ },
+ {
+ id: faker.datatype.number(),
+ content: faker.lorem.paragraph(),
+ author: {
+ id: faker.datatype.number(),
+ email: faker.internet.email(),
+ },
+ },
+ ],
+ })
+ );
+
+ const result = await nestedMiddleware(params, next);
+
+ expect(result).toEqual({
+ id: expect.any(Number),
+ title: expect.any(String),
+ comments: [
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ author: null,
+ },
+ {
+ id: expect.any(Number),
+ content: expect.any(String),
+ author: null,
+ },
+ ],
+ });
+ });
+
+ it("supports filtering nested toOne result with nested results", async () => {
+ const nestedMiddleware = createNestedMiddleware(async (params, next) => {
+ if (params.model === "User" && !params.scope?.relations.to.isList) {
+ const result = await next(params);
+ if (result.deleted) {
+ return null;
+ }
+ return result;
+ }
+ return next(params);
+ });
+
+ const params = createParams("Post", "update", {
+ where: { id: 1 },
+ data: { content: "foo" },
+ include: {
+ author: {
+ include: {
+ comments: true,
+ profile: true,
+ },
+ },
+ },
+ });
+
+ const next = jest.fn(() =>
+ Promise.resolve({
+ author: {
+ deleted: true,
+ comments: [],
+ profile: {
+ id: 1,
+ bio: "foo",
+ },
+ },
+ })
+ );
+ const result = await nestedMiddleware(params, next);
+
+ expect(next).toHaveBeenCalledWith(params);
+ expect(result).toEqual({ author: null });
+ });
+
+ it("supports filtering nested toMany results", async () => {
+ const nestedMiddleware = createNestedMiddleware(async (params, next) => {
+ if (params.model === "Comment" && params.scope?.relations.to.isList) {
+ const result = await next(params);
+ return result.filter((item: any) => !item.deleted);
+ }
+ return next(params);
+ });
+
+ const params = createParams("Post", "update", {
+ where: { id: 1 },
+ data: { content: "foo" },
+ include: {
+ comments: true,
+ },
+ });
+
+ const next = jest.fn(() =>
+ Promise.resolve({
+ comments: [
+ { deleted: true, id: 1, content: "foo" },
+ { id: 2, content: "bar" },
+ ],
+ })
+ );
+ const result = await nestedMiddleware(params, next);
+
+ expect(result).toEqual({
+ comments: [{ id: 2, content: "bar" }],
+ });
+ });
+
+ it("supports filtering nested toMany results in list results", async () => {
+ const nestedMiddleware = createNestedMiddleware(async (params, next) => {
+ if (params.model === "Comment" && params.scope?.relations.to.isList) {
+ const result = await next(params);
+ return result.filter((item: any) => !item.deleted);
+ }
+ return next(params);
+ });
+
+ const params = createParams("Post", "findMany", {
+ where: { id: 1 },
+ include: {
+ comments: true,
+ },
+ });
+
+ const next = jest.fn(() =>
+ Promise.resolve([
+ {
+ id: 1,
+ comments: [
+ { deleted: true, id: 3, content: "foo" },
+ { id: 4, content: "bar" },
+ ],
+ },
+ {
+ id: 2,
+ comments: [
+ { deleted: true, id: 5, content: "baz" },
+ { id: 6, content: "qux" },
+ ],
+ },
+ ])
+ );
+ const result = await nestedMiddleware(params, next);
+
+ expect(result).toEqual([
+ { id: 1, comments: [{ id: 4, content: "bar" }] },
+ { id: 2, comments: [{ id: 6, content: "qux" }] },
+ ]);
+ });
+
+ it("supports filtering nested toMany results in nested toOne result", async () => {
+ const nestedMiddleware = createNestedMiddleware(async (params, next) => {
+ if (params.scope && params.model === "Comment") {
+ const result = await next(params);
+ return result.filter((item: any) => !item.deleted);
+ }
+ return next(params);
+ });
+
+ const params = createParams("Comment", "findUnique", {
+ where: { id: 1 },
+ include: {
+ author: {
+ include: {
+ comments: true,
+ },
+ },
+ },
+ });
+
+ const next = jest.fn(() =>
+ Promise.resolve({
+ id: 1,
+ content: "foo",
+ author: {
+ id: 2,
+ comments: [
+ { id: 3, content: "bar" },
+ { deleted: true, id: 1, content: "baz" },
+ ],
+ },
+ })
+ );
+ const result = await nestedMiddleware(params, next);
+
+ expect(result).toEqual({
+ id: 1,
+ content: "foo",
+ author: {
+ id: 2,
+ comments: [{ id: 3, content: "bar" }],
+ },
+ });
+ });
+
+ it("supports filtering nested toMany results in nested toMany result", async () => {
+ const nestedMiddleware = createNestedMiddleware(async (params, next) => {
+ if (params.scope && params.model === "Comment") {
+ const result = await next(params);
+ return result.filter((item: any) => !item.deleted);
+ }
+ return next(params);
+ });
+
+ const params = createParams("Post", "findUnique", {
+ where: { id: 1 },
+ include: {
+ comments: {
+ include: {
+ replies: true,
+ },
+ },
+ },
+ });
+
+ const next = jest.fn(() =>
+ Promise.resolve({
+ id: 1,
+ content: "foo",
+ comments: [
+ {
+ id: 2,
+ content: "bar",
+ replies: [
+ { id: 3, content: "baz" },
+ { deleted: true, id: 4, content: "qux" },
+ ],
+ },
+ {
+ id: 5,
+ content: "quux",
+ replies: [
+ { id: 6, content: "corge" },
+ { deleted: true, id: 7, content: "grault" },
+ ],
+ },
+ ],
+ })
+ );
+
+ const result = await nestedMiddleware(params, next);
+
+ expect(result).toEqual({
+ id: 1,
+ content: "foo",
+ comments: [
+ {
+ id: 2,
+ content: "bar",
+ replies: [{ id: 3, content: "baz" }],
+ },
+ {
+ id: 5,
+ content: "quux",
+ replies: [{ id: 6, content: "corge" }],
+ },
+ ],
+ });
+ });
+
+ it("supports filtering nested toMany results with nested results", async () => {
+ const nestedMiddleware = createNestedMiddleware(async (params, next) => {
+ if (params.model === "Comment" && params.scope?.relations.to.isList) {
+ const result = await next(params);
+ return result.filter((item: any) => !item.deleted);
+ }
+ return next(params);
+ });
+
+ const params = createParams("Post", "findUnique", {
+ where: { id: 1 },
+ include: {
+ comments: {
+ include: {
+ author: true,
+ replies: true,
+ },
+ },
+ },
+ });
+
+ const next = jest.fn(() =>
+ Promise.resolve({
+ id: 1,
+ comments: [
+ {
+ deleted: true,
+ id: 2,
+ content: "foo",
+ author: { id: 3, email: "test@test.com" },
+ replies: [
+ { id: 4, content: "bar" },
+ { id: 5, content: "baz" },
+ ],
+ },
+ {
+ id: 6,
+ content: "qux",
+ author: { id: 7, email: "test2@test.com" },
+ replies: [],
+ },
+ ],
+ })
+ );
+
+ const result = await nestedMiddleware(params, next);
+
+ expect(result).toEqual({
+ id: 1,
+ comments: [
+ {
+ id: 6,
+ content: "qux",
+ author: { id: 7, email: "test2@test.com" },
+ replies: [],
+ },
+ ],
+ });
+ });
+
+ it("waits for all middleware to finish modifying result before resolving", async () => {
+ const nestedMiddleware = createNestedMiddleware(async (params, next) => {
+ const result = await next(params);
+ if (typeof result === "undefined") return;
+
+ if (params.model === "Post") {
+ await wait(100);
+ return addReturnedDate(result);
+ }
+ if (params.model === "Profile") {
+ await wait(200);
+ return addReturnedDate(result);
+ }
+ await wait(300);
+ return addReturnedDate(result);
+ });
+
+ const params = createParams("User", "findFirst", {
+ where: { id: 1 },
+ include: {
+ posts: true,
+ profile: true,
+ },
+ });
+ const next = jest.fn(() =>
+ Promise.resolve({
+ id: faker.datatype.number(),
+ email: faker.internet.email(),
+ posts: [
+ {
+ id: faker.datatype.number(),
+ title: faker.lorem.sentence(),
+ },
+ ],
+ profile: {
+ id: faker.datatype.number(),
+ bio: faker.lorem.sentence(),
+ },
+ })
+ );
+ const result = await nestedMiddleware(params, next);
+
+ expect(result).toEqual({
+ id: expect.any(Number),
+ email: expect.any(String),
+ returned: expect.any(Date),
+ posts: [
+ {
+ id: expect.any(Number),
+ title: expect.any(String),
+ returned: expect.any(Date),
+ },
+ ],
+ profile: {
+ id: expect.any(Number),
+ bio: expect.any(String),
+ returned: expect.any(Date),
+ },
+ });
+ });
+});