Skip to content

Commit

Permalink
fix: improve generated typing for polymorphic models (#1002)
Browse files Browse the repository at this point in the history
  • Loading branch information
ymc9 authored Feb 14, 2024
1 parent c1da257 commit 7b453f7
Show file tree
Hide file tree
Showing 15 changed files with 123 additions and 60 deletions.
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
"test-ci": "ZENSTACK_TEST=1 pnpm -r run test --silent --forceExit",
"publish-all": "pnpm --filter \"./packages/**\" -r publish --access public",
"publish-preview": "pnpm --filter \"./packages/**\" -r publish --force --registry https://preview.registry.zenstack.dev/",
"unpublish-preview": "pnpm --recursive --shell-mode exec -- npm unpublish -f --registry https://preview.registry.zenstack.dev/ \"\\$PNPM_PACKAGE_NAME\""
"unpublish-preview": "pnpm --recursive --shell-mode exec -- npm unpublish -f --registry https://preview.registry.zenstack.dev/ \"\\$PNPM_PACKAGE_NAME\"",
"publish-next": "pnpm --filter \"./packages/**\" -r publish --access public --tag next",
"publish-preview-next": "pnpm --filter \"./packages/**\" -r publish --force --registry https://preview.registry.zenstack.dev/ --tag next",
"unpublish-preview-next": "pnpm --recursive --shell-mode exec -- npm unpublish -f --registry https://preview.registry.zenstack.dev/ --tag next \"\\$PNPM_PACKAGE_NAME\""
},
"keywords": [],
"author": "",
Expand Down
56 changes: 31 additions & 25 deletions packages/runtime/src/enhancements/create-enhancement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,60 +32,64 @@ export type TransactionIsolationLevel =
| 'Snapshot'
| 'Serializable';

/**
* Options for {@link createEnhancement}
*/
export type EnhancementOptions = {
/**
* Policy definition
* The kinds of enhancements to apply. By default all enhancements are applied.
*/
policy: PolicyDef;
kinds?: EnhancementKind[];

/**
* Model metadata
* Whether to log Prisma query
*/
modelMeta: ModelMeta;
logPrismaQuery?: boolean;

/**
* Zod schemas for validation
* Hook for transforming errors before they are thrown to the caller.
*/
zodSchemas?: ZodSchemas;
errorTransformer?: ErrorTransformer;

/**
* Whether to log Prisma query
* The `maxWait` option passed to `prisma.$transaction()` call for transactions initiated by ZenStack.
*/
logPrismaQuery?: boolean;
transactionMaxWait?: number;

/**
* The Node module that contains PrismaClient
* The `timeout` option passed to `prisma.$transaction()` call for transactions initiated by ZenStack.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
prismaModule: any;
transactionTimeout?: number;

/**
* The kinds of enhancements to apply. By default all enhancements are applied.
* The `isolationLevel` option passed to `prisma.$transaction()` call for transactions initiated by ZenStack.
*/
kinds?: EnhancementKind[];
transactionIsolationLevel?: TransactionIsolationLevel;
};

/**
* Options for {@link createEnhancement}
*
* @private
*/
export type InternalEnhancementOptions = EnhancementOptions & {
/**
* Hook for transforming errors before they are thrown to the caller.
* Policy definition
*/
errorTransformer?: ErrorTransformer;
policy: PolicyDef;

/**
* The `maxWait` option passed to `prisma.$transaction()` call for transactions initiated by ZenStack.
* Model metadata
*/
transactionMaxWait?: number;
modelMeta: ModelMeta;

/**
* The `timeout` option passed to `prisma.$transaction()` call for transactions initiated by ZenStack.
* Zod schemas for validation
*/
transactionTimeout?: number;
zodSchemas?: ZodSchemas;

/**
* The `isolationLevel` option passed to `prisma.$transaction()` call for transactions initiated by ZenStack.
* The Node module that contains PrismaClient
*/
transactionIsolationLevel?: TransactionIsolationLevel;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
prismaModule: any;
};

