Skip to content

Commit 474be55

Browse files
authored
Merge pull request #12 from DouglasNeuroInformatics/dev
feat: add accessibleQuery function
2 parents eabc2f8 + 2f92de5 commit 474be55

File tree

5 files changed

+51
-4
lines changed

5 files changed

+51
-4
lines changed

example/app.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export default await AppContainer.create({
1616
inject: [CryptoService],
1717
useFactory: (cryptoService: CryptoService) => {
1818
return {
19-
defineAbility: (ability, payload: { isAdmin: boolean }) => {
19+
defineAbility: (ability, payload) => {
2020
if (payload.isAdmin) {
2121
ability.can('manage', 'all');
2222
}

src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ export { CurrentUser } from './decorators/current-user.decorator.js';
33
export { RouteAccess } from './decorators/route-access.decorator.js';
44
export { ValidationSchema } from './decorators/validation-schema.decorator.js';
55
export { DataTransferObject } from './mixins/data-transfer-object.mixin.js';
6-
export type { AuthModuleOptions } from './modules/auth/auth.config.js';
6+
export type { AppAbility, AuthModuleOptions } from './modules/auth/auth.config.js';
77
export { AuthModule } from './modules/auth/auth.module.js';
88
export { ConfigService } from './modules/config/config.service.js';
99
export { CryptoService } from './modules/crypto/crypto.service.js';
1010
export { LoggingService } from './modules/logging/logging.service.js';
1111
export { InjectModel } from './modules/prisma/prisma.decorators.js';
1212
export { PrismaService } from './modules/prisma/prisma.service.js';
1313
export type { Model } from './modules/prisma/prisma.types.js';
14-
export { getModelToken } from './modules/prisma/prisma.utils.js';
14+
export { accessibleQuery, getModelToken } from './modules/prisma/prisma.utils.js';
1515
export type { VirtualizationModuleOptions } from './modules/virtualization/virtualization.config.js';
1616
export { VirtualizationModule } from './modules/virtualization/virtualization.module.js';
1717
export { VirtualizationService } from './modules/virtualization/virtualization.service.js';
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { describe, expect, it, vi } from 'vitest';
2+
3+
import { accessibleQuery } from '../prisma.utils.js';
4+
5+
const accessibleBy = vi.hoisted(() => vi.fn());
6+
7+
vi.mock('@casl/prisma/runtime', () => ({
8+
createAccessibleByFactory: () => accessibleBy
9+
}));
10+
11+
describe('accessibleQuery', () => {
12+
it('should return an empty object if ability is undefined', () => {
13+
expect(accessibleQuery(undefined, 'manage', 'Cat')).toStrictEqual({});
14+
expect(accessibleBy).not.toHaveBeenCalled();
15+
});
16+
it('should call accessibleBy with the correct parameters and return the result of accessibleBy for the model', () => {
17+
accessibleBy.mockReturnValueOnce({
18+
Cat: 'QUERY'
19+
});
20+
const ability = vi.fn();
21+
expect(accessibleQuery(ability as any, 'manage', 'Cat')).toStrictEqual('QUERY');
22+
expect(accessibleBy).toHaveBeenCalledExactlyOnceWith(ability, 'manage');
23+
});
24+
});

src/modules/prisma/prisma.types.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,13 @@ export type PrismaModelKey<T extends PrismaModelName = PrismaModelName> = Uncapi
1616

1717
export type PrismaModelToken<T extends PrismaModelName> = `${T}PrismaModel`;
1818

19+
export type PrismaModelWhereInputMap = {
20+
[K in PrismaModelName]: PrismaClientLike[PrismaModelKey<K>] extends {
21+
findFirst: (args: { where: infer TWhereInput }) => any;
22+
}
23+
? TWhereInput
24+
: never;
25+
};
26+
1927
export type Model<T extends PrismaModelName> =
2028
RuntimePrismaClient extends SingleKeyMap<`${Uncapitalize<T>}`, infer U> ? U : never;

src/modules/prisma/prisma.utils.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1+
import { createAccessibleByFactory } from '@casl/prisma/runtime';
12
import { uncapitalize } from '@douglasneuroinformatics/libjs';
23

3-
import type { PrismaModelKey, PrismaModelName, PrismaModelToken } from './prisma.types.js';
4+
import type { AppAbility, AppAction, AppConditions } from '../auth/auth.config.js';
5+
import type { PrismaModelKey, PrismaModelName, PrismaModelToken, PrismaModelWhereInputMap } from './prisma.types.js';
6+
7+
const accessibleBy = createAccessibleByFactory<PrismaModelWhereInputMap, AppConditions>();
48

59
/**
610
* Generates an injection token for a given Prisma model name.
@@ -15,3 +19,14 @@ export function getModelToken<T extends PrismaModelName>(modelName: T): PrismaMo
1519
export function getModelKey<T extends PrismaModelName>(modelName: T): PrismaModelKey<T> {
1620
return uncapitalize(modelName);
1721
}
22+
23+
export function accessibleQuery<T extends PrismaModelName>(
24+
ability: AppAbility | undefined,
25+
action: AppAction,
26+
modelName: T
27+
): NonNullable<PrismaModelWhereInputMap[T]> {
28+
if (!ability) {
29+
return {};
30+
}
31+
return accessibleBy(ability, action)[modelName]!;
32+
}

0 commit comments

Comments
 (0)