From ac5c809fa79316e333e666a7a0e71b88c7e2ac89 Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Tue, 9 Jan 2024 20:39:23 +0800 Subject: [PATCH 1/4] refactor: unify enhancement API to `enhance` --- CONTRIBUTING.md | 2 +- packages/README.md | 4 +- packages/runtime/src/enhancements/enhance.ts | 98 ++++++++++++++++--- packages/runtime/src/enhancements/index.ts | 4 - packages/runtime/src/enhancements/omit.ts | 18 +--- packages/runtime/src/enhancements/password.ts | 18 +--- .../runtime/src/enhancements/policy/index.ts | 92 ++++------------- packages/runtime/src/enhancements/proxy.ts | 4 +- .../schema/src/plugins/enhancer/enhancer.ts | 4 +- packages/server/tests/api/rest.test.ts | 6 +- packages/testtools/src/schema.ts | 17 +--- .../enhancements/with-omit/with-omit.test.ts | 4 +- .../with-password/with-password.test.ts | 4 +- .../enhancements/with-policy/auth.test.ts | 60 ++++++------ .../with-policy/connect-disconnect.test.ts | 20 ++-- .../with-policy/deep-nested.test.ts | 2 +- .../with-policy/empty-policy.test.ts | 12 +-- .../with-policy/field-comparison.test.ts | 10 +- .../with-policy/field-level-policy.test.ts | 96 +++++++++--------- .../with-policy/field-validation.test.ts | 4 +- .../with-policy/fluent-api.test.ts | 4 +- .../with-policy/multi-field-unique.test.ts | 12 +-- .../with-policy/multi-id-fields.test.ts | 20 ++-- .../with-policy/nested-to-many.test.ts | 36 +++---- .../with-policy/nested-to-one.test.ts | 36 +++---- .../enhancements/with-policy/options.test.ts | 1 - .../with-policy/petstore-sample.test.ts | 4 +- .../with-policy/post-update.test.ts | 44 ++++----- .../enhancements/with-policy/postgres.test.ts | 4 +- .../with-policy/query-reduction.test.ts | 6 +- .../enhancements/with-policy/refactor.test.ts | 4 +- .../relation-many-to-many-filter.test.ts | 12 +-- .../relation-one-to-many-filter.test.ts | 16 +-- .../relation-one-to-one-filter.test.ts | 12 +-- .../with-policy/self-relation.test.ts | 12 +-- .../with-policy/subscription.test.ts | 18 ++-- .../with-policy/todo-sample.test.ts | 4 +- .../with-policy/toplevel-operations.test.ts | 12 +-- .../with-policy/unique-as-id.test.ts | 12 +-- .../enhancements/with-policy/view.test.ts | 4 +- .../integration/tests/misc/stacktrace.test.ts | 4 +- .../tests/regression/issue-665.test.ts | 10 +- .../tests/regression/issues.test.ts | 28 +++--- 43 files changed, 391 insertions(+), 403 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2b16adebf..1eed5723d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -67,7 +67,7 @@ The `zenstack` CLI and ZModel VSCode extension implementation. The package also ### `runtime` -Runtime enhancements to PrismaClient, including infrastructure for creating transparent proxies and concrete implementations for the `withPolicy`, `withPassword`, and `withOmit` proxies. +Runtime enhancements to PrismaClient, including infrastructure for creating transparent proxies and concrete implementations of various proxies. ### `server` diff --git a/packages/README.md b/packages/README.md index 2104ff0eb..d4f076584 100644 --- a/packages/README.md +++ b/packages/README.md @@ -51,12 +51,12 @@ At runtime, transparent proxies are created around Prisma clients for intercepti // Next.js example: pages/api/model/[...path].ts import { requestHandler } from '@zenstackhq/next'; -import { withPolicy } from '@zenstackhq/runtime'; +import { enhance } from '@zenstackhq/runtime'; import { getSessionUser } from '@lib/auth'; import { prisma } from '@lib/db'; export default requestHandler({ - getPrisma: (req, res) => withPolicy(prisma, { user: getSessionUser(req, res) }), + getPrisma: (req, res) => enhance(prisma, { user: getSessionUser(req, res) }), }); ``` diff --git a/packages/runtime/src/enhancements/enhance.ts b/packages/runtime/src/enhancements/enhance.ts index 977a8e950..39d7cc0d4 100644 --- a/packages/runtime/src/enhancements/enhance.ts +++ b/packages/runtime/src/enhancements/enhance.ts @@ -1,30 +1,98 @@ -import { withOmit, WithOmitOptions } from './omit'; -import { withPassword, WithPasswordOptions } from './password'; -import { withPolicy, WithPolicyContext, WithPolicyOptions } from './policy'; +import semver from 'semver'; +import { PRISMA_MINIMUM_VERSION } from '../constants'; +import { ModelMeta } from '../cross'; +import type { AuthUser } from '../types'; +import { withOmit } from './omit'; +import { withPassword } from './password'; +import { withPolicy } from './policy'; +import type { ErrorTransformer } from './proxy'; +import type { PolicyDef, ZodSchemas } from './types'; /** - * Options @see enhance + * Kinds of enhancements to `PrismaClient` */ -export type EnhancementOptions = WithPolicyOptions & WithPasswordOptions & WithOmitOptions; +export enum EnhancementKind { + Password = 'password', + Omit = 'omit', + Policy = 'policy', +} + +/** + * Options for {@link createEnhancement} + */ +export type EnhancementOptions = { + /** + * Policy definition + */ + policy: PolicyDef; + + /** + * Model metadata + */ + modelMeta: ModelMeta; + + /** + * Zod schemas for validation + */ + zodSchemas?: ZodSchemas; + + /** + * Whether to log Prisma query + */ + logPrismaQuery?: boolean; + + /** + * Hook for transforming errors before they are thrown to the caller. + */ + errorTransformer?: ErrorTransformer; + + /** + * The Node module that contains PrismaClient + */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + prismaModule: any; + + /** + * The kinds of enhancements to apply. By default all enhancements are applied. + */ + kinds?: EnhancementKind[]; +}; + +/** + * Context for creating enhanced `PrismaClient` + */ +export type EnhancementContext = { + user?: AuthUser; +}; let hasPassword: boolean | undefined = undefined; let hasOmit: boolean | undefined = undefined; /** - * Gets a Prisma client enhanced with all essential behaviors, including access + * Gets a Prisma client enhanced with all enhancement behaviors, including access * policy, field validation, field omission and password hashing. * - * It's a shortcut for calling withOmit(withPassword(withPolicy(prisma, options))). - * * @param prisma The Prisma client to enhance. - * @param context The context to for evaluating access policies. + * @param context Context. * @param options Options. */ export function createEnhancement( prisma: DbClient, options: EnhancementOptions, - context?: WithPolicyContext + context?: EnhancementContext ) { + if (!prisma) { + throw new Error('Invalid prisma instance'); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const prismaVer = (prisma as any)._clientVersion; + if (prismaVer && semver.lt(prismaVer, PRISMA_MINIMUM_VERSION)) { + console.warn( + `ZenStack requires Prisma version "${PRISMA_MINIMUM_VERSION}" or higher. Detected version is "${prismaVer}".` + ); + } + let result = prisma; if (hasPassword === undefined || hasOmit === undefined) { @@ -33,18 +101,22 @@ export function createEnhancement( hasOmit = allFields.some((field) => field.attributes?.some((attr) => attr.name === '@omit')); } - if (hasPassword) { + const kinds = options.kinds ?? [EnhancementKind.Password, EnhancementKind.Omit, EnhancementKind.Policy]; + + if (hasPassword && kinds.includes(EnhancementKind.Password)) { // @password proxy result = withPassword(result, options); } - if (hasOmit) { + if (hasOmit && kinds.includes(EnhancementKind.Omit)) { // @omit proxy result = withOmit(result, options); } // policy proxy - result = withPolicy(result, options, context); + if (kinds.includes(EnhancementKind.Policy)) { + result = withPolicy(result, options, context); + } return result; } diff --git a/packages/runtime/src/enhancements/index.ts b/packages/runtime/src/enhancements/index.ts index 51f304657..ebdd16a08 100644 --- a/packages/runtime/src/enhancements/index.ts +++ b/packages/runtime/src/enhancements/index.ts @@ -1,8 +1,4 @@ export * from '../cross'; export * from './enhance'; -export * from './omit'; -export * from './password'; -export * from './policy'; export * from './types'; export * from './utils'; -export * from './where-visitor'; diff --git a/packages/runtime/src/enhancements/omit.ts b/packages/runtime/src/enhancements/omit.ts index 22b002309..d3e566ebb 100644 --- a/packages/runtime/src/enhancements/omit.ts +++ b/packages/runtime/src/enhancements/omit.ts @@ -3,23 +3,15 @@ import { enumerate, getModelFields, resolveField, type ModelMeta } from '../cross'; import { DbClientContract } from '../types'; +import { EnhancementOptions } from './enhance'; import { DefaultPrismaProxyHandler, makeProxy } from './proxy'; -import { CommonEnhancementOptions } from './types'; /** - * Options for @see withOmit + * Gets an enhanced Prisma client that supports `@omit` attribute. + * + * @private */ -export interface WithOmitOptions extends CommonEnhancementOptions { - /** - * Model metadata - */ - modelMeta: ModelMeta; -} - -/** - * Gets an enhanced Prisma client that supports @omit attribute. - */ -export function withOmit(prisma: DbClient, options: WithOmitOptions): DbClient { +export function withOmit(prisma: DbClient, options: EnhancementOptions): DbClient { return makeProxy( prisma, options.modelMeta, diff --git a/packages/runtime/src/enhancements/password.ts b/packages/runtime/src/enhancements/password.ts index 5846bc8dc..d10e1d9a7 100644 --- a/packages/runtime/src/enhancements/password.ts +++ b/packages/runtime/src/enhancements/password.ts @@ -5,23 +5,15 @@ import { hash } from 'bcryptjs'; import { DEFAULT_PASSWORD_SALT_LENGTH } from '../constants'; import { NestedWriteVisitor, type ModelMeta, type PrismaWriteActionType } from '../cross'; import { DbClientContract } from '../types'; +import { EnhancementOptions } from './enhance'; import { DefaultPrismaProxyHandler, PrismaProxyActions, makeProxy } from './proxy'; -import { CommonEnhancementOptions } from './types'; /** - * Options for @see withPassword + * Gets an enhanced Prisma client that supports `@password` attribute. + * + * @private */ -export interface WithPasswordOptions extends CommonEnhancementOptions { - /** - * Model metadata - */ - modelMeta: ModelMeta; -} - -/** - * Gets an enhanced Prisma client that supports @password attribute. - */ -export function withPassword(prisma: DbClient, options: WithPasswordOptions): DbClient { +export function withPassword(prisma: DbClient, options: EnhancementOptions): DbClient { return makeProxy( prisma, options.modelMeta, diff --git a/packages/runtime/src/enhancements/policy/index.ts b/packages/runtime/src/enhancements/policy/index.ts index 497379cc3..fd52ba051 100644 --- a/packages/runtime/src/enhancements/policy/index.ts +++ b/packages/runtime/src/enhancements/policy/index.ts @@ -1,57 +1,12 @@ -/* eslint-disable @typescript-eslint/no-var-requires */ /* eslint-disable @typescript-eslint/no-explicit-any */ -import semver from 'semver'; -import { PRISMA_MINIMUM_VERSION } from '../../constants'; -import { getIdFields, type ModelMeta } from '../../cross'; -import { AuthUser, DbClientContract } from '../../types'; +import { getIdFields } from '../../cross'; +import { DbClientContract } from '../../types'; import { hasAllFields } from '../../validation'; -import { ErrorTransformer, makeProxy } from '../proxy'; -import type { CommonEnhancementOptions, PolicyDef, ZodSchemas } from '../types'; +import type { EnhancementContext, EnhancementOptions } from '../enhance'; +import { makeProxy } from '../proxy'; import { PolicyProxyHandler } from './handler'; -/** - * Context for evaluating access policies - */ -export type WithPolicyContext = { - user?: AuthUser; -}; - -/** - * Options for @see withPolicy - */ -export interface WithPolicyOptions extends CommonEnhancementOptions { - /** - * Policy definition - */ - policy: PolicyDef; - - /** - * Model metadata - */ - modelMeta: ModelMeta; - - /** - * Zod schemas for validation - */ - zodSchemas?: ZodSchemas; - - /** - * Whether to log Prisma query - */ - logPrismaQuery?: boolean; - - /** - * Hook for transforming errors before they are thrown to the caller. - */ - errorTransformer?: ErrorTransformer; - - /** - * The Node module that contains PrismaClient - */ - prismaModule: any; -} - /** * Gets an enhanced Prisma client with access policy check. * @@ -59,31 +14,20 @@ export interface WithPolicyOptions extends CommonEnhancementOptions { * @param context The policy evaluation context * @param policy The policy definition, will be loaded from default location if not provided * @param modelMeta The model metadata, will be loaded from default location if not provided + * + * @private */ export function withPolicy( prisma: DbClient, - options: WithPolicyOptions, - context?: WithPolicyContext + options: EnhancementOptions, + context?: EnhancementContext ): DbClient { - if (!prisma) { - throw new Error('Invalid prisma instance'); - } - - const prismaVer = (prisma as any)._clientVersion; - if (prismaVer && semver.lt(prismaVer, PRISMA_MINIMUM_VERSION)) { - console.warn( - `ZenStack requires Prisma version "${PRISMA_MINIMUM_VERSION}" or higher. Detected version is "${prismaVer}".` - ); - } - - const _policy = options.policy; - const _modelMeta = options.modelMeta; - const _zodSchemas = options?.zodSchemas; + const { modelMeta, policy, zodSchemas, prismaModule, logPrismaQuery } = options; // validate user context const userContext = context?.user; - if (userContext && _modelMeta.authModel) { - const idFields = getIdFields(_modelMeta, _modelMeta.authModel); + if (userContext && modelMeta.authModel) { + const idFields = getIdFields(modelMeta, modelMeta.authModel); if ( !hasAllFields( context.user, @@ -96,7 +40,7 @@ export function withPolicy( } // validate user context for fields used in policy expressions - const authSelector = _policy.authSelector; + const authSelector = policy.authSelector; if (authSelector) { Object.keys(authSelector).forEach((f) => { if (!(f in userContext)) { @@ -108,17 +52,17 @@ export function withPolicy( return makeProxy( prisma, - _modelMeta, + modelMeta, (_prisma, model) => new PolicyProxyHandler( _prisma as DbClientContract, - _policy, - _modelMeta, - _zodSchemas, - options.prismaModule, + policy, + modelMeta, + zodSchemas, + prismaModule, model, context?.user, - options?.logPrismaQuery + logPrismaQuery ), 'policy', options?.errorTransformer diff --git a/packages/runtime/src/enhancements/proxy.ts b/packages/runtime/src/enhancements/proxy.ts index 358bff153..5256ae226 100644 --- a/packages/runtime/src/enhancements/proxy.ts +++ b/packages/runtime/src/enhancements/proxy.ts @@ -258,11 +258,11 @@ function createHandlerProxy( // eslint-disable-next-line @typescript-eslint/ban-types const origMethod = prop as Function; return function (...args: any[]) { - // using proxy with async functions results in messed-up error stack trace, + // using proxy with async functions messes up error stack trace, // create an error to capture the current stack const capture = new Error(ERROR_MARKER); - // the original proxy returned by the PrismaClient proxy + // the original promise returned by the PrismaClient proxy const promise: Promise = origMethod.apply(handler, args); // modify the error stack diff --git a/packages/schema/src/plugins/enhancer/enhancer.ts b/packages/schema/src/plugins/enhancer/enhancer.ts index 09360254b..5eccd356d 100644 --- a/packages/schema/src/plugins/enhancer/enhancer.ts +++ b/packages/schema/src/plugins/enhancer/enhancer.ts @@ -8,13 +8,13 @@ export async function generate(model: Model, options: PluginOptions, project: Pr project.createSourceFile( outFile, - `import { createEnhancement, type WithPolicyContext, type EnhancementOptions, type ZodSchemas } from '@zenstackhq/runtime'; + `import { createEnhancement, type EnhancementContext, type EnhancementOptions, type ZodSchemas } from '@zenstackhq/runtime'; import modelMeta from './model-meta'; import policy from './policy'; ${options.withZodSchemas ? "import * as zodSchemas from './zod';" : 'const zodSchemas = undefined;'} import { Prisma } from '${getPrismaClientImportSpec(model, outDir)}'; -export function enhance(prisma: DbClient, context?: WithPolicyContext, options?: EnhancementOptions): DbClient { +export function enhance(prisma: DbClient, context?: EnhancementContext, options?: EnhancementOptions): DbClient { return createEnhancement(prisma, { modelMeta, policy, diff --git a/packages/server/tests/api/rest.test.ts b/packages/server/tests/api/rest.test.ts index c1b3bfd4e..770d05017 100644 --- a/packages/server/tests/api/rest.test.ts +++ b/packages/server/tests/api/rest.test.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ /// -import { CrudFailureReason, withPolicy, type ModelMeta } from '@zenstackhq/runtime'; +import { CrudFailureReason, type ModelMeta } from '@zenstackhq/runtime'; import { loadSchema, run } from '@zenstackhq/testtools'; import { Decimal } from 'decimal.js'; import SuperJSON from 'superjson'; @@ -1882,7 +1882,7 @@ describe('REST server tests', () => { beforeAll(async () => { const params = await loadSchema(schema); - prisma = withPolicy(params.prisma, params); + prisma = params.enhanceRaw(params.prisma, params); zodSchemas = params.zodSchemas; modelMeta = params.modelMeta; @@ -1995,7 +1995,7 @@ describe('REST server tests', () => { beforeAll(async () => { const params = await loadSchema(schema); - prisma = withPolicy(params.prisma, params); + prisma = params.enhanceRaw(params.prisma, params); zodSchemas = params.zodSchemas; modelMeta = params.modelMeta; diff --git a/packages/testtools/src/schema.ts b/packages/testtools/src/schema.ts index fd7df30b7..9b6c16e11 100644 --- a/packages/testtools/src/schema.ts +++ b/packages/testtools/src/schema.ts @@ -2,7 +2,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import type { DMMF } from '@prisma/generator-helper'; import type { Model } from '@zenstackhq/language/ast'; -import { withOmit, withPassword, withPolicy, type AuthUser, type DbOperations } from '@zenstackhq/runtime'; +import type { AuthUser, DbOperations, EnhancementOptions } from '@zenstackhq/runtime'; import { getDMMF } from '@zenstackhq/sdk'; import { execSync } from 'child_process'; import * as fs from 'fs'; @@ -245,9 +245,6 @@ export async function loadSchema(schema: string, options?: SchemaLoadOptions) { prisma, prismaModule, projectDir: projectRoot, - withPolicy: undefined as any, - withOmit: undefined as any, - withPassword: undefined as any, enhance: undefined as any, enhanceRaw: undefined as any, policy: undefined as any, @@ -277,16 +274,12 @@ export async function loadSchema(schema: string, options?: SchemaLoadOptions) { return { projectDir: projectRoot, prisma, - withPolicy: (user?: AuthUser) => - withPolicy( + enhance: (user?: AuthUser, options?: EnhancementOptions): FullDbClientContract => + enhance( prisma, - { policy, modelMeta, zodSchemas, prismaModule, logPrismaQuery: opt.logPrismaQuery }, - { user } + { user }, + { policy, modelMeta, zodSchemas, logPrismaQuery: opt.logPrismaQuery, ...options } ), - withOmit: () => withOmit(prisma, { modelMeta }), - withPassword: () => withPassword(prisma, { modelMeta }), - enhance: (user?: AuthUser): FullDbClientContract => - enhance(prisma, { user }, { policy, modelMeta, zodSchemas, logPrismaQuery: opt.logPrismaQuery }), enhanceRaw: enhance, policy, modelMeta, diff --git a/tests/integration/tests/enhancements/with-omit/with-omit.test.ts b/tests/integration/tests/enhancements/with-omit/with-omit.test.ts index 82e3ec2c8..67b97776a 100644 --- a/tests/integration/tests/enhancements/with-omit/with-omit.test.ts +++ b/tests/integration/tests/enhancements/with-omit/with-omit.test.ts @@ -32,9 +32,9 @@ describe('Omit test', () => { `; it('omit tests', async () => { - const { withOmit } = await loadSchema(model); + const { enhance } = await loadSchema(model); - const db = withOmit(); + const db = enhance(); const r = await db.user.create({ include: { profile: true }, data: { diff --git a/tests/integration/tests/enhancements/with-password/with-password.test.ts b/tests/integration/tests/enhancements/with-password/with-password.test.ts index 737338666..37b23ecde 100644 --- a/tests/integration/tests/enhancements/with-password/with-password.test.ts +++ b/tests/integration/tests/enhancements/with-password/with-password.test.ts @@ -22,9 +22,9 @@ describe('Password test', () => { }`; it('password tests', async () => { - const { withPassword } = await loadSchema(model); + const { enhance } = await loadSchema(model); - const db = withPassword(); + const db = enhance(); const r = await db.user.create({ data: { id: '1', diff --git a/tests/integration/tests/enhancements/with-policy/auth.test.ts b/tests/integration/tests/enhancements/with-policy/auth.test.ts index 8f095f677..942d2d579 100644 --- a/tests/integration/tests/enhancements/with-policy/auth.test.ts +++ b/tests/integration/tests/enhancements/with-policy/auth.test.ts @@ -13,7 +13,7 @@ describe('With Policy: auth() test', () => { }); it('undefined user with string id simple', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model User { id String @id @default(uuid()) @@ -29,15 +29,15 @@ describe('With Policy: auth() test', () => { ` ); - const db = withPolicy(); + const db = enhance(); await expect(db.post.create({ data: { title: 'abc' } })).toBeRejectedByPolicy(); - const authDb = withPolicy({ id: 'user1' }); + const authDb = enhance({ id: 'user1' }); await expect(authDb.post.create({ data: { title: 'abc' } })).toResolveTruthy(); }); it('undefined user with string id more', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model User { id String @id @default(uuid()) @@ -53,15 +53,15 @@ describe('With Policy: auth() test', () => { ` ); - const db = withPolicy(); + const db = enhance(); await expect(db.post.create({ data: { title: 'abc' } })).toBeRejectedByPolicy(); - const authDb = withPolicy({ id: 'user1' }); + const authDb = enhance({ id: 'user1' }); await expect(authDb.post.create({ data: { title: 'abc' } })).toResolveTruthy(); }); it('undefined user with int id', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model User { id Int @id @default(autoincrement()) @@ -77,15 +77,15 @@ describe('With Policy: auth() test', () => { ` ); - const db = withPolicy(); + const db = enhance(); await expect(db.post.create({ data: { title: 'abc' } })).toBeRejectedByPolicy(); - const authDb = withPolicy({ id: 'user1' }); + const authDb = enhance({ id: 'user1' }); await expect(authDb.post.create({ data: { title: 'abc' } })).toResolveTruthy(); }); it('undefined user compared with field', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model User { id String @id @default(uuid()) @@ -106,21 +106,21 @@ describe('With Policy: auth() test', () => { ` ); - const db = withPolicy(); + const db = enhance(); await expect(db.user.create({ data: { id: 'user1' } })).toResolveTruthy(); await expect(db.post.create({ data: { id: '1', title: 'abc', authorId: 'user1' } })).toResolveTruthy(); - const authDb = withPolicy(); + const authDb = enhance(); await expect(authDb.post.update({ where: { id: '1' }, data: { title: 'bcd' } })).toBeRejectedByPolicy(); - expect(() => withPolicy({ id: null })).toThrow(/Invalid user context/); + expect(() => enhance({ id: null })).toThrow(/Invalid user context/); - const authDb2 = withPolicy({ id: 'user1' }); + const authDb2 = enhance({ id: 'user1' }); await expect(authDb2.post.update({ where: { id: '1' }, data: { title: 'bcd' } })).toResolveTruthy(); }); it('undefined user compared with field more', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model User { id String @id @default(uuid()) @@ -141,18 +141,18 @@ describe('With Policy: auth() test', () => { ` ); - const db = withPolicy(); + const db = enhance(); await expect(db.user.create({ data: { id: 'user1' } })).toResolveTruthy(); await expect(db.post.create({ data: { id: '1', title: 'abc', authorId: 'user1' } })).toResolveTruthy(); await expect(db.post.update({ where: { id: '1' }, data: { title: 'bcd' } })).toBeRejectedByPolicy(); - const authDb2 = withPolicy({ id: 'user1' }); + const authDb2 = enhance({ id: 'user1' }); await expect(authDb2.post.update({ where: { id: '1' }, data: { title: 'bcd' } })).toResolveTruthy(); }); it('undefined user non-id field', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model User { id String @id @default(uuid()) @@ -174,20 +174,20 @@ describe('With Policy: auth() test', () => { ` ); - const db = withPolicy(); + const db = enhance(); await expect(db.user.create({ data: { id: 'user1', role: 'USER' } })).toResolveTruthy(); await expect(db.post.create({ data: { id: '1', title: 'abc', authorId: 'user1' } })).toResolveTruthy(); await expect(db.post.update({ where: { id: '1' }, data: { title: 'bcd' } })).toBeRejectedByPolicy(); - const authDb = withPolicy({ id: 'user1', role: 'USER' }); + const authDb = enhance({ id: 'user1', role: 'USER' }); await expect(authDb.post.update({ where: { id: '1' }, data: { title: 'bcd' } })).toBeRejectedByPolicy(); - const authDb1 = withPolicy({ id: 'user2', role: 'ADMIN' }); + const authDb1 = enhance({ id: 'user2', role: 'ADMIN' }); await expect(authDb1.post.update({ where: { id: '1' }, data: { title: 'bcd' } })).toResolveTruthy(); }); it('non User auth model', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model Foo { id String @id @default(uuid()) @@ -206,15 +206,15 @@ describe('With Policy: auth() test', () => { ` ); - const userDb = withPolicy({ id: 'user1', role: 'USER' }); + const userDb = enhance({ id: 'user1', role: 'USER' }); await expect(userDb.post.create({ data: { title: 'abc' } })).toBeRejectedByPolicy(); - const adminDb = withPolicy({ id: 'user1', role: 'ADMIN' }); + const adminDb = enhance({ id: 'user1', role: 'ADMIN' }); await expect(adminDb.post.create({ data: { title: 'abc' } })).toResolveTruthy(); }); it('User model ignored', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model User { id String @id @default(uuid()) @@ -233,15 +233,15 @@ describe('With Policy: auth() test', () => { ` ); - const userDb = withPolicy({ id: 'user1', role: 'USER' }); + const userDb = enhance({ id: 'user1', role: 'USER' }); await expect(userDb.post.create({ data: { title: 'abc' } })).toBeRejectedByPolicy(); - const adminDb = withPolicy({ id: 'user1', role: 'ADMIN' }); + const adminDb = enhance({ id: 'user1', role: 'ADMIN' }); await expect(adminDb.post.create({ data: { title: 'abc' } })).toResolveTruthy(); }); it('Auth model ignored', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model Foo { id String @id @default(uuid()) @@ -261,10 +261,10 @@ describe('With Policy: auth() test', () => { ` ); - const userDb = withPolicy({ id: 'user1', role: 'USER' }); + const userDb = enhance({ id: 'user1', role: 'USER' }); await expect(userDb.post.create({ data: { title: 'abc' } })).toBeRejectedByPolicy(); - const adminDb = withPolicy({ id: 'user1', role: 'ADMIN' }); + const adminDb = enhance({ id: 'user1', role: 'ADMIN' }); await expect(adminDb.post.create({ data: { title: 'abc' } })).toResolveTruthy(); }); diff --git a/tests/integration/tests/enhancements/with-policy/connect-disconnect.test.ts b/tests/integration/tests/enhancements/with-policy/connect-disconnect.test.ts index 99ae6d626..7bc4a9ed9 100644 --- a/tests/integration/tests/enhancements/with-policy/connect-disconnect.test.ts +++ b/tests/integration/tests/enhancements/with-policy/connect-disconnect.test.ts @@ -47,9 +47,9 @@ describe('With Policy: connect-disconnect', () => { `; it('simple to-many', async () => { - const { withPolicy, prisma } = await loadSchema(modelToMany); + const { enhance, prisma } = await loadSchema(modelToMany); - const db = withPolicy(); + const db = enhance(); // m1-1 -> m2-1 await db.m2.create({ data: { id: 'm2-1', value: 1, deleted: false } }); @@ -164,9 +164,9 @@ describe('With Policy: connect-disconnect', () => { }); it('nested to-many', async () => { - const { withPolicy } = await loadSchema(modelToMany); + const { enhance } = await loadSchema(modelToMany); - const db = withPolicy(); + const db = enhance(); await db.m3.create({ data: { id: 'm3-1', value: 1, deleted: false } }); await expect( @@ -219,9 +219,9 @@ describe('With Policy: connect-disconnect', () => { `; it('to-one', async () => { - const { withPolicy, prisma } = await loadSchema(modelToOne); + const { enhance, prisma } = await loadSchema(modelToOne); - const db = withPolicy(); + const db = enhance(); await db.m2.create({ data: { id: 'm2-1', value: 1, deleted: false } }); await db.m1.create({ @@ -314,9 +314,9 @@ describe('With Policy: connect-disconnect', () => { `; it('implicit many-to-many', async () => { - const { withPolicy, prisma } = await loadSchema(modelImplicitManyToMany); + const { enhance, prisma } = await loadSchema(modelImplicitManyToMany); - const db = withPolicy(); + const db = enhance(); // await prisma.m1.create({ data: { id: 'm1-1', value: 1 } }); // await prisma.m2.create({ data: { id: 'm2-1', value: 1 } }); @@ -379,9 +379,9 @@ describe('With Policy: connect-disconnect', () => { `; it('explicit many-to-many', async () => { - const { withPolicy, prisma } = await loadSchema(modelExplicitManyToMany); + const { enhance, prisma } = await loadSchema(modelExplicitManyToMany); - const db = withPolicy(); + const db = enhance(); await prisma.m1.create({ data: { id: 'm1-1', value: 1 } }); await prisma.m2.create({ data: { id: 'm2-1', value: 1 } }); diff --git a/tests/integration/tests/enhancements/with-policy/deep-nested.test.ts b/tests/integration/tests/enhancements/with-policy/deep-nested.test.ts index 9608f9c62..c4da8349f 100644 --- a/tests/integration/tests/enhancements/with-policy/deep-nested.test.ts +++ b/tests/integration/tests/enhancements/with-policy/deep-nested.test.ts @@ -69,7 +69,7 @@ describe('With Policy:deep nested', () => { beforeEach(async () => { const params = await loadSchema(model); - db = params.withPolicy(); + db = params.enhance(); prisma = params.prisma; }); diff --git a/tests/integration/tests/enhancements/with-policy/empty-policy.test.ts b/tests/integration/tests/enhancements/with-policy/empty-policy.test.ts index 4a1a4d0c5..ee0b61850 100644 --- a/tests/integration/tests/enhancements/with-policy/empty-policy.test.ts +++ b/tests/integration/tests/enhancements/with-policy/empty-policy.test.ts @@ -13,7 +13,7 @@ describe('With Policy:empty policy', () => { }); it('direct operations', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model Model { id String @id @default(uuid()) @@ -22,7 +22,7 @@ describe('With Policy:empty policy', () => { ` ); - const db = withPolicy(); + const db = enhance(); await prisma.model.create({ data: { id: '1', value: 0 } }); await expect(db.model.create({ data: {} })).toBeRejectedByPolicy(); @@ -57,7 +57,7 @@ describe('With Policy:empty policy', () => { }); it('to-many write', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model M1 { id String @id @default(uuid()) @@ -74,7 +74,7 @@ describe('With Policy:empty policy', () => { ` ); - const db = withPolicy(); + const db = enhance(); await expect( db.m1.create({ @@ -88,7 +88,7 @@ describe('With Policy:empty policy', () => { }); it('to-one write', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model M1 { id String @id @default(uuid()) @@ -105,7 +105,7 @@ describe('With Policy:empty policy', () => { ` ); - const db = withPolicy(); + const db = enhance(); await expect( db.m1.create({ diff --git a/tests/integration/tests/enhancements/with-policy/field-comparison.test.ts b/tests/integration/tests/enhancements/with-policy/field-comparison.test.ts index 4f014d2f2..f130b2c94 100644 --- a/tests/integration/tests/enhancements/with-policy/field-comparison.test.ts +++ b/tests/integration/tests/enhancements/with-policy/field-comparison.test.ts @@ -3,7 +3,7 @@ import path from 'path'; const DB_NAME = 'field-comparison'; -describe('WithPolicy: field comparison tests', () => { +describe('Policy: field comparison tests', () => { let origDir: string; let dbUrl: string; let prisma: any; @@ -41,7 +41,7 @@ describe('WithPolicy: field comparison tests', () => { ); prisma = r.prisma; - const db = r.withPolicy(); + const db = r.enhance(); await expect(db.model.create({ data: { x: 1, y: 2 } })).toBeRejectedByPolicy(); await expect(db.model.create({ data: { x: 2, y: 1 } })).toResolveTruthy(); }); @@ -62,7 +62,7 @@ describe('WithPolicy: field comparison tests', () => { ); prisma = r.prisma; - const db = r.withPolicy(); + const db = r.enhance(); await expect(db.model.create({ data: { x: 1, y: 2 } })).toBeRejectedByPolicy(); await expect(db.model.create({ data: { x: 2, y: 1 } })).toResolveTruthy(); }); @@ -83,7 +83,7 @@ describe('WithPolicy: field comparison tests', () => { ); prisma = r.prisma; - const db = r.withPolicy(); + const db = r.enhance(); await expect(db.model.create({ data: { x: 'a', y: ['b', 'c'] } })).toBeRejectedByPolicy(); await expect(db.model.create({ data: { x: 'a', y: ['a', 'c'] } })).toResolveTruthy(); }); @@ -104,7 +104,7 @@ describe('WithPolicy: field comparison tests', () => { ); prisma = r.prisma; - const db = r.withPolicy(); + const db = r.enhance(); await expect(db.model.create({ data: { x: 'a', y: ['b', 'c'] } })).toBeRejectedByPolicy(); await expect(db.model.create({ data: { x: 'a', y: ['a', 'c'] } })).toResolveTruthy(); }); diff --git a/tests/integration/tests/enhancements/with-policy/field-level-policy.test.ts b/tests/integration/tests/enhancements/with-policy/field-level-policy.test.ts index ee89c58e7..ebaf2d858 100644 --- a/tests/integration/tests/enhancements/with-policy/field-level-policy.test.ts +++ b/tests/integration/tests/enhancements/with-policy/field-level-policy.test.ts @@ -1,7 +1,7 @@ import { loadSchema } from '@zenstackhq/testtools'; import path from 'path'; -describe('With Policy: field-level policy', () => { +describe('Policy: field-level policy', () => { let origDir: string; beforeAll(async () => { @@ -13,7 +13,7 @@ describe('With Policy: field-level policy', () => { }); it('read simple', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model User { id Int @id @default(autoincrement()) @@ -37,7 +37,7 @@ describe('With Policy: field-level policy', () => { await prisma.user.create({ data: { id: 1, admin: true } }); - const db = withPolicy(); + const db = enhance(); let r; // y is unreadable @@ -103,7 +103,7 @@ describe('With Policy: field-level policy', () => { }); it('read override', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model User { id Int @id @default(autoincrement()) @@ -128,7 +128,7 @@ describe('With Policy: field-level policy', () => { await prisma.user.create({ data: { id: 1, admin: true } }); - const db = withPolicy(); + const db = enhance(); // created but can't read back await expect( @@ -181,7 +181,7 @@ describe('With Policy: field-level policy', () => { }); it('read filter with auth', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model User { id Int @id @default(autoincrement()) @@ -205,7 +205,7 @@ describe('With Policy: field-level policy', () => { await prisma.user.create({ data: { id: 1, admin: true } }); - let db = withPolicy({ id: 1, admin: false }); + let db = enhance({ id: 1, admin: false }); let r; // y is unreadable @@ -246,7 +246,7 @@ describe('With Policy: field-level policy', () => { expect(r.y).toBeUndefined(); // y is readable - db = withPolicy({ id: 1, admin: true }); + db = enhance({ id: 1, admin: true }); r = await db.model.create({ data: { id: 2, @@ -281,7 +281,7 @@ describe('With Policy: field-level policy', () => { }); it('read filter with relation', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model User { id Int @id @default(autoincrement()) @@ -306,7 +306,7 @@ describe('With Policy: field-level policy', () => { await prisma.user.create({ data: { id: 1, admin: false } }); await prisma.user.create({ data: { id: 2, admin: true } }); - const db = withPolicy(); + const db = enhance(); let r; // y is unreadable @@ -381,7 +381,7 @@ describe('With Policy: field-level policy', () => { }); it('read coverage', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model Model { id Int @id @default(autoincrement()) @@ -393,7 +393,7 @@ describe('With Policy: field-level policy', () => { ` ); - const db = withPolicy(); + const db = enhance(); let r; // y is unreadable @@ -430,7 +430,7 @@ describe('With Policy: field-level policy', () => { }); it('read relation', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model User { id Int @id @default(autoincrement()) @@ -472,7 +472,7 @@ describe('With Policy: field-level policy', () => { }, }); - const db = withPolicy(); + const db = enhance(); // read to-many relation let r = await db.user.findUnique({ @@ -498,7 +498,7 @@ describe('With Policy: field-level policy', () => { }); it('update simple', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model User { id Int @id @default(autoincrement()) @@ -523,7 +523,7 @@ describe('With Policy: field-level policy', () => { await prisma.user.create({ data: { id: 1 }, }); - const db = withPolicy(); + const db = enhance(); await db.model.create({ data: { id: 1, x: 0, y: 0, ownerId: 1 }, @@ -569,7 +569,7 @@ describe('With Policy: field-level policy', () => { }); it('update with override', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model Model { id Int @id @default(autoincrement()) @@ -583,7 +583,7 @@ describe('With Policy: field-level policy', () => { ` ); - const db = withPolicy(); + const db = enhance(); await db.model.create({ data: { id: 1, x: 0, y: 0, z: 0 }, @@ -648,7 +648,7 @@ describe('With Policy: field-level policy', () => { }); it('update filter with relation', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model User { id Int @id @default(autoincrement()) @@ -676,7 +676,7 @@ describe('With Policy: field-level policy', () => { await prisma.user.create({ data: { id: 2, admin: true }, }); - const db = withPolicy(); + const db = enhance(); await db.model.create({ data: { id: 1, x: 0, y: 0, ownerId: 1 }, @@ -706,7 +706,7 @@ describe('With Policy: field-level policy', () => { }); it('update with nested to-many relation', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model User { id Int @id @default(autoincrement()) @@ -734,7 +734,7 @@ describe('With Policy: field-level policy', () => { await prisma.user.create({ data: { id: 2, admin: true, models: { create: { id: 2, x: 0, y: 0 } } }, }); - const db = withPolicy(); + const db = enhance(); await expect( db.user.update({ @@ -758,7 +758,7 @@ describe('With Policy: field-level policy', () => { }); it('update with nested to-one relation', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model User { id Int @id @default(autoincrement()) @@ -786,7 +786,7 @@ describe('With Policy: field-level policy', () => { await prisma.user.create({ data: { id: 2, admin: true, model: { create: { id: 2, x: 0, y: 0 } } }, }); - const db = withPolicy(); + const db = enhance(); await expect( db.user.update({ @@ -828,7 +828,7 @@ describe('With Policy: field-level policy', () => { }); it('update with connect to-many relation', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model User { id Int @id @default(autoincrement()) @@ -854,7 +854,7 @@ describe('With Policy: field-level policy', () => { await prisma.model.create({ data: { id: 1, value: 0 } }); await prisma.model.create({ data: { id: 2, value: 1 } }); - const db = withPolicy(); + const db = enhance(); await expect( db.model.update({ @@ -922,7 +922,7 @@ describe('With Policy: field-level policy', () => { }); it('update with connect to-one relation', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model User { id Int @id @default(autoincrement()) @@ -948,7 +948,7 @@ describe('With Policy: field-level policy', () => { await prisma.model.create({ data: { id: 1, value: 0 } }); await prisma.model.create({ data: { id: 2, value: 1 } }); - const db = withPolicy(); + const db = enhance(); await expect( db.model.update({ @@ -1010,7 +1010,7 @@ describe('With Policy: field-level policy', () => { }); it('updateMany simple', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model User { id Int @id @default(autoincrement()) @@ -1042,7 +1042,7 @@ describe('With Policy: field-level policy', () => { }, }, }); - const db = withPolicy(); + const db = enhance(); await expect(db.model.updateMany({ data: { y: 2 } })).resolves.toEqual({ count: 1 }); await expect(db.model.findUnique({ where: { id: 1 } })).resolves.toEqual( @@ -1054,7 +1054,7 @@ describe('With Policy: field-level policy', () => { }); it('updateMany override', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model Model { id Int @id @default(autoincrement()) @@ -1067,7 +1067,7 @@ describe('With Policy: field-level policy', () => { ` ); - const db = withPolicy(); + const db = enhance(); await db.model.create({ data: { id: 1, x: 0, y: 0 } }); await db.model.create({ data: { id: 2, x: 1, y: 0 } }); @@ -1084,7 +1084,7 @@ describe('With Policy: field-level policy', () => { }); it('updateMany nested', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model User { id Int @id @default(autoincrement()) @@ -1116,7 +1116,7 @@ describe('With Policy: field-level policy', () => { }, }, }); - const db = withPolicy(); + const db = enhance(); await expect( db.user.update({ where: { id: 1 }, data: { models: { updateMany: { data: { y: 2 } } } } }) @@ -1144,7 +1144,7 @@ describe('With Policy: field-level policy', () => { }); it('this expression', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model User { id Int @id @@ -1157,24 +1157,24 @@ describe('With Policy: field-level policy', () => { await prisma.user.create({ data: { id: 1, username: 'test' } }); // admin - let r = await withPolicy({ id: 1, admin: true }).user.findFirst(); + let r = await enhance({ id: 1, admin: true }).user.findFirst(); expect(r.username).toEqual('test'); // owner - r = await withPolicy({ id: 1 }).user.findFirst(); + r = await enhance({ id: 1 }).user.findFirst(); expect(r.username).toEqual('test'); // anonymous - r = await withPolicy().user.findFirst(); + r = await enhance().user.findFirst(); expect(r.username).toBeUndefined(); // non-owner - r = await withPolicy({ id: 2 }).user.findFirst(); + r = await enhance({ id: 2 }).user.findFirst(); expect(r.username).toBeUndefined(); }); it('collection predicate', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model User { id Int @id @default(autoincrement()) @@ -1206,7 +1206,7 @@ describe('With Policy: field-level policy', () => { ` ); - const db = withPolicy(); + const db = enhance(); await prisma.user.create({ data: { @@ -1269,7 +1269,7 @@ describe('With Policy: field-level policy', () => { }); it('deny only without field access', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model User { id Int @id @default(autoincrement()) @@ -1285,14 +1285,14 @@ describe('With Policy: field-level policy', () => { }); await expect( - withPolicy({ id: 1, role: 'ADMIN' }).user.update({ + enhance({ id: 1, role: 'ADMIN' }).user.update({ where: { id: user.id }, data: { role: 'ADMIN' }, }) ).toResolveTruthy(); await expect( - withPolicy({ id: 1, role: 'USER' }).user.update({ + enhance({ id: 1, role: 'USER' }).user.update({ where: { id: user.id }, data: { role: 'ADMIN' }, }) @@ -1300,7 +1300,7 @@ describe('With Policy: field-level policy', () => { }); it('deny only with field access', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model User { id Int @id @default(autoincrement()) @@ -1317,14 +1317,14 @@ describe('With Policy: field-level policy', () => { }); await expect( - withPolicy({ id: 1, role: 'ADMIN' }).user.update({ + enhance({ id: 1, role: 'ADMIN' }).user.update({ where: { id: user1.id }, data: { role: 'ADMIN' }, }) ).toResolveTruthy(); await expect( - withPolicy({ id: 1, role: 'USER' }).user.update({ + enhance({ id: 1, role: 'USER' }).user.update({ where: { id: user1.id }, data: { role: 'ADMIN' }, }) @@ -1335,7 +1335,7 @@ describe('With Policy: field-level policy', () => { }); await expect( - withPolicy({ id: 1, role: 'ADMIN' }).user.update({ + enhance({ id: 1, role: 'ADMIN' }).user.update({ where: { id: user2.id }, data: { role: 'ADMIN' }, }) diff --git a/tests/integration/tests/enhancements/with-policy/field-validation.test.ts b/tests/integration/tests/enhancements/with-policy/field-validation.test.ts index 8727f1561..ca71841db 100644 --- a/tests/integration/tests/enhancements/with-policy/field-validation.test.ts +++ b/tests/integration/tests/enhancements/with-policy/field-validation.test.ts @@ -5,7 +5,7 @@ describe('With Policy: field validation', () => { let db: FullDbClientContract; beforeAll(async () => { - const { withPolicy, prisma: _prisma } = await loadSchema( + const { enhance, prisma: _prisma } = await loadSchema( ` model User { id String @id @default(cuid()) @@ -49,7 +49,7 @@ describe('With Policy: field validation', () => { } ` ); - db = withPolicy(); + db = enhance(); }); beforeEach(() => { diff --git a/tests/integration/tests/enhancements/with-policy/fluent-api.test.ts b/tests/integration/tests/enhancements/with-policy/fluent-api.test.ts index 264c5da28..6c27aab1c 100644 --- a/tests/integration/tests/enhancements/with-policy/fluent-api.test.ts +++ b/tests/integration/tests/enhancements/with-policy/fluent-api.test.ts @@ -13,7 +13,7 @@ describe('With Policy: fluent API', () => { }); it('fluent api', async () => { - const { withPolicy, prisma } = await loadSchema( + const { enhance, prisma } = await loadSchema( ` model User { id Int @id @@ -58,7 +58,7 @@ model Post { }, }); - const db = withPolicy({ id: 1 }); + const db = enhance({ id: 1 }); // check policies await expect(db.user.findUnique({ where: { id: 1 } }).posts()).resolves.toHaveLength(2); diff --git a/tests/integration/tests/enhancements/with-policy/multi-field-unique.test.ts b/tests/integration/tests/enhancements/with-policy/multi-field-unique.test.ts index 3dcc07850..f0eeb1a8a 100644 --- a/tests/integration/tests/enhancements/with-policy/multi-field-unique.test.ts +++ b/tests/integration/tests/enhancements/with-policy/multi-field-unique.test.ts @@ -13,7 +13,7 @@ describe('With Policy: multi-field unique', () => { }); it('toplevel crud test unnamed constraint', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model Model { id String @id @default(uuid()) @@ -28,7 +28,7 @@ describe('With Policy: multi-field unique', () => { ` ); - const db = withPolicy(); + const db = enhance(); await expect(db.model.create({ data: { a: 'a1', b: 'b1', x: 1 } })).toResolveTruthy(); await expect(db.model.create({ data: { a: 'a1', b: 'b1', x: 2 } })).toBeRejectedWithCode('P2002'); @@ -43,7 +43,7 @@ describe('With Policy: multi-field unique', () => { }); it('toplevel crud test named constraint', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model Model { id String @id @default(uuid()) @@ -58,7 +58,7 @@ describe('With Policy: multi-field unique', () => { ` ); - const db = withPolicy(); + const db = enhance(); await expect(db.model.create({ data: { a: 'a1', b: 'b1', x: 1 } })).toResolveTruthy(); await expect(db.model.findUnique({ where: { myconstraint: { a: 'a1', b: 'b1' } } })).toResolveTruthy(); @@ -73,7 +73,7 @@ describe('With Policy: multi-field unique', () => { }); it('nested crud test', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model M1 { id String @id @default(uuid()) @@ -95,7 +95,7 @@ describe('With Policy: multi-field unique', () => { ` ); - const db = withPolicy(); + const db = enhance(); await expect(db.m1.create({ data: { id: '1', m2: { create: { a: 'a1', b: 'b1', x: 1 } } } })).toResolveTruthy(); await expect( diff --git a/tests/integration/tests/enhancements/with-policy/multi-id-fields.test.ts b/tests/integration/tests/enhancements/with-policy/multi-id-fields.test.ts index f48cdba45..227dc5a27 100644 --- a/tests/integration/tests/enhancements/with-policy/multi-id-fields.test.ts +++ b/tests/integration/tests/enhancements/with-policy/multi-id-fields.test.ts @@ -13,7 +13,7 @@ describe('With Policy: multiple id fields', () => { }); it('multi-id fields', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model A { x String @@ -43,7 +43,7 @@ describe('With Policy: multiple id fields', () => { ` ); - const db = withPolicy(); + const db = enhance(); await expect(db.a.create({ data: { x: '1', y: 1, value: 0 } })).toBeRejectedByPolicy(); await expect(db.a.create({ data: { x: '1', y: 2, value: 1 } })).toResolveTruthy(); @@ -70,7 +70,7 @@ describe('With Policy: multiple id fields', () => { }); it('multi-id auth', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model User { x String @@ -124,7 +124,7 @@ describe('With Policy: multiple id fields', () => { await prisma.user.create({ data: { x: '1', y: '1' } }); await prisma.user.create({ data: { x: '1', y: '2' } }); - const anonDb = withPolicy(); + const anonDb = enhance(); await expect( anonDb.m.create({ data: { owner: { connect: { x_y: { x: '1', y: '2' } } } } }) @@ -139,7 +139,7 @@ describe('With Policy: multiple id fields', () => { anonDb.n.create({ data: { owner: { connect: { x_y: { x: '1', y: '1' } } } } }) ).toBeRejectedByPolicy(); - const db = withPolicy({ x: '1', y: '1' }); + const db = enhance({ x: '1', y: '1' }); await expect(db.m.create({ data: { owner: { connect: { x_y: { x: '1', y: '2' } } } } })).toBeRejectedByPolicy(); await expect(db.m.create({ data: { owner: { connect: { x_y: { x: '1', y: '1' } } } } })).toResolveTruthy(); @@ -149,13 +149,13 @@ describe('With Policy: multiple id fields', () => { await expect(db.p.create({ data: { owner: { connect: { x_y: { x: '1', y: '2' } } } } })).toResolveTruthy(); await expect( - withPolicy(undefined).q.create({ data: { owner: { connect: { x_y: { x: '1', y: '1' } } } } }) + enhance(undefined).q.create({ data: { owner: { connect: { x_y: { x: '1', y: '1' } } } } }) ).toBeRejectedByPolicy(); await expect(db.q.create({ data: { owner: { connect: { x_y: { x: '1', y: '2' } } } } })).toResolveTruthy(); }); it('multi-id to-one nested write', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model A { x Int @@ -177,7 +177,7 @@ describe('With Policy: multiple id fields', () => { } ` ); - const db = withPolicy(); + const db = enhance(); await expect( db.b.create({ data: { @@ -205,7 +205,7 @@ describe('With Policy: multiple id fields', () => { }); it('multi-id to-many nested write', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model A { x Int @@ -237,7 +237,7 @@ describe('With Policy: multiple id fields', () => { } ` ); - const db = withPolicy(); + const db = enhance(); await expect( db.b.create({ data: { diff --git a/tests/integration/tests/enhancements/with-policy/nested-to-many.test.ts b/tests/integration/tests/enhancements/with-policy/nested-to-many.test.ts index b112aeeb1..777af1118 100644 --- a/tests/integration/tests/enhancements/with-policy/nested-to-many.test.ts +++ b/tests/integration/tests/enhancements/with-policy/nested-to-many.test.ts @@ -13,7 +13,7 @@ describe('With Policy:nested to-many', () => { }); it('read filtering', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model M1 { id String @id @default(uuid()) @@ -34,7 +34,7 @@ describe('With Policy:nested to-many', () => { ` ); - const db = withPolicy(); + const db = enhance(); let read = await db.m1.create({ include: { m2: true }, @@ -62,7 +62,7 @@ describe('With Policy:nested to-many', () => { }); it('read condition hoisting', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model M1 { id String @id @default(uuid()) @@ -108,7 +108,7 @@ describe('With Policy:nested to-many', () => { ` ); - const db = withPolicy(); + const db = enhance(); await db.m1.create({ include: { m2: true }, @@ -144,7 +144,7 @@ describe('With Policy:nested to-many', () => { }); it('create simple', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model M1 { id String @id @default(uuid()) @@ -165,7 +165,7 @@ describe('With Policy:nested to-many', () => { ` ); - const db = withPolicy(); + const db = enhance(); // single create denied await expect( @@ -211,7 +211,7 @@ describe('With Policy:nested to-many', () => { }); it('update simple', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model M1 { id String @id @default(uuid()) @@ -233,7 +233,7 @@ describe('With Policy:nested to-many', () => { ` ); - const db = withPolicy(); + const db = enhance(); await db.m1.create({ data: { @@ -285,7 +285,7 @@ describe('With Policy:nested to-many', () => { }); it('update with create from one to many', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model M1 { id String @id @default(uuid()) @@ -307,7 +307,7 @@ describe('With Policy:nested to-many', () => { ` ); - const db = withPolicy(); + const db = enhance(); await db.m1.create({ data: { @@ -342,7 +342,7 @@ describe('With Policy:nested to-many', () => { }); it('update with create from many to one', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model M1 { id String @id @default(uuid()) @@ -364,7 +364,7 @@ describe('With Policy:nested to-many', () => { ` ); - const db = withPolicy(); + const db = enhance(); await db.m2.create({ data: { id: '1' } }); @@ -392,7 +392,7 @@ describe('With Policy:nested to-many', () => { }); it('update with delete', async () => { - const { withPolicy, prisma } = await loadSchema( + const { enhance, prisma } = await loadSchema( ` model M1 { id String @id @default(uuid()) @@ -415,7 +415,7 @@ describe('With Policy:nested to-many', () => { ` ); - const db = withPolicy(); + const db = enhance(); await db.m1.create({ data: { @@ -496,7 +496,7 @@ describe('With Policy:nested to-many', () => { }); it('create with nested read', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model M1 { id String @id @default(uuid()) @@ -530,7 +530,7 @@ describe('With Policy:nested to-many', () => { ` ); - const db = withPolicy(); + const db = enhance(); await expect( db.m1.create({ @@ -589,7 +589,7 @@ describe('With Policy:nested to-many', () => { }); it('update with nested read', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model M1 { id String @id @default(uuid()) @@ -621,7 +621,7 @@ describe('With Policy:nested to-many', () => { ` ); - const db = withPolicy(); + const db = enhance(); await db.m1.create({ data: { id: '1', diff --git a/tests/integration/tests/enhancements/with-policy/nested-to-one.test.ts b/tests/integration/tests/enhancements/with-policy/nested-to-one.test.ts index 2e14b6d02..4b30c095f 100644 --- a/tests/integration/tests/enhancements/with-policy/nested-to-one.test.ts +++ b/tests/integration/tests/enhancements/with-policy/nested-to-one.test.ts @@ -13,7 +13,7 @@ describe('With Policy:nested to-one', () => { }); it('read filtering for optional relation', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model M1 { id String @id @default(uuid()) @@ -34,7 +34,7 @@ describe('With Policy:nested to-one', () => { ` ); - const db = withPolicy(); + const db = enhance(); let read = await db.m1.create({ include: { m2: true }, @@ -60,7 +60,7 @@ describe('With Policy:nested to-one', () => { }); it('read rejection for non-optional relation', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model M1 { id String @id @default(uuid()) @@ -91,7 +91,7 @@ describe('With Policy:nested to-one', () => { }, }); - const db = withPolicy(); + const db = enhance(); await expect(db.m2.findUnique({ where: { id: '1' }, include: { m1: true } })).toResolveFalsy(); await expect(db.m2.findMany({ include: { m1: true } })).resolves.toHaveLength(0); @@ -100,7 +100,7 @@ describe('With Policy:nested to-one', () => { }); it('read condition hoisting', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model M1 { id String @id @default(uuid()) @@ -134,7 +134,7 @@ describe('With Policy:nested to-one', () => { ` ); - const db = withPolicy(); + const db = enhance(); await db.m1.create({ include: { m2: true }, @@ -153,7 +153,7 @@ describe('With Policy:nested to-one', () => { }); it('create and update tests', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model M1 { id String @id @default(uuid()) @@ -175,7 +175,7 @@ describe('With Policy:nested to-one', () => { ` ); - const db = withPolicy(); + const db = enhance(); // create denied await expect( @@ -213,7 +213,7 @@ describe('With Policy:nested to-one', () => { }); it('nested create', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model M1 { id String @id @default(uuid()) @@ -236,7 +236,7 @@ describe('With Policy:nested to-one', () => { { logPrismaQuery: true } ); - const db = withPolicy(); + const db = enhance(); await db.m1.create({ data: { @@ -269,7 +269,7 @@ describe('With Policy:nested to-one', () => { }); it('nested delete', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model M1 { id String @id @default(uuid()) @@ -292,7 +292,7 @@ describe('With Policy:nested to-one', () => { ` ); - const db = withPolicy(); + const db = enhance(); await db.m1.create({ data: { @@ -335,7 +335,7 @@ describe('With Policy:nested to-one', () => { }); it('nested relation delete', async () => { - const { withPolicy, prisma } = await loadSchema( + const { enhance, prisma } = await loadSchema( ` model User { id String @id @default(uuid()) @@ -356,7 +356,7 @@ describe('With Policy:nested to-one', () => { ` ); - await withPolicy({ id: 'user1' }).m1.create({ + await enhance({ id: 'user1' }).m1.create({ data: { id: 'm1', value: 1, @@ -364,7 +364,7 @@ describe('With Policy:nested to-one', () => { }); await expect( - withPolicy({ id: 'user2' }).user.create({ + enhance({ id: 'user2' }).user.create({ data: { id: 'user2', m1: { @@ -375,7 +375,7 @@ describe('With Policy:nested to-one', () => { ).toResolveTruthy(); await expect( - withPolicy({ id: 'user2' }).user.update({ + enhance({ id: 'user2' }).user.update({ where: { id: 'user2' }, data: { m1: { delete: true }, @@ -384,7 +384,7 @@ describe('With Policy:nested to-one', () => { ).toBeRejectedByPolicy(); await expect( - withPolicy({ id: 'user1' }).user.create({ + enhance({ id: 'user1' }).user.create({ data: { id: 'user1', m1: { @@ -395,7 +395,7 @@ describe('With Policy:nested to-one', () => { ).toResolveTruthy(); await expect( - withPolicy({ id: 'user1' }).user.update({ + enhance({ id: 'user1' }).user.update({ where: { id: 'user1' }, data: { m1: { delete: true }, diff --git a/tests/integration/tests/enhancements/with-policy/options.test.ts b/tests/integration/tests/enhancements/with-policy/options.test.ts index 79adb8c49..55c5458f4 100644 --- a/tests/integration/tests/enhancements/with-policy/options.test.ts +++ b/tests/integration/tests/enhancements/with-policy/options.test.ts @@ -1,4 +1,3 @@ -import { withPolicy } from '@zenstackhq/runtime'; import { loadSchema } from '@zenstackhq/testtools'; import path from 'path'; diff --git a/tests/integration/tests/enhancements/with-policy/petstore-sample.test.ts b/tests/integration/tests/enhancements/with-policy/petstore-sample.test.ts index 9c251faf5..691c6176a 100644 --- a/tests/integration/tests/enhancements/with-policy/petstore-sample.test.ts +++ b/tests/integration/tests/enhancements/with-policy/petstore-sample.test.ts @@ -7,11 +7,11 @@ describe('Pet Store Policy Tests', () => { let prisma: FullDbClientContract; beforeAll(async () => { - const { withPolicy, prisma: _prisma } = await loadSchemaFromFile( + const { enhance, prisma: _prisma } = await loadSchemaFromFile( path.join(__dirname, '../../schema/petstore.zmodel'), { addPrelude: false } ); - getDb = withPolicy; + getDb = enhance; prisma = _prisma; }); diff --git a/tests/integration/tests/enhancements/with-policy/post-update.test.ts b/tests/integration/tests/enhancements/with-policy/post-update.test.ts index c40d338a3..e2d7e0156 100644 --- a/tests/integration/tests/enhancements/with-policy/post-update.test.ts +++ b/tests/integration/tests/enhancements/with-policy/post-update.test.ts @@ -13,7 +13,7 @@ describe('With Policy: post update', () => { }); it('simple allow', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model Model { id String @id @default(uuid()) @@ -25,7 +25,7 @@ describe('With Policy: post update', () => { ` ); - const db = withPolicy(); + const db = enhance(); await expect(db.model.create({ data: { id: '1', value: 0 } })).toResolveTruthy(); await expect(db.model.update({ where: { id: '1' }, data: { value: 1 } })).toBeRejectedByPolicy(); @@ -33,7 +33,7 @@ describe('With Policy: post update', () => { }); it('simple deny', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model Model { id String @id @default(uuid()) @@ -45,7 +45,7 @@ describe('With Policy: post update', () => { ` ); - const db = withPolicy(); + const db = enhance(); await expect(db.model.create({ data: { id: '1', value: 0 } })).toResolveTruthy(); await expect(db.model.update({ where: { id: '1' }, data: { value: 1 } })).toBeRejectedByPolicy(); @@ -53,7 +53,7 @@ describe('With Policy: post update', () => { }); it('mixed pre and post', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model Model { id String @id @default(uuid()) @@ -65,7 +65,7 @@ describe('With Policy: post update', () => { ` ); - const db = withPolicy(); + const db = enhance(); await expect(db.model.create({ data: { id: '1', value: 0 } })).toResolveTruthy(); await expect(db.model.update({ where: { id: '1' }, data: { value: 1 } })).toBeRejectedByPolicy(); @@ -76,7 +76,7 @@ describe('With Policy: post update', () => { }); it('functions pre-update', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model Model { id String @id @default(uuid()) @@ -89,7 +89,7 @@ describe('With Policy: post update', () => { ` ); - const db = withPolicy(); + const db = enhance(); await prisma.model.create({ data: { id: '1', value: 'good', x: 1 } }); await expect(db.model.update({ where: { id: '1' }, data: { value: 'hello' } })).toBeRejectedByPolicy(); @@ -100,7 +100,7 @@ describe('With Policy: post update', () => { }); it('functions post-update', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model Model { id String @id @default(uuid()) @@ -114,7 +114,7 @@ describe('With Policy: post update', () => { { logPrismaQuery: true } ); - const db = withPolicy(); + const db = enhance(); await prisma.model.create({ data: { id: '1', value: 'good', x: 1 } }); await expect(db.model.update({ where: { id: '1' }, data: { value: 'nice' } })).toBeRejectedByPolicy(); @@ -124,7 +124,7 @@ describe('With Policy: post update', () => { }); it('collection predicate pre-update', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model M1 { id String @id @default(uuid()) @@ -145,7 +145,7 @@ describe('With Policy: post update', () => { ` ); - const db = withPolicy(); + const db = enhance(); await prisma.m1.create({ data: { @@ -181,7 +181,7 @@ describe('With Policy: post update', () => { }); it('collection predicate post-update', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model M1 { id String @id @default(uuid()) @@ -202,7 +202,7 @@ describe('With Policy: post update', () => { ` ); - const db = withPolicy(); + const db = enhance(); await prisma.m1.create({ data: { @@ -238,7 +238,7 @@ describe('With Policy: post update', () => { }); it('nested to-many', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model M1 { id String @id @default(uuid()) @@ -258,7 +258,7 @@ describe('With Policy: post update', () => { ` ); - const db = withPolicy(); + const db = enhance(); await expect( db.m1.create({ @@ -297,7 +297,7 @@ describe('With Policy: post update', () => { }); it('nested to-one', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model M1 { id String @id @default(uuid()) @@ -317,7 +317,7 @@ describe('With Policy: post update', () => { ` ); - const db = withPolicy(); + const db = enhance(); await expect( db.m1.create({ @@ -350,7 +350,7 @@ describe('With Policy: post update', () => { }); it('nested select', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model M1 { id String @id @default(uuid()) @@ -370,7 +370,7 @@ describe('With Policy: post update', () => { ` ); - const db = withPolicy(); + const db = enhance(); await expect( db.m1.create({ @@ -401,7 +401,7 @@ describe('With Policy: post update', () => { }); it('deep nesting', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model M1 { id String @id @default(uuid()) @@ -432,7 +432,7 @@ describe('With Policy: post update', () => { ` ); - const db = withPolicy(); + const db = enhance(); await expect( db.m1.create({ diff --git a/tests/integration/tests/enhancements/with-policy/postgres.test.ts b/tests/integration/tests/enhancements/with-policy/postgres.test.ts index caed6a5ce..7653b89f7 100644 --- a/tests/integration/tests/enhancements/with-policy/postgres.test.ts +++ b/tests/integration/tests/enhancements/with-policy/postgres.test.ts @@ -16,14 +16,14 @@ describe('With Policy: with postgres', () => { beforeEach(async () => { dbUrl = await createPostgresDb(DB_NAME); - const { prisma: _prisma, withPolicy } = await loadSchemaFromFile( + const { prisma: _prisma, enhance } = await loadSchemaFromFile( path.join(__dirname, '../../schema/todo-pg.zmodel'), { provider: 'postgresql', dbUrl, } ); - getDb = withPolicy; + getDb = enhance; prisma = _prisma; }); diff --git a/tests/integration/tests/enhancements/with-policy/query-reduction.test.ts b/tests/integration/tests/enhancements/with-policy/query-reduction.test.ts index 1654fba96..264119453 100644 --- a/tests/integration/tests/enhancements/with-policy/query-reduction.test.ts +++ b/tests/integration/tests/enhancements/with-policy/query-reduction.test.ts @@ -13,7 +13,7 @@ describe('With Policy: query reduction', () => { }); it('test query reduction', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model User { id Int @id @default(autoincrement()) @@ -65,8 +65,8 @@ describe('With Policy: query reduction', () => { }, }); - const dbUser1 = withPolicy({ id: 1 }); - const dbUser2 = withPolicy({ id: 2 }); + const dbUser1 = enhance({ id: 1 }); + const dbUser2 = enhance({ id: 2 }); await expect( dbUser1.user.findMany({ diff --git a/tests/integration/tests/enhancements/with-policy/refactor.test.ts b/tests/integration/tests/enhancements/with-policy/refactor.test.ts index 126c038fa..0cd490f6c 100644 --- a/tests/integration/tests/enhancements/with-policy/refactor.test.ts +++ b/tests/integration/tests/enhancements/with-policy/refactor.test.ts @@ -21,7 +21,7 @@ describe('With Policy: refactor tests', () => { beforeEach(async () => { dbUrl = await createPostgresDb(DB_NAME); - const { prisma: _prisma, withPolicy } = await loadSchemaFromFile( + const { prisma: _prisma, enhance } = await loadSchemaFromFile( path.join(__dirname, '../../schema/refactor-pg.zmodel'), { provider: 'postgresql', @@ -29,7 +29,7 @@ describe('With Policy: refactor tests', () => { logPrismaQuery: true, } ); - getDb = withPolicy; + getDb = enhance; prisma = _prisma; anonDb = getDb(); user1Db = getDb({ id: 1 }); diff --git a/tests/integration/tests/enhancements/with-policy/relation-many-to-many-filter.test.ts b/tests/integration/tests/enhancements/with-policy/relation-many-to-many-filter.test.ts index fe0c686db..e7ddb043e 100644 --- a/tests/integration/tests/enhancements/with-policy/relation-many-to-many-filter.test.ts +++ b/tests/integration/tests/enhancements/with-policy/relation-many-to-many-filter.test.ts @@ -35,9 +35,9 @@ describe('With Policy: relation many-to-many filter', () => { `; it('some filter', async () => { - const { withPolicy } = await loadSchema(model); + const { enhance } = await loadSchema(model); - const db = withPolicy(); + const db = enhance(); await db.m1.create({ data: { @@ -128,9 +128,9 @@ describe('With Policy: relation many-to-many filter', () => { }); it('none filter', async () => { - const { withPolicy } = await loadSchema(model); + const { enhance } = await loadSchema(model); - const db = withPolicy(); + const db = enhance(); await db.m1.create({ data: { @@ -211,9 +211,9 @@ describe('With Policy: relation many-to-many filter', () => { }); it('every filter', async () => { - const { withPolicy } = await loadSchema(model); + const { enhance } = await loadSchema(model); - const db = withPolicy(); + const db = enhance(); await db.m1.create({ data: { diff --git a/tests/integration/tests/enhancements/with-policy/relation-one-to-many-filter.test.ts b/tests/integration/tests/enhancements/with-policy/relation-one-to-many-filter.test.ts index 3737bbf4c..1a1c40406 100644 --- a/tests/integration/tests/enhancements/with-policy/relation-one-to-many-filter.test.ts +++ b/tests/integration/tests/enhancements/with-policy/relation-one-to-many-filter.test.ts @@ -45,9 +45,9 @@ describe('With Policy: relation one-to-many filter', () => { `; it('some filter', async () => { - const { withPolicy } = await loadSchema(model); + const { enhance } = await loadSchema(model); - const db = withPolicy(); + const db = enhance(); // m1 with m2 and m3 await db.m1.create({ @@ -163,9 +163,9 @@ describe('With Policy: relation one-to-many filter', () => { }); it('none filter', async () => { - const { withPolicy } = await loadSchema(model); + const { enhance } = await loadSchema(model); - const db = withPolicy(); + const db = enhance(); // m1 with m2 and m3 await db.m1.create({ @@ -281,9 +281,9 @@ describe('With Policy: relation one-to-many filter', () => { }); it('every filter', async () => { - const { withPolicy } = await loadSchema(model); + const { enhance } = await loadSchema(model); - const db = withPolicy(); + const db = enhance(); // m1 with m2 and m3 await db.m1.create({ @@ -399,9 +399,9 @@ describe('With Policy: relation one-to-many filter', () => { }); it('_count filter', async () => { - const { withPolicy } = await loadSchema(model); + const { enhance } = await loadSchema(model); - const db = withPolicy(); + const db = enhance(); // m1 with m2 and m3 await db.m1.create({ diff --git a/tests/integration/tests/enhancements/with-policy/relation-one-to-one-filter.test.ts b/tests/integration/tests/enhancements/with-policy/relation-one-to-one-filter.test.ts index 7c26bc854..d076e18e5 100644 --- a/tests/integration/tests/enhancements/with-policy/relation-one-to-one-filter.test.ts +++ b/tests/integration/tests/enhancements/with-policy/relation-one-to-one-filter.test.ts @@ -45,9 +45,9 @@ describe('With Policy: relation one-to-one filter', () => { `; it('is filter', async () => { - const { withPolicy } = await loadSchema(model); + const { enhance } = await loadSchema(model); - const db = withPolicy(); + const db = enhance(); // m1 with m2 and m3 await db.m1.create({ @@ -152,9 +152,9 @@ describe('With Policy: relation one-to-one filter', () => { }); it('isNot filter', async () => { - const { withPolicy } = await loadSchema(model); + const { enhance } = await loadSchema(model); - const db = withPolicy(); + const db = enhance(); // m1 with m2 and m3 await db.m1.create({ @@ -261,9 +261,9 @@ describe('With Policy: relation one-to-one filter', () => { }); it('direct object filter', async () => { - const { withPolicy } = await loadSchema(model); + const { enhance } = await loadSchema(model); - const db = withPolicy(); + const db = enhance(); // m1 with m2 and m3 await db.m1.create({ diff --git a/tests/integration/tests/enhancements/with-policy/self-relation.test.ts b/tests/integration/tests/enhancements/with-policy/self-relation.test.ts index dc7cb96ca..525d30043 100644 --- a/tests/integration/tests/enhancements/with-policy/self-relation.test.ts +++ b/tests/integration/tests/enhancements/with-policy/self-relation.test.ts @@ -13,7 +13,7 @@ describe('With Policy: self relations', () => { }); it('one-to-one', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model User { id Int @id @default(autoincrement()) @@ -28,7 +28,7 @@ describe('With Policy: self relations', () => { ` ); - const db = withPolicy(); + const db = enhance(); // create denied await expect( @@ -90,7 +90,7 @@ describe('With Policy: self relations', () => { }); it('one-to-many', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model User { id Int @id @default(autoincrement()) @@ -105,7 +105,7 @@ describe('With Policy: self relations', () => { ` ); - const db = withPolicy(); + const db = enhance(); // create denied await expect( @@ -157,7 +157,7 @@ describe('With Policy: self relations', () => { }); it('many-to-many', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model User { id Int @id @default(autoincrement()) @@ -171,7 +171,7 @@ describe('With Policy: self relations', () => { ` ); - const db = withPolicy(); + const db = enhance(); // create denied await expect( diff --git a/tests/integration/tests/enhancements/with-policy/subscription.test.ts b/tests/integration/tests/enhancements/with-policy/subscription.test.ts index 2befdd42a..a4dccf807 100644 --- a/tests/integration/tests/enhancements/with-policy/subscription.test.ts +++ b/tests/integration/tests/enhancements/with-policy/subscription.test.ts @@ -17,7 +17,7 @@ describe.skip('With Policy: subscription test', () => { }); it('subscribe auth check', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model User { id Int @id @default(autoincrement()) @@ -42,11 +42,11 @@ describe.skip('With Policy: subscription test', () => { const rawSub = await prisma.model.subscribe(); - const anonDb = withPolicy(); + const anonDb = enhance(); console.log('Anonymous db subscribing'); const anonSub = await anonDb.model.subscribe(); - const authDb = withPolicy({ id: 1 }); + const authDb = enhance({ id: 1 }); console.log('Auth db subscribing'); const authSub = await authDb.model.subscribe(); @@ -75,7 +75,7 @@ describe.skip('With Policy: subscription test', () => { }); it('subscribe model check', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model Model { id Int @id @default(autoincrement()) @@ -96,7 +96,7 @@ describe.skip('With Policy: subscription test', () => { const rawSub = await prisma.model.subscribe(); - const enhanced = withPolicy(); + const enhanced = enhance(); console.log('Auth db subscribing'); const enhancedSub = await enhanced.model.subscribe(); @@ -130,7 +130,7 @@ describe.skip('With Policy: subscription test', () => { }); it('subscribe partial', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model Model { id Int @id @default(autoincrement()) @@ -151,7 +151,7 @@ describe.skip('With Policy: subscription test', () => { const rawSub = await prisma.model.subscribe({ create: {} }); - const enhanced = withPolicy(); + const enhanced = enhance(); console.log('Auth db subscribing'); const enhancedSub = await enhanced.model.subscribe({ create: {} }); @@ -185,7 +185,7 @@ describe.skip('With Policy: subscription test', () => { }); it('subscribe mixed model check', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model Model { id Int @id @default(autoincrement()) @@ -210,7 +210,7 @@ describe.skip('With Policy: subscription test', () => { delete: { before: { name: { contains: 'world' } } }, }); - const enhanced = withPolicy(); + const enhanced = enhance(); console.log('Auth db subscribing'); const enhancedSub = await enhanced.model.subscribe({ create: { after: { name: { contains: 'world' } } }, diff --git a/tests/integration/tests/enhancements/with-policy/todo-sample.test.ts b/tests/integration/tests/enhancements/with-policy/todo-sample.test.ts index 2b7dd416b..fe26dd561 100644 --- a/tests/integration/tests/enhancements/with-policy/todo-sample.test.ts +++ b/tests/integration/tests/enhancements/with-policy/todo-sample.test.ts @@ -7,11 +7,11 @@ describe('Todo Policy Tests', () => { let prisma: FullDbClientContract; beforeAll(async () => { - const { withPolicy, prisma: _prisma } = await loadSchemaFromFile( + const { enhance, prisma: _prisma } = await loadSchemaFromFile( path.join(__dirname, '../../schema/todo.zmodel'), { addPrelude: false } ); - getDb = withPolicy; + getDb = enhance; prisma = _prisma; }); diff --git a/tests/integration/tests/enhancements/with-policy/toplevel-operations.test.ts b/tests/integration/tests/enhancements/with-policy/toplevel-operations.test.ts index 99179e015..61f25dc25 100644 --- a/tests/integration/tests/enhancements/with-policy/toplevel-operations.test.ts +++ b/tests/integration/tests/enhancements/with-policy/toplevel-operations.test.ts @@ -13,7 +13,7 @@ describe('With Policy: toplevel operations', () => { }); it('read tests', async () => { - const { withPolicy, prisma } = await loadSchema( + const { enhance, prisma } = await loadSchema( ` model Model { id String @id @default(uuid()) @@ -25,7 +25,7 @@ describe('With Policy: toplevel operations', () => { ` ); - const db = withPolicy(); + const db = enhance(); await expect( db.model.create({ @@ -62,7 +62,7 @@ describe('With Policy: toplevel operations', () => { }); it('write tests', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model Model { id String @id @default(uuid()) @@ -75,7 +75,7 @@ describe('With Policy: toplevel operations', () => { ` ); - const db = withPolicy(); + const db = enhance(); // create denied await expect( @@ -148,7 +148,7 @@ describe('With Policy: toplevel operations', () => { }); it('delete tests', async () => { - const { withPolicy, prisma } = await loadSchema( + const { enhance, prisma } = await loadSchema( ` model Model { id String @id @default(uuid()) @@ -161,7 +161,7 @@ describe('With Policy: toplevel operations', () => { ` ); - const db = withPolicy(); + const db = enhance(); await expect(db.model.delete({ where: { id: '1' } })).toBeNotFound(); diff --git a/tests/integration/tests/enhancements/with-policy/unique-as-id.test.ts b/tests/integration/tests/enhancements/with-policy/unique-as-id.test.ts index e4d399204..a7ec74fa5 100644 --- a/tests/integration/tests/enhancements/with-policy/unique-as-id.test.ts +++ b/tests/integration/tests/enhancements/with-policy/unique-as-id.test.ts @@ -13,7 +13,7 @@ describe('With Policy: unique as id', () => { }); it('unique fields', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model A { x String @unique @@ -38,7 +38,7 @@ describe('With Policy: unique as id', () => { ` ); - const db = withPolicy(); + const db = enhance(); await expect(db.a.create({ data: { x: '1', y: 1, value: 0 } })).toBeRejectedByPolicy(); await expect(db.a.create({ data: { x: '1', y: 2, value: 1 } })).toResolveTruthy(); @@ -64,7 +64,7 @@ describe('With Policy: unique as id', () => { }); it('unique fields mixed with id', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model A { id Int @id @default(autoincrement()) @@ -91,7 +91,7 @@ describe('With Policy: unique as id', () => { ` ); - const db = withPolicy(); + const db = enhance(); await expect(db.a.create({ data: { x: '1', y: 1, value: 0 } })).toBeRejectedByPolicy(); await expect(db.a.create({ data: { x: '1', y: 2, value: 1 } })).toResolveTruthy(); @@ -117,7 +117,7 @@ describe('With Policy: unique as id', () => { }); it('model-level unique fields', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model A { x String @@ -147,7 +147,7 @@ describe('With Policy: unique as id', () => { ` ); - const db = withPolicy(); + const db = enhance(); await expect(db.a.create({ data: { x: '1', y: 1, value: 0 } })).toBeRejectedByPolicy(); await expect(db.a.create({ data: { x: '1', y: 2, value: 1 } })).toResolveTruthy(); diff --git a/tests/integration/tests/enhancements/with-policy/view.test.ts b/tests/integration/tests/enhancements/with-policy/view.test.ts index f5abe6439..3c541d2b0 100644 --- a/tests/integration/tests/enhancements/with-policy/view.test.ts +++ b/tests/integration/tests/enhancements/with-policy/view.test.ts @@ -13,7 +13,7 @@ describe('View Policy Test', () => { }); it('view policy', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` datasource db { provider = "sqlite" @@ -91,7 +91,7 @@ describe('View Policy Test', () => { }, }); - const db = withPolicy(); + const db = enhance(); await expect(prisma.userInfo.findMany()).resolves.toHaveLength(2); await expect(db.userInfo.findMany()).resolves.toHaveLength(1); diff --git a/tests/integration/tests/misc/stacktrace.test.ts b/tests/integration/tests/misc/stacktrace.test.ts index 6573ed088..08454d529 100644 --- a/tests/integration/tests/misc/stacktrace.test.ts +++ b/tests/integration/tests/misc/stacktrace.test.ts @@ -13,7 +13,7 @@ describe('Stack trace tests', () => { }); it('stack trace', async () => { - const { withPolicy } = await loadSchema( + const { enhance } = await loadSchema( ` model Model { id String @id @default(uuid()) @@ -21,7 +21,7 @@ describe('Stack trace tests', () => { ` ); - const db = withPolicy(); + const db = enhance(); let error: Error | undefined = undefined; try { diff --git a/tests/integration/tests/regression/issue-665.test.ts b/tests/integration/tests/regression/issue-665.test.ts index 8bd9f717b..b6552fd2b 100644 --- a/tests/integration/tests/regression/issue-665.test.ts +++ b/tests/integration/tests/regression/issue-665.test.ts @@ -2,7 +2,7 @@ import { loadSchema } from '@zenstackhq/testtools'; describe('Regression: issue 665', () => { it('regression', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model User { id Int @id @default(autoincrement()) @@ -20,19 +20,19 @@ describe('Regression: issue 665', () => { await prisma.user.create({ data: { id: 1, username: 'test', password: 'test', admin: true } }); // admin - let r = await withPolicy({ id: 1, admin: true }).user.findFirst(); + let r = await enhance({ id: 1, admin: true }).user.findFirst(); expect(r.username).toEqual('test'); // owner - r = await withPolicy({ id: 1 }).user.findFirst(); + r = await enhance({ id: 1 }).user.findFirst(); expect(r.username).toEqual('test'); // anonymous - r = await withPolicy().user.findFirst(); + r = await enhance().user.findFirst(); expect(r.username).toBeUndefined(); // non-owner - r = await withPolicy({ id: 2 }).user.findFirst(); + r = await enhance({ id: 2 }).user.findFirst(); expect(r.username).toBeUndefined(); }); }); diff --git a/tests/integration/tests/regression/issues.test.ts b/tests/integration/tests/regression/issues.test.ts index 8353f8bad..4ade85c8c 100644 --- a/tests/integration/tests/regression/issues.test.ts +++ b/tests/integration/tests/regression/issues.test.ts @@ -13,7 +13,7 @@ describe('GitHub issues regression', () => { }); it('issue 389', async () => { - const { withPolicy } = await loadSchema(` + const { enhance } = await loadSchema(` model model { id String @id @default(uuid()) value Int @@ -21,7 +21,7 @@ describe('GitHub issues regression', () => { @@allow('create', value > 0) } `); - const db = withPolicy(); + const db = enhance(); await expect(db.model.create({ data: { value: 0 } })).toBeRejectedByPolicy(); await expect(db.model.create({ data: { value: 1 } })).toResolveTruthy(); }); @@ -88,7 +88,7 @@ describe('GitHub issues regression', () => { }); it('select with _count', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model User { id String @id @unique @default(uuid()) @@ -117,7 +117,7 @@ describe('GitHub issues regression', () => { }, }); - const db = withPolicy(); + const db = enhance(); const r = await db.user.findFirst({ select: { _count: { select: { posts: true } } } }); expect(r).toMatchObject({ _count: { posts: 2 } }); }); @@ -150,7 +150,7 @@ describe('GitHub issues regression', () => { }); it('issue 552', async () => { - const { withPolicy, prisma } = await loadSchema( + const { enhance, prisma } = await loadSchema( ` model Tenant { id Int @id @default(autoincrement()) @@ -240,7 +240,7 @@ describe('GitHub issues regression', () => { }, }); - const db = withPolicy({ id: 1, is_super_admin: true }); + const db = enhance({ id: 1, is_super_admin: true }); await db.userTenant.update({ where: { user_id_tenant_id: { @@ -259,7 +259,7 @@ describe('GitHub issues regression', () => { }); it('issue 609', async () => { - const { withPolicy, prisma } = await loadSchema( + const { enhance, prisma } = await loadSchema( ` model User { id String @id @default(cuid()) @@ -300,7 +300,7 @@ describe('GitHub issues regression', () => { }); // connecting a child comment from a different user to a parent comment should succeed - const db = withPolicy({ id: '2' }); + const db = enhance({ id: '2' }); await expect( db.comment.create({ data: { @@ -313,7 +313,7 @@ describe('GitHub issues regression', () => { }); it('issue 624', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model User { id String @id @default(uuid()) @@ -476,7 +476,7 @@ model Group { console.log(`Created user with id: ${user.id}`); } - const db = withPolicy({ id: 'robin@prisma.io' }); + const db = enhance({ id: 'robin@prisma.io' }); await expect( db.post.findMany({ where: {}, @@ -507,7 +507,7 @@ model Group { }); it('issue 627', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model User { id String @id @default(uuid()) @@ -541,7 +541,7 @@ model Equipment extends BaseEntityWithTenant { }, }); - const db = withPolicy({ id: 'tenant-1' }); + const db = enhance({ id: 'tenant-1' }); await expect( db.equipment.create({ data: { @@ -586,7 +586,7 @@ model TwoEnumsOneModelTest { }); it('issue 634', async () => { - const { prisma, withPolicy } = await loadSchema( + const { prisma, enhance } = await loadSchema( ` model User { id String @id @default(uuid()) @@ -749,7 +749,7 @@ model Group { console.log(`Created user with id: ${user.id}`); } - const db = withPolicy({ id: 'robin@prisma.io' }); + const db = enhance({ id: 'robin@prisma.io' }); await expect( db.comment.findMany({ where: { From 53d6e91ecb1f24797b6c35c4f2cf723366262a63 Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Tue, 9 Jan 2024 23:46:23 +0800 Subject: [PATCH 2/4] fix issue with wrong detection of nested transactions --- packages/runtime/src/enhancements/proxy.ts | 25 ++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/packages/runtime/src/enhancements/proxy.ts b/packages/runtime/src/enhancements/proxy.ts index 5256ae226..b0a54d612 100644 --- a/packages/runtime/src/enhancements/proxy.ts +++ b/packages/runtime/src/enhancements/proxy.ts @@ -183,6 +183,10 @@ export function makeProxy( errorTransformer?: ErrorTransformer ) { const models = Object.keys(modelMeta.fields).map((k) => k.toLowerCase()); + + // a store for saving fields that belong to the proxy (not the target) + const proxyStorage: Record = {}; + const proxy = new Proxy(prisma, { get: (target: any, prop: string | symbol, receiver: any) => { // enhancer metadata @@ -194,6 +198,10 @@ export function makeProxy( return () => `$zenstack_${name}[${target.toString()}]`; } + if (typeof prop === 'string' && prop in proxyStorage) { + return proxyStorage[prop]; + } + if (prop === '$transaction') { // for interactive transactions, we need to proxy the transaction function so that // when it runs the callback, it provides a proxy to the Prisma client wrapped with @@ -213,8 +221,14 @@ export function makeProxy( const txFunc = input; return $transaction.bind(target)((tx: any) => { + // create a proxy for the transaction function const txProxy = makeProxy(tx, modelMeta, makeHandler, name + '$tx'); + + // record in-transaction flag on the proxy (not the target) + // see logic in "set" handler below txProxy[PRISMA_TX_FLAG] = true; + + // call the transaction function with the proxy return txFunc(txProxy); }, ...rest); }; @@ -235,6 +249,17 @@ export function makeProxy( return createHandlerProxy(makeHandler(target, prop), propVal, errorTransformer); }, + + set: (target: any, prop: string | symbol, value: any) => { + if (prop === PRISMA_TX_FLAG) { + // set to the proxy store + proxyStorage[prop] = value; + } else { + // pass through to the original target + target[prop] = value; + } + return true; + }, }); return proxy; From 41528412615aacc5347aa10bca849207c421a0da Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Wed, 10 Jan 2024 10:07:13 +0800 Subject: [PATCH 3/4] fix transaction detection, make transaction options configurable --- packages/runtime/src/constants.ts | 5 -- packages/runtime/src/enhancements/enhance.ts | 35 +++++++++++-- .../src/enhancements/policy/handler.ts | 51 ++++++++----------- .../runtime/src/enhancements/policy/index.ts | 14 +---- .../src/enhancements/policy/policy-utils.ts | 23 ++++++--- packages/runtime/src/enhancements/proxy.ts | 30 ++--------- 6 files changed, 72 insertions(+), 86 deletions(-) diff --git a/packages/runtime/src/constants.ts b/packages/runtime/src/constants.ts index bd191924a..c381a5a88 100644 --- a/packages/runtime/src/constants.ts +++ b/packages/runtime/src/constants.ts @@ -53,11 +53,6 @@ export enum PrismaErrorCode { DEPEND_ON_RECORD_NOT_FOUND = 'P2025', } -/** - * Field name for storing in-transaction flag - */ -export const PRISMA_TX_FLAG = '$__zenstack_tx'; - /** * Field name for getting current enhancer */ diff --git a/packages/runtime/src/enhancements/enhance.ts b/packages/runtime/src/enhancements/enhance.ts index 39d7cc0d4..a82640905 100644 --- a/packages/runtime/src/enhancements/enhance.ts +++ b/packages/runtime/src/enhancements/enhance.ts @@ -17,6 +17,16 @@ export enum EnhancementKind { Policy = 'policy', } +/** + * Transaction isolation levels: https://www.prisma.io/docs/orm/prisma-client/queries/transactions#transaction-isolation-level + */ +export type TransactionIsolationLevel = + | 'ReadUncommitted' + | 'ReadCommitted' + | 'RepeatableRead' + | 'Snapshot' + | 'Serializable'; + /** * Options for {@link createEnhancement} */ @@ -41,11 +51,6 @@ export type EnhancementOptions = { */ logPrismaQuery?: boolean; - /** - * Hook for transforming errors before they are thrown to the caller. - */ - errorTransformer?: ErrorTransformer; - /** * The Node module that contains PrismaClient */ @@ -56,6 +61,26 @@ export type EnhancementOptions = { * The kinds of enhancements to apply. By default all enhancements are applied. */ kinds?: EnhancementKind[]; + + /** + * Hook for transforming errors before they are thrown to the caller. + */ + errorTransformer?: ErrorTransformer; + + /** + * The `maxWait` option passed to `prisma.$transaction()` call for transactions initiated by ZenStack. + */ + transactionMaxWait?: number; + + /** + * The `timeout` option passed to `prisma.$transaction()` call for transactions initiated by ZenStack. + */ + transactionTimeout?: number; + + /** + * The `isolationLevel` option passed to `prisma.$transaction()` call for transactions initiated by ZenStack. + */ + transactionIsolationLevel?: TransactionIsolationLevel; }; /** diff --git a/packages/runtime/src/enhancements/policy/handler.ts b/packages/runtime/src/enhancements/policy/handler.ts index 748877805..bc8214698 100644 --- a/packages/runtime/src/enhancements/policy/handler.ts +++ b/packages/runtime/src/enhancements/policy/handler.ts @@ -4,7 +4,7 @@ import { lowerCaseFirst } from 'lower-case-first'; import invariant from 'tiny-invariant'; import { upperCaseFirst } from 'upper-case-first'; import { fromZodError } from 'zod-validation-error'; -import { CrudFailureReason, PRISMA_TX_FLAG } from '../../constants'; +import { CrudFailureReason } from '../../constants'; import { ModelDataVisitor, NestedWriteVisitor, @@ -16,9 +16,9 @@ import { type FieldInfo, type ModelMeta, } from '../../cross'; -import { AuthUser, DbClientContract, DbOperations, PolicyOperationKind } from '../../types'; +import { DbClientContract, DbOperations, PolicyOperationKind } from '../../types'; +import type { EnhancementContext, EnhancementOptions } from '../enhance'; import { PrismaProxyHandler } from '../proxy'; -import type { PolicyDef, ZodSchemas } from '../types'; import { formatObject, prismaClientValidationError } from '../utils'; import { Logger } from './logger'; import { PolicyUtil } from './policy-utils'; @@ -41,28 +41,22 @@ export class PolicyProxyHandler implements Pr private readonly logger: Logger; private readonly utils: PolicyUtil; private readonly model: string; + private readonly modelMeta: ModelMeta; + private readonly prismaModule: any; + private readonly logPrismaQuery?: boolean; constructor( private readonly prisma: DbClient, - private readonly policy: PolicyDef, - private readonly modelMeta: ModelMeta, - private readonly zodSchemas: ZodSchemas | undefined, - private readonly prismaModule: any, model: string, - private readonly user?: AuthUser, - private readonly logPrismaQuery?: boolean + private readonly options: EnhancementOptions, + private readonly context?: EnhancementContext ) { this.logger = new Logger(prisma); - this.utils = new PolicyUtil( - this.prisma, - this.modelMeta, - this.policy, - this.zodSchemas, - this.prismaModule, - this.user, - this.shouldLogQuery - ); this.model = lowerCaseFirst(model); + + ({ modelMeta: this.modelMeta, logPrismaQuery: this.logPrismaQuery, prismaModule: this.prismaModule } = options); + + this.utils = new PolicyUtil(prisma, options, context, this.shouldLogQuery); } private get modelClient() { @@ -1278,11 +1272,15 @@ export class PolicyProxyHandler implements Pr } private transaction(action: (tx: Record) => Promise) { - if (this.prisma[PRISMA_TX_FLAG]) { + if (this.prisma['$transaction']) { + return this.prisma.$transaction((tx) => action(tx), { + maxWait: this.options.transactionMaxWait, + timeout: this.options.transactionTimeout, + isolationLevel: this.options.transactionIsolationLevel, + }); + } else { // already in transaction, don't nest return action(this.prisma); - } else { - return this.prisma.$transaction((tx) => action(tx), { maxWait: 100000, timeout: 100000 }); } } @@ -1295,16 +1293,7 @@ export class PolicyProxyHandler implements Pr } private makeHandler(model: string) { - return new PolicyProxyHandler( - this.prisma, - this.policy, - this.modelMeta, - this.zodSchemas, - this.prismaModule, - model, - this.user, - this.logPrismaQuery - ); + return new PolicyProxyHandler(this.prisma, model, this.options, this.context); } private requireBackLink(fieldInfo: FieldInfo) { diff --git a/packages/runtime/src/enhancements/policy/index.ts b/packages/runtime/src/enhancements/policy/index.ts index fd52ba051..439f48933 100644 --- a/packages/runtime/src/enhancements/policy/index.ts +++ b/packages/runtime/src/enhancements/policy/index.ts @@ -22,7 +22,7 @@ export function withPolicy( options: EnhancementOptions, context?: EnhancementContext ): DbClient { - const { modelMeta, policy, zodSchemas, prismaModule, logPrismaQuery } = options; + const { modelMeta, policy } = options; // validate user context const userContext = context?.user; @@ -53,17 +53,7 @@ export function withPolicy( return makeProxy( prisma, modelMeta, - (_prisma, model) => - new PolicyProxyHandler( - _prisma as DbClientContract, - policy, - modelMeta, - zodSchemas, - prismaModule, - model, - context?.user, - logPrismaQuery - ), + (_prisma, model) => new PolicyProxyHandler(_prisma as DbClientContract, model, options, context), 'policy', options?.errorTransformer ); diff --git a/packages/runtime/src/enhancements/policy/policy-utils.ts b/packages/runtime/src/enhancements/policy/policy-utils.ts index 7b1597ccd..4ebd8de3a 100644 --- a/packages/runtime/src/enhancements/policy/policy-utils.ts +++ b/packages/runtime/src/enhancements/policy/policy-utils.ts @@ -29,6 +29,7 @@ import { } from '../../cross'; import { AuthUser, DbClientContract, DbOperations, PolicyOperationKind } from '../../types'; import { getVersion } from '../../version'; +import type { EnhancementContext, EnhancementOptions } from '../enhance'; import type { InputCheckFunc, PolicyDef, ReadFieldCheckFunc, ZodSchemas } from '../types'; import { formatObject, @@ -42,20 +43,28 @@ import { Logger } from './logger'; * Access policy enforcement utilities */ export class PolicyUtil { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore private readonly logger: Logger; + private readonly modelMeta: ModelMeta; + private readonly policy: PolicyDef; + private readonly zodSchemas?: ZodSchemas; + private readonly prismaModule: any; + private readonly user?: AuthUser; constructor( private readonly db: DbClientContract, - private readonly modelMeta: ModelMeta, - private readonly policy: PolicyDef, - private readonly zodSchemas: ZodSchemas | undefined, - private readonly prismaModule: any, - private readonly user?: AuthUser, + options: EnhancementOptions, + context?: EnhancementContext, private readonly shouldLogQuery = false ) { this.logger = new Logger(db); + this.user = context?.user; + + ({ + modelMeta: this.modelMeta, + policy: this.policy, + zodSchemas: this.zodSchemas, + prismaModule: this.prismaModule, + } = options); } //#region Logical operators diff --git a/packages/runtime/src/enhancements/proxy.ts b/packages/runtime/src/enhancements/proxy.ts index b0a54d612..c735d595a 100644 --- a/packages/runtime/src/enhancements/proxy.ts +++ b/packages/runtime/src/enhancements/proxy.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { PRISMA_PROXY_ENHANCER, PRISMA_TX_FLAG } from '../constants'; +import { PRISMA_PROXY_ENHANCER } from '../constants'; import type { ModelMeta } from '../cross'; import type { DbClientContract } from '../types'; import { createDeferredPromise } from './policy/promise'; @@ -184,9 +184,6 @@ export function makeProxy( ) { const models = Object.keys(modelMeta.fields).map((k) => k.toLowerCase()); - // a store for saving fields that belong to the proxy (not the target) - const proxyStorage: Record = {}; - const proxy = new Proxy(prisma, { get: (target: any, prop: string | symbol, receiver: any) => { // enhancer metadata @@ -195,11 +192,7 @@ export function makeProxy( } if (prop === 'toString') { - return () => `$zenstack_${name}[${target.toString()}]`; - } - - if (typeof prop === 'string' && prop in proxyStorage) { - return proxyStorage[prop]; + return () => `$zenstack_prisma_${prisma._clientVersion}`; } if (prop === '$transaction') { @@ -224,10 +217,6 @@ export function makeProxy( // create a proxy for the transaction function const txProxy = makeProxy(tx, modelMeta, makeHandler, name + '$tx'); - // record in-transaction flag on the proxy (not the target) - // see logic in "set" handler below - txProxy[PRISMA_TX_FLAG] = true; - // call the transaction function with the proxy return txFunc(txProxy); }, ...rest); @@ -249,17 +238,6 @@ export function makeProxy( return createHandlerProxy(makeHandler(target, prop), propVal, errorTransformer); }, - - set: (target: any, prop: string | symbol, value: any) => { - if (prop === PRISMA_TX_FLAG) { - // set to the proxy store - proxyStorage[prop] = value; - } else { - // pass through to the original target - target[prop] = value; - } - return true; - }, }); return proxy; @@ -283,11 +261,11 @@ function createHandlerProxy( // eslint-disable-next-line @typescript-eslint/ban-types const origMethod = prop as Function; return function (...args: any[]) { - // using proxy with async functions messes up error stack trace, + // using proxy with async functions results in messed-up error stack trace, // create an error to capture the current stack const capture = new Error(ERROR_MARKER); - // the original promise returned by the PrismaClient proxy + // the original proxy returned by the PrismaClient proxy const promise: Promise = origMethod.apply(handler, args); // modify the error stack From 6c01b3cfa69573349b6256c9aa7777e403a9003a Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Wed, 10 Jan 2024 10:39:05 +0800 Subject: [PATCH 4/4] increase transaction timeout in tests --- packages/testtools/src/schema.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/testtools/src/schema.ts b/packages/testtools/src/schema.ts index 9b6c16e11..2b501abc0 100644 --- a/packages/testtools/src/schema.ts +++ b/packages/testtools/src/schema.ts @@ -278,7 +278,14 @@ export async function loadSchema(schema: string, options?: SchemaLoadOptions) { enhance( prisma, { user }, - { policy, modelMeta, zodSchemas, logPrismaQuery: opt.logPrismaQuery, ...options } + { + policy, + modelMeta, + zodSchemas, + logPrismaQuery: opt.logPrismaQuery, + transactionTimeout: 10000, + ...options, + } ), enhanceRaw: enhance, policy,