/**
Expand All @@ -103,13 +107,15 @@ let hasDefaultAuth: boolean | undefined = undefined;
* Gets a Prisma client enhanced with all enhancement behaviors, including access
* policy, field validation, field omission and password hashing.
*
* @private
*
* @param prisma The Prisma client to enhance.
* @param context Context.
* @param options Options.
*/
export function createEnhancement<DbClient extends object>(
prisma: DbClient,
options: EnhancementOptions,
options: InternalEnhancementOptions,
context?: EnhancementContext
) {
if (!prisma) {
Expand Down
6 changes: 3 additions & 3 deletions packages/runtime/src/enhancements/default-auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import deepcopy from 'deepcopy';
import { FieldInfo, NestedWriteVisitor, PrismaWriteActionType, enumerate, getFields } from '../cross';
import { DbClientContract } from '../types';
import { EnhancementContext, EnhancementOptions } from './create-enhancement';
import { EnhancementContext, InternalEnhancementOptions } from './create-enhancement';
import { DefaultPrismaProxyHandler, PrismaProxyActions, makeProxy } from './proxy';

/**
Expand All @@ -14,7 +14,7 @@ import { DefaultPrismaProxyHandler, PrismaProxyActions, makeProxy } from './prox
*/
export function withDefaultAuth<DbClient extends object>(
prisma: DbClient,
options: EnhancementOptions,
options: InternalEnhancementOptions,
context?: EnhancementContext
): DbClient {
return makeProxy(
Expand All @@ -31,7 +31,7 @@ class DefaultAuthHandler extends DefaultPrismaProxyHandler {
constructor(
prisma: DbClientContract,
model: string,
options: EnhancementOptions,
options: InternalEnhancementOptions,
private readonly context?: EnhancementContext
) {
super(prisma, model, options);
Expand Down
6 changes: 3 additions & 3 deletions packages/runtime/src/enhancements/delegate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ import {
resolveField,
} from '../cross';
import type { CrudContract, DbClientContract } from '../types';
import type { EnhancementOptions } from './create-enhancement';
import type { InternalEnhancementOptions } from './create-enhancement';
import { Logger } from './logger';
import { DefaultPrismaProxyHandler, makeProxy } from './proxy';
import { QueryUtils } from './query-utils';
import { formatObject, prismaClientValidationError } from './utils';

export function withDelegate<DbClient extends object>(prisma: DbClient, options: EnhancementOptions): DbClient {
export function withDelegate<DbClient extends object>(prisma: DbClient, options: InternalEnhancementOptions): DbClient {
return makeProxy(
prisma,
options.modelMeta,
Expand All @@ -34,7 +34,7 @@ export class DelegateProxyHandler extends DefaultPrismaProxyHandler {
private readonly logger: Logger;
private readonly queryUtils: QueryUtils;

constructor(prisma: DbClientContract, model: string, options: EnhancementOptions) {
constructor(prisma: DbClientContract, model: string, options: InternalEnhancementOptions) {
super(prisma, model, options);
this.logger = new Logger(prisma);
this.queryUtils = new QueryUtils(prisma, this.options);
Expand Down
8 changes: 4 additions & 4 deletions packages/runtime/src/enhancements/omit.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable @typescript-eslint/no-explicit-any */

import { enumerate, getModelFields, resolveField, type ModelMeta } from '../cross';
import { enumerate, getModelFields, resolveField } from '../cross';
import { DbClientContract } from '../types';
import { EnhancementOptions } from './create-enhancement';
import { InternalEnhancementOptions } from './create-enhancement';
import { DefaultPrismaProxyHandler, makeProxy } from './proxy';

/**
* Gets an enhanced Prisma client that supports `@omit` attribute.
*
* @private
*/
export function withOmit<DbClient extends object>(prisma: DbClient, options: EnhancementOptions): DbClient {
export function withOmit<DbClient extends object>(prisma: DbClient, options: InternalEnhancementOptions): DbClient {
return makeProxy(
prisma,
options.modelMeta,
Expand All @@ -21,7 +21,7 @@ export function withOmit<DbClient extends object>(prisma: DbClient, options: Enh
}

class OmitHandler extends DefaultPrismaProxyHandler {
constructor(prisma: DbClientContract, model: string, options: EnhancementOptions) {
constructor(prisma: DbClientContract, model: string, options: InternalEnhancementOptions) {
super(prisma, model, options);
}

Expand Down
11 changes: 7 additions & 4 deletions packages/runtime/src/enhancements/password.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,20 @@

import { hash } from 'bcryptjs';
import { DEFAULT_PASSWORD_SALT_LENGTH } from '../constants';
import { NestedWriteVisitor, type ModelMeta, type PrismaWriteActionType } from '../cross';
import { NestedWriteVisitor, type PrismaWriteActionType } from '../cross';
import { DbClientContract } from '../types';
import { EnhancementOptions } from './create-enhancement';
import { InternalEnhancementOptions } from './create-enhancement';
import { DefaultPrismaProxyHandler, PrismaProxyActions, makeProxy } from './proxy';

/**
* Gets an enhanced Prisma client that supports `@password` attribute.
*
* @private
*/
export function withPassword<DbClient extends object = any>(prisma: DbClient, options: EnhancementOptions): DbClient {
export function withPassword<DbClient extends object = any>(
prisma: DbClient,
options: InternalEnhancementOptions
): DbClient {
return makeProxy(
prisma,
options.modelMeta,
Expand All @@ -23,7 +26,7 @@ export function withPassword<DbClient extends object = any>(prisma: DbClient, op
}

class PasswordHandler extends DefaultPrismaProxyHandler {
constructor(prisma: DbClientContract, model: string, options: EnhancementOptions) {
constructor(prisma: DbClientContract, model: string, options: InternalEnhancementOptions) {
super(prisma, model, options);
}

Expand Down
6 changes: 3 additions & 3 deletions packages/runtime/src/enhancements/policy/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import {
type FieldInfo,
type ModelMeta,
} from '../../cross';
import { type CrudContract, type DbClientContract, PolicyOperationKind } from '../../types';
import type { EnhancementContext, EnhancementOptions } from '../create-enhancement';
import { PolicyOperationKind, type CrudContract, type DbClientContract } from '../../types';
import type { EnhancementContext, InternalEnhancementOptions } from '../create-enhancement';
import { Logger } from '../logger';
import { PrismaProxyHandler } from '../proxy';
import { QueryUtils } from '../query-utils';
Expand Down Expand Up @@ -49,7 +49,7 @@ export class PolicyProxyHandler<DbClient extends DbClientContract> implements Pr
constructor(
private readonly prisma: DbClient,
model: string,
private readonly options: EnhancementOptions,
private readonly options: InternalEnhancementOptions,
private readonly context?: EnhancementContext
) {
this.logger = new Logger(prisma);
Expand Down
4 changes: 2 additions & 2 deletions packages/runtime/src/enhancements/policy/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { getIdFields } from '../../cross';
import { DbClientContract } from '../../types';
import { hasAllFields } from '../../validation';
import type { EnhancementContext, EnhancementOptions } from '../create-enhancement';
import type { EnhancementContext, InternalEnhancementOptions } from '../create-enhancement';
import { makeProxy } from '../proxy';
import { PolicyProxyHandler } from './handler';

Expand All @@ -19,7 +19,7 @@ import { PolicyProxyHandler } from './handler';
*/
export function withPolicy<DbClient extends object>(
prisma: DbClient,
options: EnhancementOptions,
options: InternalEnhancementOptions,
context?: EnhancementContext
): DbClient {
const { modelMeta, policy } = options;
Expand Down
4 changes: 2 additions & 2 deletions packages/runtime/src/enhancements/policy/policy-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
import { enumerate, getFields, getModelFields, resolveField, zip, type FieldInfo, type ModelMeta } from '../../cross';
import { AuthUser, CrudContract, DbClientContract, PolicyOperationKind } from '../../types';
import { getVersion } from '../../version';
import type { EnhancementContext, EnhancementOptions } from '../create-enhancement';
import type { EnhancementContext, InternalEnhancementOptions } from '../create-enhancement';
import { Logger } from '../logger';
import { QueryUtils } from '../query-utils';
import type { InputCheckFunc, PolicyDef, ReadFieldCheckFunc, ZodSchemas } from '../types';
Expand All @@ -38,7 +38,7 @@ export class PolicyUtil extends QueryUtils {

constructor(
private readonly db: DbClientContract,
options: EnhancementOptions,
options: InternalEnhancementOptions,
context?: EnhancementContext,
private readonly shouldLogQuery = false
) {
Expand Down
13 changes: 7 additions & 6 deletions packages/runtime/src/enhancements/proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { PRISMA_PROXY_ENHANCER } from '../constants';
import type { ModelMeta } from '../cross';
import type { DbClientContract } from '../types';
import { EnhancementOptions } from './create-enhancement';
import { InternalEnhancementOptions } from './create-enhancement';
import { createDeferredPromise } from './policy/promise';

/**
Expand Down Expand Up @@ -67,7 +67,7 @@ export class DefaultPrismaProxyHandler implements PrismaProxyHandler {
constructor(
protected readonly prisma: DbClientContract,
protected readonly model: string,
protected readonly options: EnhancementOptions
protected readonly options: InternalEnhancementOptions
) {}

async findUnique(args: any): Promise<unknown> {
Expand Down Expand Up @@ -241,7 +241,7 @@ export function makeProxy<T extends PrismaProxyHandler>(
return propVal;
}

return createHandlerProxy(makeHandler(target, prop), propVal, errorTransformer);
return createHandlerProxy(makeHandler(target, prop), propVal, prop, errorTransformer);
},
});

Expand All @@ -252,6 +252,7 @@ export function makeProxy<T extends PrismaProxyHandler>(
function createHandlerProxy<T extends PrismaProxyHandler>(
handler: T,
origTarget: any,
model: string,
errorTransformer?: ErrorTransformer
): T {
return new Proxy(handler, {
Expand Down Expand Up @@ -282,7 +283,7 @@ function createHandlerProxy<T extends PrismaProxyHandler>(
if (capture.stack && err instanceof Error) {
// save the original stack and replace it with a clean one
(err as any).internalStack = err.stack;
err.stack = cleanCallStack(capture.stack, propKey.toString(), err.message);
err.stack = cleanCallStack(capture.stack, model, propKey.toString(), err.message);
}

if (errorTransformer) {
Expand All @@ -308,9 +309,9 @@ function createHandlerProxy<T extends PrismaProxyHandler>(
}

// Filter out @zenstackhq/runtime stack (generated by proxy) from stack trace
function cleanCallStack(stack: string, method: string, message: string) {
function cleanCallStack(stack: string, model: string, method: string, message: string) {
// message line
let resultStack = `Error calling enhanced Prisma method \`${method}\`: ${message}`;
let resultStack = `Error calling enhanced Prisma method \`${model}.${method}\`: ${message}`;

const lines = stack.split('\n');
let foundMarker = false;
Expand Down
4 changes: 2 additions & 2 deletions packages/runtime/src/enhancements/query-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import {
} from '../cross';
import { CrudContract, DbClientContract } from '../types';
import { getVersion } from '../version';
import { EnhancementOptions } from './create-enhancement';
import { InternalEnhancementOptions } from './create-enhancement';
import { prismaClientUnknownRequestError, prismaClientValidationError } from './utils';

export class QueryUtils {
constructor(private readonly prisma: DbClientContract, private readonly options: EnhancementOptions) {}
constructor(private readonly prisma: DbClientContract, private readonly options: InternalEnhancementOptions) {}

getIdFields(model: string) {
return getIdFields(this.options.modelMeta, model, true);
Expand Down
Loading

0 comments on commit 7b453f7

Please sign in to comment.