diff --git a/packages/casl-prisma/schema.prisma b/packages/casl-prisma/schema.prisma index f4b41347a..d6354f56a 100644 --- a/packages/casl-prisma/schema.prisma +++ b/packages/casl-prisma/schema.prisma @@ -12,6 +12,7 @@ model User { firstName String lastName String age Int + verified Boolean? posts Post[] } diff --git a/packages/casl-prisma/spec/prismaQuery.spec.ts b/packages/casl-prisma/spec/prismaQuery.spec.ts index dd13bd52b..ff39de61c 100644 --- a/packages/casl-prisma/spec/prismaQuery.spec.ts +++ b/packages/casl-prisma/spec/prismaQuery.spec.ts @@ -75,6 +75,22 @@ describe('PrismaQuery evaluation', () => { expect(test({ name: 'Jane Doe' })).toBe(false) }) }) + describe('isSet', () => { + it('throws if value is not a boolean', () => { + expect(() => prismaQuery({ items: { isSet: 1 } })).toThrow(/expects to receive a boolean/) + expect(() => prismaQuery({ items: { isSet: {} } })).toThrow(/expects to receive a boolean/) + expect(() => prismaQuery({ items: { isSet: true } })).not.toThrow() + }) + + it('checks that object value is not defined when using "isSet"', () => { + const test = prismaQuery({ verified: { isSet: true } }) + expect(test({ verified: true })).toBe(true) + expect(test({ verified: false })).toBe(true) + expect(test({ verified: null })).toBe(true) + expect(test({ verified: undefined })).toBe(false) + expect(test({})).toBe(false) + }) + }) describe('in', () => { it('throws if passed value is not an array', () => { diff --git a/packages/casl-prisma/src/prisma/PrismaQueryParser.ts b/packages/casl-prisma/src/prisma/PrismaQueryParser.ts index f0d1c764b..c90e4d9f1 100644 --- a/packages/casl-prisma/src/prisma/PrismaQueryParser.ts +++ b/packages/casl-prisma/src/prisma/PrismaQueryParser.ts @@ -112,7 +112,7 @@ const compound: CompoundInstruction = { } }; -const isEmpty: FieldInstruction = { +const booleanField: FieldInstruction = { type: 'field', validate(instruction, value) { if (typeof value !== 'boolean') { @@ -183,7 +183,7 @@ const instructions = { startsWith: compareString, endsWith: compareString, contains: compareString, - isEmpty, + isEmpty: booleanField, has, hasSome, hasEvery: hasSome, @@ -195,6 +195,7 @@ const instructions = { none: inverted('some', relation), is: relation, isNot: inverted('is', relation), + isSet: booleanField }; export interface ParseOptions { diff --git a/packages/casl-prisma/src/prisma/interpretPrismaQuery.ts b/packages/casl-prisma/src/prisma/interpretPrismaQuery.ts index 930dda21f..0919acc1d 100644 --- a/packages/casl-prisma/src/prisma/interpretPrismaQuery.ts +++ b/packages/casl-prisma/src/prisma/interpretPrismaQuery.ts @@ -79,6 +79,11 @@ const not: JsInterpreter = (condition, object, { interpret }) return condition.value.every(subCondition => !interpret(subCondition, object)); }; +const isSet: JsInterpreter> = (condition, object, { get }) => { + const item = get(object, condition.field); + return item !== undefined; +} + function toComparable(value: unknown) { return value && typeof value === 'object' ? value.valueOf() : value; } @@ -112,6 +117,7 @@ export const interpretPrismaQuery = createJsInterpreter({ every, some, is, + isSet, }, { get: (object, field) => object[field], compare: compareValues, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9975c0b3a..fcbe352e0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11968,4 +11968,4 @@ packages: /zone.js@0.15.0: resolution: {integrity: sha512-9oxn0IIjbCZkJ67L+LkhYWRyAy7axphb3VgE2MBDlOqnmHMPWGYMxJxBYFueFq/JGY2GMwS0rU+UCLunEmy5UA==} - dev: true + dev: true \ No newline at end of file