diff --git a/README.md b/README.md index c0111d3..bb6880d 100644 --- a/README.md +++ b/README.md @@ -159,3 +159,9 @@ Here are some performance metrics for the above query for the small test sqlite #### Nested fields and wildcards are not supported / tested `can('read', 'User', ['nested.field', 'field.*'])` probably won't work. + +#### Combined relation fields are not supported + +`entry Relation @relation(fields: [entryIdA, entryIdB], references: [refA, refB])` does not work. +`entry Relation @relation(fields: [entryId], references: [ref])` does work. +Since internally a relation like `entryId: someId` might be replaced by `entry: { connect: { ref: someId, ...caslConditions } }` diff --git a/src/applyCaslToQuery.ts b/src/applyCaslToQuery.ts index a7bde98..f798cee 100644 --- a/src/applyCaslToQuery.ts +++ b/src/applyCaslToQuery.ts @@ -6,6 +6,7 @@ import { applyIncludeSelectQuery } from "./applyIncludeSelectQuery" import { applyRuleRelationsQuery } from './applyRuleRelationsQuery' import { applyWhereQuery } from "./applyWhereQuery" import { caslOperationDict, PrismaCaslOperation } from "./helpers" +import { transformDataToWhereQuery } from "./transformDataToWhereQuery" /** * Applies CASL authorization logic to prisma query @@ -26,6 +27,9 @@ export function applyCaslToQuery(operation: PrismaCaslOperation, args: any, abil const { args: dataArgs, creationTree: dataCreationTree } = applyDataQuery(abilities, args.data, operationAbility.action, model) creationTree = dataCreationTree args.data = dataArgs + if (operation === 'updateMany') { + args = transformDataToWhereQuery(args, model) + } } diff --git a/src/transformDataToWhereQuery.ts b/src/transformDataToWhereQuery.ts new file mode 100644 index 0000000..4034193 --- /dev/null +++ b/src/transformDataToWhereQuery.ts @@ -0,0 +1,33 @@ +import { relationFieldsByModel } from "./helpers" + +export function transformDataToWhereQuery(args: any, model: string) { + + ;['connect', 'disconnect'].forEach((action) => { + Object.entries(args.data).forEach(([relation, obj]: [string, any]) => { + if (typeof obj === 'object' && !Array.isArray(obj) && obj[action]) { + // combine args.data.relation[action].AND and args.where.AND + const ANDArgs = { AND: [...(obj[action].AND ?? []), ...(args.where[relation]?.AND ?? [])] } + const relationTo = relationFieldsByModel[model][relation].relationToFields?.[0] + const relationFrom = relationFieldsByModel[model][relation].relationFromFields?.[0] + if (!relationTo || !relationFrom) { + throw new Error('Cannot find correct relations to transform updateMany to casl query.') + } + args.where = { + ...(args.where ?? {}), + [relation]: { + ...(args.where[relation] ?? {}), + ...obj[action], + ...(ANDArgs.AND.length > 0 ? ANDArgs : {}) + } + } + args.data = { + ...args.data, + [relationFrom]: obj[action][relationTo] + } + delete args.data[relation] + delete args.where[relation][relationTo] + } + }) + }) + return args +} \ No newline at end of file diff --git a/test/transformDataToWhereQuery.test.ts b/test/transformDataToWhereQuery.test.ts new file mode 100644 index 0000000..a88257a --- /dev/null +++ b/test/transformDataToWhereQuery.test.ts @@ -0,0 +1,21 @@ +import { transformDataToWhereQuery } from '../src/transformDataToWhereQuery' + +describe('transform data to where query', () => { + + it('converts data relations with connect to where query and transforms to relationField data entries', () => { + + const result = transformDataToWhereQuery({ data: { id: 1, thread: { connect: { id: 0, text: 'a' } } }, where: { id: 0 } }, + 'Post' + ) + expect(result.data).toEqual({ id: 1, threadId: 0 }) + expect(result.where).toEqual({ id: 0, thread: { text: 'a' } }) + }) + + it('converts data relations with disconnect to where query and transforms to relationField data entries', () => { + const result = transformDataToWhereQuery({ data: { id: 1, thread: { disconnect: { id: 0, text: 'a' } } }, where: { id: 0 } }, + 'Post' + ) + expect(result.data).toEqual({ id: 1, threadId: 0 }) + expect(result.where).toEqual({ id: 0, thread: { text: 'a' } }) + }) +})