Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ version: '3.7'
services:
postgres:
image: "postgres:latest"
container_name: further-postgres
hostname: postgres
user: postgres
restart: always
Expand Down
1 change: 1 addition & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,5 @@ model Profile {
age Int?
user User @relation(fields: [userId], references: [id])
userId Int @unique
meta Json?
}
26 changes: 26 additions & 0 deletions src/lib/utils/cloneParams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Prisma } from "@prisma/client";
import { cloneDeep, cloneDeepWith } from "lodash";

import { NestedParams } from "../types";

// Prisma v4 requires that instances of Prisma.NullTypes are not cloned,
// otherwise it will parse them as 'undefined' and the operation will fail.
function passThroughNullTypes(value: any) {
if (
value instanceof Prisma.NullTypes.DbNull ||
value instanceof Prisma.NullTypes.JsonNull ||
value instanceof Prisma.NullTypes.AnyNull
) {
return value;
}
}

export function cloneParams(params: NestedParams) {
// only handle null types if they are present, Prisma versions lower than v4
// do not have them and we can clone the string values as usual
if (Prisma.NullTypes) {
return cloneDeepWith(params, passThroughNullTypes);
}

return cloneDeep(params);
}
4 changes: 2 additions & 2 deletions src/lib/utils/execution.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { DeferredPromise } from "@open-draft/deferred-promise";
import { omit } from "lodash";
import cloneDeep from "lodash/cloneDeep";

import {
MiddlewareCall,
NestedMiddleware,
NestedParams,
Target,
} from "../types";
import { cloneParams } from "./cloneParams";

export async function executeMiddleware(
middleware: NestedMiddleware,
Expand All @@ -19,7 +19,7 @@ export async function executeMiddleware(
>();
const nextPromise = new DeferredPromise<any>();

const result = middleware(cloneDeep(params), (updatedParams) => {
const result = middleware(cloneParams(params), (updatedParams) => {
paramsUpdatedPromise.resolve(updatedParams);
return nextPromise;
}).catch((e) => {
Expand Down
4 changes: 2 additions & 2 deletions src/lib/utils/params.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ 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 {
Expand Down Expand Up @@ -32,6 +31,7 @@ import {
toOneRelationNonListActions,
} from "./actions";
import { fieldsByWriteAction } from "./extractNestedActions";
import { cloneParams } from "./cloneParams";

function addWriteArgsToParams(
params: NestedParams,
Expand Down Expand Up @@ -159,7 +159,7 @@ export function buildParamsFromCalls(
calls: MiddlewareCall[],
parentParams: NestedParams
) {
const finalParams = cloneDeep(parentParams);
const finalParams = cloneParams(parentParams);

// calls should update the parent calls updated params

Expand Down
22 changes: 21 additions & 1 deletion test/e2e/smoke.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Post, PrismaClient, User } from "@prisma/client";
import { Post, Prisma, PrismaClient, User } from "@prisma/client";
import faker from "faker";

import { createNestedMiddleware } from "../../src";
Expand Down Expand Up @@ -131,6 +131,26 @@ describe("smoke", () => {
},
});
expect(updatedComment).not.toBeNull();

// supports Json null values
await testClient.profile.create({
data: {
userId: secondUser.id,
meta: Prisma.DbNull,
},
});

const profile = await testClient.profile.findFirst({
where: {
OR: [
{ meta: { equals: Prisma.AnyNull } },
{ meta: { equals: Prisma.DbNull } },
{ meta: { equals: Prisma.JsonNull } },
],
},
});
expect(profile).not.toBeNull();
expect(profile!.meta).toBe(null);
});
});
});
27 changes: 27 additions & 0 deletions test/unit/args.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Prisma } from "@prisma/client";
import faker from "faker";
import { set } from "lodash";

Expand Down Expand Up @@ -27,6 +28,32 @@ describe("params", () => {
expect(params.args.data.posts.create).not.toHaveProperty("test");
});

it("passes through instances of Prisma.NullTypes to next", async () => {
const nestedMiddleware = createNestedMiddleware((params, next) => {
return next(params);
});

const next = jest.fn((_: any) => Promise.resolve(null));
const params = createParams("Profile", "findFirst", {
where: {
OR: [
{ meta: { equals: Prisma.JsonNull } },
{ meta: { equals: Prisma.DbNull } },
{ meta: { equals: Prisma.AnyNull } },
],
},
});
await nestedMiddleware(params, next);

expect(next).toHaveBeenCalledWith(params);
expect(next.mock.calls[0][0].args.where.OR).toHaveLength(3);
next.mock.calls[0][0].args.where.OR.forEach(
({ meta }: any, index: number) => {
expect(meta.equals).toBe(params.args.where.OR[index].meta.equals);
}
);
});

it("allows middleware to modify root args", async () => {
const nestedMiddleware = createNestedMiddleware((params, next) => {
return next({
Expand Down