diff --git a/packages/next/tests/request-handler.test.ts b/packages/next/tests/request-handler.test.ts index 7daf57a6a..fb56fecc7 100644 --- a/packages/next/tests/request-handler.test.ts +++ b/packages/next/tests/request-handler.test.ts @@ -1,6 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { loadSchema } from '@zenstackhq/testtools'; -import fs from 'fs'; import { createServer, RequestListener } from 'http'; import { apiResolver } from 'next/dist/server/api-utils/node'; import superjson from 'superjson'; @@ -37,7 +36,6 @@ function makeTestClient(apiPath: string, options: RequestHandlerOptions, queryAr describe('request handler tests', () => { let origDir: string; - let projDir: string; beforeEach(() => { origDir = process.cwd(); @@ -45,9 +43,6 @@ describe('request handler tests', () => { afterEach(() => { process.chdir(origDir); - if (projDir) { - fs.rmSync(projDir, { recursive: true, force: true }); - } }); it('simple crud', async () => { @@ -58,8 +53,7 @@ model M { } `; - const { prisma, projectDir } = await loadSchema(model); - projDir = projectDir; + const { prisma } = await loadSchema(model); await makeTestClient('/m/create', { getPrisma: () => prisma }) .post('/') @@ -180,8 +174,7 @@ model M { } `; - const { withPresets, projectDir } = await loadSchema(model); - projDir = projectDir; + const { withPresets } = await loadSchema(model); await makeTestClient('/m/create', { getPrisma: () => withPresets() }) .post('/m/create') diff --git a/packages/plugins/react/src/generator/react-query.ts b/packages/plugins/react/src/generator/react-query.ts index 076e67511..ca2c379bd 100644 --- a/packages/plugins/react/src/generator/react-query.ts +++ b/packages/plugins/react/src/generator/react-query.ts @@ -1,5 +1,5 @@ import { DMMF } from '@prisma/generator-helper'; -import { getDataModels, PluginError, PluginOptions } from '@zenstackhq/sdk'; +import { PluginError, PluginOptions, createProject, getDataModels, saveProject } from '@zenstackhq/sdk'; import { DataModel, Model } from '@zenstackhq/sdk/ast'; import { camelCase, paramCase, pascalCase } from 'change-case'; import * as path from 'path'; @@ -16,7 +16,7 @@ export async function generate(model: Model, options: PluginOptions, dmmf: DMMF. outDir = path.join(path.dirname(options.schemaPath), outDir); } - const project = new Project(); + const project = createProject(); const warnings: string[] = []; const models = getDataModels(model); @@ -31,7 +31,7 @@ export async function generate(model: Model, options: PluginOptions, dmmf: DMMF. generateModelHooks(project, outDir, dataModel, mapping); }); - await project.save(); + await saveProject(project); return warnings; } @@ -319,12 +319,9 @@ function generateModelHooks(project: Project, outDir: string, model: DataModel, `T extends { select: any; } ? T['select'] extends true ? number : Prisma.GetScalarType : number` ); } - - sf.formatText(); } function generateIndex(project: Project, outDir: string, models: DataModel[]) { const sf = project.createSourceFile(path.join(outDir, 'index.ts'), undefined, { overwrite: true }); sf.addStatements(models.map((d) => `export * from './${paramCase(d.name)}';`)); - sf.formatText(); } diff --git a/packages/plugins/react/src/generator/swr.ts b/packages/plugins/react/src/generator/swr.ts index 9f2d93053..5ad536ae7 100644 --- a/packages/plugins/react/src/generator/swr.ts +++ b/packages/plugins/react/src/generator/swr.ts @@ -1,5 +1,12 @@ import { DMMF } from '@prisma/generator-helper'; -import { CrudFailureReason, getDataModels, PluginError, PluginOptions } from '@zenstackhq/sdk'; +import { + CrudFailureReason, + PluginError, + PluginOptions, + createProject, + getDataModels, + saveProject, +} from '@zenstackhq/sdk'; import { DataModel, Model } from '@zenstackhq/sdk/ast'; import { camelCase, paramCase } from 'change-case'; import * as path from 'path'; @@ -16,7 +23,7 @@ export async function generate(model: Model, options: PluginOptions, dmmf: DMMF. outDir = path.join(path.dirname(options.schemaPath), outDir); } - const project = new Project(); + const project = createProject(); const warnings: string[] = []; const models = getDataModels(model); @@ -31,7 +38,7 @@ export async function generate(model: Model, options: PluginOptions, dmmf: DMMF. generateModelHooks(project, outDir, dataModel, mapping); }); - await project.save(); + await saveProject(project); return warnings; } @@ -489,14 +496,9 @@ function generateModelHooks(project: Project, outDir: string, model: DataModel, } useFunc.addStatements([`return { ${methods.join(', ')} };`]); - - sf.formatText(); } function generateIndex(project: Project, outDir: string, models: DataModel[]) { const sf = project.createSourceFile(path.join(outDir, 'index.ts'), undefined, { overwrite: true }); - sf.addStatements(models.map((d) => `export * from './${paramCase(d.name)}';`)); - - sf.formatText(); } diff --git a/packages/plugins/react/tests/react-hooks.test.ts b/packages/plugins/react/tests/react-hooks.test.ts index abfa484a0..e27c454a1 100644 --- a/packages/plugins/react/tests/react-hooks.test.ts +++ b/packages/plugins/react/tests/react-hooks.test.ts @@ -1,11 +1,9 @@ /// import { loadSchema } from '@zenstackhq/testtools'; -import fs from 'fs'; describe('React Hooks Plugin Tests', () => { let origDir: string; - let projDir: string; beforeAll(() => { origDir = process.cwd(); @@ -13,9 +11,6 @@ describe('React Hooks Plugin Tests', () => { afterEach(() => { process.chdir(origDir); - if (projDir) { - fs.rmSync(projDir, { recursive: true, force: true }); - } }); const sharedModel = ` @@ -46,7 +41,7 @@ model Foo { `; it('swr generator', async () => { - const { projectDir } = await loadSchema( + await loadSchema( ` plugin react { provider = '${process.cwd()}/dist' @@ -60,11 +55,10 @@ ${sharedModel} [`${origDir}/dist`, 'react', '@types/react', 'swr'], true ); - projDir = projectDir; }); it('react-query generator', async () => { - const { projectDir } = await loadSchema( + await loadSchema( ` plugin react { provider = '${process.cwd()}/dist' @@ -79,6 +73,5 @@ ${sharedModel} [`${origDir}/dist`, 'react', '@types/react', '@tanstack/react-query'], true ); - projDir = projectDir; }); }); diff --git a/packages/plugins/trpc/src/generator.ts b/packages/plugins/trpc/src/generator.ts index 26f535a60..29bf6c743 100644 --- a/packages/plugins/trpc/src/generator.ts +++ b/packages/plugins/trpc/src/generator.ts @@ -1,5 +1,5 @@ import { DMMF } from '@prisma/generator-helper'; -import { CrudFailureReason, PluginError, PluginOptions, RUNTIME_PACKAGE } from '@zenstackhq/sdk'; +import { CrudFailureReason, PluginError, PluginOptions, RUNTIME_PACKAGE, saveProject } from '@zenstackhq/sdk'; import { Model } from '@zenstackhq/sdk/ast'; import { camelCase } from 'change-case'; import { promises as fs } from 'fs'; @@ -42,7 +42,7 @@ export async function generate(model: Model, options: PluginOptions, dmmf: DMMF. createAppRouter(outDir, modelOperations, hiddenModels); createHelper(outDir); - await project.save(); + await saveProject(project); } function createAppRouter(outDir: string, modelOperations: DMMF.ModelMapping[], hiddenModels: string[]) { diff --git a/packages/plugins/trpc/src/zod/generator.ts b/packages/plugins/trpc/src/zod/generator.ts index a1cd1018b..d480ac646 100644 --- a/packages/plugins/trpc/src/zod/generator.ts +++ b/packages/plugins/trpc/src/zod/generator.ts @@ -1,18 +1,21 @@ import { ConnectorType, DMMF } from '@prisma/generator-helper'; import { Dictionary } from '@prisma/internals'; -import { getLiteral, PluginOptions } from '@zenstackhq/sdk'; -import { DataSource, isDataSource, Model } from '@zenstackhq/sdk/ast'; +import { PluginOptions, getLiteral } from '@zenstackhq/sdk'; +import { DataSource, Model, isDataSource } from '@zenstackhq/sdk/ast'; import { - addMissingInputObjectTypes, AggregateOperationSupport, + addMissingInputObjectTypes, resolveAggregateOperationSupport, } from '@zenstackhq/sdk/dmmf-helpers'; import { promises as fs } from 'fs'; +import path from 'path'; import Transformer from './transformer'; import removeDir from './utils/removeDir'; +import { writeFileSafely } from './utils/writeFileSafely'; export async function generate(model: Model, options: PluginOptions, dmmf: DMMF.Document) { - await handleGeneratorOutputValue((options.output as string) ?? './generated'); + const output = (options.output as string) ?? './generated'; + await handleGeneratorOutputValue(output); const prismaClientDmmf = dmmf; @@ -38,7 +41,7 @@ export async function generate(model: Model, options: PluginOptions, dmmf: DMMF. const aggregateOperationSupport = resolveAggregateOperationSupport(inputObjectTypes); - await generateObjectSchemas(inputObjectTypes); + await generateObjectSchemas(inputObjectTypes, output); await generateModelSchemas(models, modelOperations, aggregateOperationSupport); } @@ -61,13 +64,19 @@ async function generateEnumSchemas(prismaSchemaEnum: DMMF.SchemaEnum[], modelSch await transformer.generateEnumSchemas(); } -async function generateObjectSchemas(inputObjectTypes: DMMF.InputType[]) { +async function generateObjectSchemas(inputObjectTypes: DMMF.InputType[], output: string) { + const moduleNames: string[] = []; for (let i = 0; i < inputObjectTypes.length; i += 1) { const fields = inputObjectTypes[i]?.fields; const name = inputObjectTypes[i]?.name; const transformer = new Transformer({ name, fields }); - await transformer.generateObjectSchema(); + const moduleName = await transformer.generateObjectSchema(); + moduleNames.push(moduleName); } + await writeFileSafely( + path.join(output, `schemas/objects/index.ts`), + moduleNames.map((name) => `export * from './${name}';`).join('\n') + ); } async function generateModelSchemas( diff --git a/packages/plugins/trpc/src/zod/transformer.ts b/packages/plugins/trpc/src/zod/transformer.ts index ca14fb2f4..58b39386c 100644 --- a/packages/plugins/trpc/src/zod/transformer.ts +++ b/packages/plugins/trpc/src/zod/transformer.ts @@ -59,6 +59,11 @@ export default class Transformer { )}` ); } + + await writeFileSafely( + path.join(Transformer.outputPath, `schemas/enums/index.ts`), + this.enumTypes.map((enumType) => `export * from './${enumType.name}.schema';`).join('\n') + ); } generateImportZodStatement() { @@ -77,6 +82,7 @@ export default class Transformer { path.join(Transformer.outputPath, `schemas/objects/${this.name}.schema.ts`), '/* eslint-disable */\n' + objectSchema ); + return `${this.name}.schema`; } generateObjectSchemaFields() { @@ -113,10 +119,11 @@ export default class Transformer { } else if (inputType.type === 'Boolean') { result.push(this.wrapWithZodValidators('z.boolean()', field, inputType)); } else if (inputType.type === 'DateTime') { - result.push(this.wrapWithZodValidators('z.date()', field, inputType)); + result.push(this.wrapWithZodValidators(['z.date()', 'z.string().datetime()'], field, inputType)); + } else if (inputType.type === 'Bytes') { + result.push(this.wrapWithZodValidators('z.number().array()', field, inputType)); } else if (inputType.type === 'Json') { this.hasJson = true; - result.push(this.wrapWithZodValidators('jsonSchema', field, inputType)); } else if (inputType.type === 'True') { result.push(this.wrapWithZodValidators('z.literal(true)', field, inputType)); @@ -158,19 +165,29 @@ export default class Transformer { } wrapWithZodValidators( - mainValidator: string, + mainValidators: string | string[], field: PrismaDMMF.SchemaArg, inputType: PrismaDMMF.SchemaArgInputType ) { let line = ''; - line = mainValidator; - if (inputType.isList) { - line += '.array()'; - } + const base = Array.isArray(mainValidators) ? mainValidators : [mainValidators]; + + line += base + .map((validator) => { + let r = validator; + if (inputType.isList) { + r += '.array()'; + } + if (!field.isRequired) { + r += '.optional()'; + } + return r; + }) + .join(', '); - if (!field.isRequired) { - line += '.optional()'; + if (base.length > 1) { + line = `z.union([${line}])`; } return line; @@ -356,8 +373,7 @@ export default class Transformer { } async generateModelSchemas() { - const globalImports: string[] = []; - let globalExport = ''; + const globalExports: string[] = []; for (const modelOperation of this.modelOperations) { const { @@ -381,8 +397,7 @@ export default class Transformer { // eslint-disable-next-line @typescript-eslint/no-explicit-any } = modelOperation; - globalImports.push(`import { ${modelName}Schema } from './${modelName}.schema'`); - globalExport += `${modelName}: ${modelName}Schema,`; + globalExports.push(`export { ${modelName}Schema as ${modelName} } from './${modelName}.schema'`); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const model = findModelByName(this.models, modelName)!; @@ -547,13 +562,7 @@ ${indentString(codeBody, 4)} path.join(Transformer.outputPath, 'schemas/index.ts'), ` /* eslint-disable */ -${globalImports.join(';\n')} - -const schemas = { -${indentString(globalExport, 4)} -}; - -export default schemas; +${globalExports.join(';\n')} ` ); } diff --git a/packages/plugins/trpc/tests/trpc.test.ts b/packages/plugins/trpc/tests/trpc.test.ts index 60170ab7e..2ca105c7e 100644 --- a/packages/plugins/trpc/tests/trpc.test.ts +++ b/packages/plugins/trpc/tests/trpc.test.ts @@ -1,11 +1,9 @@ /// import { loadSchema } from '@zenstackhq/testtools'; -import fs from 'fs'; describe('tRPC Plugin Tests', () => { let origDir: string; - let projDir: string; beforeAll(() => { origDir = process.cwd(); @@ -13,13 +11,10 @@ describe('tRPC Plugin Tests', () => { afterEach(() => { process.chdir(origDir); - if (projDir) { - fs.rmSync(projDir, { recursive: true, force: true }); - } }); it('run plugin', async () => { - const { projectDir } = await loadSchema( + await loadSchema( ` plugin trpc { provider = '${process.cwd()}/dist' @@ -56,6 +51,5 @@ model Foo { [`${origDir}/dist`, '@trpc/client', '@trpc/server'], true ); - projDir = projectDir; }); }); diff --git a/packages/schema/src/cli/config.ts b/packages/schema/src/cli/config.ts index 78cd14002..99aa66a2e 100644 --- a/packages/schema/src/cli/config.ts +++ b/packages/schema/src/cli/config.ts @@ -24,6 +24,7 @@ export function loadConfig(filename: string) { const fileData = fs.readFileSync(filename, `utf-8`); const content = JSON.parse(fileData); config = schema.parse(content); + // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (err: any) { if (err?.code === `ENOENT`) { throw new CliError(`Config file could not be found: ${filename}`); diff --git a/packages/schema/src/plugins/access-policy/policy-guard-generator.ts b/packages/schema/src/plugins/access-policy/policy-guard-generator.ts index 15549e964..7d04d1691 100644 --- a/packages/schema/src/plugins/access-policy/policy-guard-generator.ts +++ b/packages/schema/src/plugins/access-policy/policy-guard-generator.ts @@ -16,6 +16,8 @@ import { import type { PolicyKind, PolicyOperationKind } from '@zenstackhq/runtime'; import { analyzePolicies, + createProject, + emitProject, getDataModels, getLiteral, GUARD_FIELD_NAME, @@ -23,11 +25,12 @@ import { PluginOptions, resolved, RUNTIME_PACKAGE, + saveProject, } from '@zenstackhq/sdk'; import { camelCase } from 'change-case'; import { streamAllContents } from 'langium'; import path from 'path'; -import { FunctionDeclaration, Project, SourceFile, VariableDeclarationKind } from 'ts-morph'; +import { FunctionDeclaration, SourceFile, VariableDeclarationKind } from 'ts-morph'; import { name } from '.'; import { isFromStdlib } from '../../language-server/utils'; import { getIdFields } from '../../utils/ast-utils'; @@ -47,7 +50,7 @@ export default class PolicyGenerator { return; } - const project = new Project(); + const project = createProject(); const sf = project.createSourceFile(path.join(output, 'policy.ts'), undefined, { overwrite: true }); sf.addImportDeclaration({ @@ -55,11 +58,6 @@ export default class PolicyGenerator { moduleSpecifier: `${RUNTIME_PACKAGE}`, }); - sf.addImportDeclaration({ - namedImports: [{ name: 'z' }], - moduleSpecifier: 'zod', - }); - // import enums for (const e of model.declarations.filter((d) => isEnum(d))) { sf.addImportDeclaration({ @@ -77,6 +75,8 @@ export default class PolicyGenerator { const zodGenerator = new ZodSchemaGenerator(); + let fieldSchemaGenerated = false; + sf.addVariableStatement({ declarationKind: VariableDeclarationKind.Const, declarations: [ @@ -104,18 +104,33 @@ export default class PolicyGenerator { writer.writeLine(','); writer.write('schema:'); - zodGenerator.generate(writer, models); + if (zodGenerator.generate(writer, models)) { + fieldSchemaGenerated = true; + } }); }, }, ], }); + if (fieldSchemaGenerated) { + sf.addImportDeclaration({ + namedImports: [{ name: 'z' }], + moduleSpecifier: 'zod', + }); + } + sf.addStatements('export default policy'); - sf.formatText(); - await project.save(); - await project.emit(); + // emit if generated into standard location or compilation is forced + const shouldCompile = !options.output || options.compile === true; + if (!shouldCompile || options.preserveTsFiles === true) { + // save ts files + await saveProject(project); + } + if (shouldCompile) { + await emitProject(project); + } } private getPolicyExpressions(model: DataModel, kind: PolicyKind, operation: PolicyOperationKind) { diff --git a/packages/schema/src/plugins/access-policy/zod-schema-generator.ts b/packages/schema/src/plugins/access-policy/zod-schema-generator.ts index 96d45ecc2..a6f94a19b 100644 --- a/packages/schema/src/plugins/access-policy/zod-schema-generator.ts +++ b/packages/schema/src/plugins/access-policy/zod-schema-generator.ts @@ -8,6 +8,7 @@ import { CodeBlockWriter } from 'ts-morph'; */ export class ZodSchemaGenerator { generate(writer: CodeBlockWriter, models: DataModel[]) { + let generated = false; writer.inlineBlock(() => { models.forEach((model) => { const fields = model.fields.filter( @@ -22,6 +23,7 @@ export class ZodSchemaGenerator { return; } + generated = true; writer.write(`${camelCase(model.name)}: z.object(`); writer.inlineBlock(() => { fields.forEach((field) => { @@ -31,6 +33,7 @@ export class ZodSchemaGenerator { writer.writeLine(').partial(),'); }); }); + return generated; } private hasValidationAttributes(field: DataModelField) { diff --git a/packages/schema/src/plugins/model-meta/index.ts b/packages/schema/src/plugins/model-meta/index.ts index 3710b36be..271dcde64 100644 --- a/packages/schema/src/plugins/model-meta/index.ts +++ b/packages/schema/src/plugins/model-meta/index.ts @@ -8,10 +8,20 @@ import { ReferenceExpr, } from '@zenstackhq/language/ast'; import type { RuntimeAttribute } from '@zenstackhq/runtime'; -import { getAttributeArgs, getDataModels, getLiteral, hasAttribute, PluginOptions, resolved } from '@zenstackhq/sdk'; +import { + createProject, + emitProject, + getAttributeArgs, + getDataModels, + getLiteral, + hasAttribute, + PluginOptions, + resolved, + saveProject, +} from '@zenstackhq/sdk'; import { camelCase } from 'change-case'; import path from 'path'; -import { CodeBlockWriter, Project, VariableDeclarationKind } from 'ts-morph'; +import { CodeBlockWriter, VariableDeclarationKind } from 'ts-morph'; import { getIdFields } from '../../language-server/utils'; import { ensureNodeModuleFolder, getDefaultOutputFolder } from '../plugin-utils'; @@ -26,7 +36,7 @@ export default async function run(model: Model, options: PluginOptions) { const dataModels = getDataModels(model); - const project = new Project(); + const project = createProject(); if (!options.output) { ensureNodeModuleFolder(output); @@ -39,10 +49,15 @@ export default async function run(model: Model, options: PluginOptions) { }); sf.addStatements('export default metadata;'); - sf.formatText(); - - await project.save(); - await project.emit(); + // emit if generated into standard location or compilation is forced + const shouldCompile = !options.output || options.compile === true; + if (!shouldCompile || options.preserveTsFiles === true) { + // save ts files + await saveProject(project); + } + if (shouldCompile) { + await emitProject(project); + } } function generateModelMetadata(dataModels: DataModel[], writer: CodeBlockWriter) { diff --git a/packages/schema/src/plugins/zod/generator.ts b/packages/schema/src/plugins/zod/generator.ts index 50d135101..310f78918 100644 --- a/packages/schema/src/plugins/zod/generator.ts +++ b/packages/schema/src/plugins/zod/generator.ts @@ -1,10 +1,10 @@ import { ConnectorType, DMMF } from '@prisma/generator-helper'; import { Dictionary } from '@prisma/internals'; -import { emitProject, getLiteral, PluginOptions } from '@zenstackhq/sdk'; -import { DataSource, isDataSource, Model } from '@zenstackhq/sdk/ast'; +import { PluginOptions, createProject, emitProject, getLiteral, saveProject } from '@zenstackhq/sdk'; +import { DataSource, Model, isDataSource } from '@zenstackhq/sdk/ast'; import { - addMissingInputObjectTypes, AggregateOperationSupport, + addMissingInputObjectTypes, resolveAggregateOperationSupport, } from '@zenstackhq/sdk/dmmf-helpers'; import { promises as fs } from 'fs'; @@ -33,7 +33,7 @@ export async function generate(model: Model, options: PluginOptions, dmmf: DMMF. const outputObjectTypes = prismaClientDmmf.schema.outputObjectTypes.prisma; const models: DMMF.Model[] = prismaClientDmmf.datamodel.models; - const project = new Project(); + const project = createProject(); await generateEnumSchemas( prismaClientDmmf.schema.enumTypes.prisma, @@ -56,10 +56,18 @@ export async function generate(model: Model, options: PluginOptions, dmmf: DMMF. const aggregateOperationSupport = resolveAggregateOperationSupport(inputObjectTypes); - await generateObjectSchemas(inputObjectTypes, project); + await generateObjectSchemas(inputObjectTypes, project, output); await generateModelSchemas(models, modelOperations, aggregateOperationSupport, project); - await emitProject(project); + // emit if generated into standard location or compilation is forced + const shouldCompile = !options.output || options.compile === true; + if (!shouldCompile || options.preserveTsFiles === true) { + // save ts files + await saveProject(project); + } + if (shouldCompile) { + await emitProject(project); + } } async function handleGeneratorOutputValue(output: string) { @@ -86,13 +94,20 @@ async function generateEnumSchemas( await transformer.generateEnumSchemas(); } -async function generateObjectSchemas(inputObjectTypes: DMMF.InputType[], project: Project) { +async function generateObjectSchemas(inputObjectTypes: DMMF.InputType[], project: Project, output: string) { + const moduleNames: string[] = []; for (let i = 0; i < inputObjectTypes.length; i += 1) { const fields = inputObjectTypes[i]?.fields; const name = inputObjectTypes[i]?.name; const transformer = new Transformer({ name, fields, project }); - await transformer.generateObjectSchema(); + const moduleName = transformer.generateObjectSchema(); + moduleNames.push(moduleName); } + project.createSourceFile( + path.join(output, 'objects/index.ts'), + moduleNames.map((name) => `export * from './${name}';`).join('\n'), + { overwrite: true } + ); } async function generateModelSchemas( diff --git a/packages/schema/src/plugins/zod/transformer.ts b/packages/schema/src/plugins/zod/transformer.ts index 8b73fe825..087c7dc75 100644 --- a/packages/schema/src/plugins/zod/transformer.ts +++ b/packages/schema/src/plugins/zod/transformer.ts @@ -60,6 +60,11 @@ export default class Transformer { )}`; this.project.createSourceFile(filePath, content, { overwrite: true }); } + this.project.createSourceFile( + path.join(Transformer.outputPath, `enums/index.ts`), + this.enumTypes.map((enumType) => `export * from './${enumType.name}.schema';`).join('\n'), + { overwrite: true } + ); } generateImportZodStatement() { @@ -70,7 +75,7 @@ export default class Transformer { return `export const ${name}Schema = ${schema}`; } - async generateObjectSchema() { + generateObjectSchema() { const zodObjectSchemaFields = this.generateObjectSchemaFields(); const objectSchema = this.prepareObjectSchema(zodObjectSchemaFields); const objectSchemaName = this.resolveObjectSchemaName(); @@ -78,6 +83,7 @@ export default class Transformer { const filePath = path.join(Transformer.outputPath, `objects/${objectSchemaName}.schema.ts`); const content = '/* eslint-disable */\n' + objectSchema; this.project.createSourceFile(filePath, content, { overwrite: true }); + return `${objectSchemaName}.schema`; } generateObjectSchemaFields() { @@ -114,10 +120,11 @@ export default class Transformer { } else if (inputType.type === 'Boolean') { result.push(this.wrapWithZodValidators('z.boolean()', field, inputType)); } else if (inputType.type === 'DateTime') { - result.push(this.wrapWithZodValidators('z.date()', field, inputType)); + result.push(this.wrapWithZodValidators(['z.date()', 'z.string().datetime()'], field, inputType)); + } else if (inputType.type === 'Bytes') { + result.push(this.wrapWithZodValidators('z.number().array()', field, inputType)); } else if (inputType.type === 'Json') { this.hasJson = true; - result.push(this.wrapWithZodValidators('jsonSchema', field, inputType)); } else if (inputType.type === 'True') { result.push(this.wrapWithZodValidators('z.literal(true)', field, inputType)); @@ -159,19 +166,29 @@ export default class Transformer { } wrapWithZodValidators( - mainValidator: string, + mainValidators: string | string[], field: PrismaDMMF.SchemaArg, inputType: PrismaDMMF.SchemaArgInputType ) { let line = ''; - line = mainValidator; - if (inputType.isList) { - line += '.array()'; - } + const base = Array.isArray(mainValidators) ? mainValidators : [mainValidators]; + + line += base + .map((validator) => { + let r = validator; + if (inputType.isList) { + r += '.array()'; + } + if (!field.isRequired) { + r += '.optional()'; + } + return r; + }) + .join(', '); - if (!field.isRequired) { - line += '.optional()'; + if (base.length > 1) { + line = `z.union([${line}])`; } return line; @@ -361,8 +378,7 @@ export default class Transformer { } async generateModelSchemas() { - const globalImports: string[] = []; - let globalExport = ''; + const globalExports: string[] = []; for (const modelOperation of this.modelOperations) { const { @@ -386,8 +402,7 @@ export default class Transformer { // eslint-disable-next-line @typescript-eslint/no-explicit-any } = modelOperation; - globalImports.push(`import { ${modelName}Schema } from './${modelName}.schema'`); - globalExport += `${modelName}: ${modelName}Schema,`; + globalExports.push(`export { ${modelName}Schema as ${modelName} } from './${modelName}.schema'`); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const model = findModelByName(this.models, modelName)!; @@ -554,13 +569,7 @@ export default class Transformer { const indexFilePath = path.join(Transformer.outputPath, 'index.ts'); const indexContent = ` /* eslint-disable */ -${globalImports.join(';\n')} - -const schemas = { -${indentString(globalExport, 4)} -}; - -export default schemas; +${globalExports.join(';\n')} `; this.project.createSourceFile(indexFilePath, indexContent, { overwrite: true }); } diff --git a/packages/schema/tests/cli/command.test.ts b/packages/schema/tests/cli/command.test.ts index 96ae9e17a..9ef4de1d0 100644 --- a/packages/schema/tests/cli/command.test.ts +++ b/packages/schema/tests/cli/command.test.ts @@ -9,19 +9,16 @@ import { createProgram } from '../../src/cli'; import { execSync } from '../../src/utils/exec-utils'; describe('CLI Command Tests', () => { - let projDir: string; let origDir: string; beforeEach(() => { origDir = process.cwd(); - const r = tmp.dirSync(); - projDir = r.name; - console.log(`Project dir: ${projDir}`); - process.chdir(projDir); + const r = tmp.dirSync({ unsafeCleanup: true }); + console.log(`Project dir: ${r.name}`); + process.chdir(r.name); }); afterEach(() => { - fs.rmSync(projDir, { recursive: true, force: true }); process.chdir(origDir); }); diff --git a/packages/schema/tests/cli/config.test.ts b/packages/schema/tests/cli/config.test.ts index 3fa522f56..3a8b22b75 100644 --- a/packages/schema/tests/cli/config.test.ts +++ b/packages/schema/tests/cli/config.test.ts @@ -9,21 +9,18 @@ import { config } from '../../src/cli/config'; import { GUARD_FIELD_NAME, TRANSACTION_FIELD_NAME } from '@zenstackhq/sdk'; describe('CLI Config Tests', () => { - let projDir: string; let origDir: string; beforeEach(() => { origDir = process.cwd(); - const r = tmp.dirSync(); - projDir = r.name; - console.log(`Project dir: ${projDir}`); - process.chdir(projDir); + const r = tmp.dirSync({ unsafeCleanup: true }); + console.log(`Project dir: ${r.name}`); + process.chdir(r.name); fs.writeFileSync('package.json', JSON.stringify({ name: 'my app', version: '1.0.0' })); }); afterEach(() => { - fs.rmSync(projDir, { recursive: true, force: true }); process.chdir(origDir); }); diff --git a/packages/schema/tests/cli/plugins.test.ts b/packages/schema/tests/cli/plugins.test.ts index 31eb86557..4d1520f2e 100644 --- a/packages/schema/tests/cli/plugins.test.ts +++ b/packages/schema/tests/cli/plugins.test.ts @@ -8,19 +8,16 @@ import * as tmp from 'tmp'; import { createProgram } from '../../src/cli'; describe('CLI Plugins Tests', () => { - let projDir: string; let origDir: string; beforeEach(() => { origDir = process.cwd(); - const r = tmp.dirSync(); - projDir = r.name; - console.log(`Project dir: ${projDir}`); - process.chdir(projDir); + const r = tmp.dirSync({ unsafeCleanup: true }); + console.log(`Project dir: ${r.name}`); + process.chdir(r.name); }); afterEach(() => { - fs.rmSync(projDir, { recursive: true, force: true }); process.chdir(origDir); }); diff --git a/packages/schema/tests/generator/expression-writer.test.ts b/packages/schema/tests/generator/expression-writer.test.ts index 157d4916e..e3296a720 100644 --- a/packages/schema/tests/generator/expression-writer.test.ts +++ b/packages/schema/tests/generator/expression-writer.test.ts @@ -1189,7 +1189,6 @@ async function check(schema: string, getExpr: (model: DataModel) => Expression, }, ], }); - sf.formatText(); await project.save(); console.log('Source saved:', sourcePath); diff --git a/packages/schema/tests/schema/cal-com.zmodel b/packages/schema/tests/schema/cal-com.zmodel index c6e874304..15025f0ef 100644 --- a/packages/schema/tests/schema/cal-com.zmodel +++ b/packages/schema/tests/schema/cal-com.zmodel @@ -14,11 +14,15 @@ generator client { plugin meta { provider = '@core/model-meta' output = '.zenstack' + compile = true + preserveTsFiles = true } plugin policy { provider = '@core/access-policy' output = '.zenstack' + compile = true + preserveTsFiles = true } enum SchedulingType { diff --git a/packages/sdk/src/code-gen.ts b/packages/sdk/src/code-gen.ts index f58e6c9fd..02dd348b1 100644 --- a/packages/sdk/src/code-gen.ts +++ b/packages/sdk/src/code-gen.ts @@ -1,5 +1,6 @@ import prettier from 'prettier'; -import { Project, SourceFile } from 'ts-morph'; +import { CompilerOptions, DiagnosticCategory, ModuleKind, Project, ScriptTarget, SourceFile } from 'ts-morph'; +import { PluginError } from './types'; const formatOptions = { trailingComma: 'all', @@ -23,16 +24,55 @@ async function formatFile(sourceFile: SourceFile) { } } +/** + * Creates a TS code generation project + */ +export function createProject(options?: CompilerOptions) { + return new Project({ + compilerOptions: { + target: ScriptTarget.ES2016, + module: ModuleKind.CommonJS, + esModuleInterop: true, + declaration: true, + strict: true, + skipLibCheck: true, + noEmitOnError: true, + ...options, + }, + }); +} + +/** + * Persists a TS project to disk. + */ +export async function saveProject(project: Project) { + await Promise.all( + project.getSourceFiles().map(async (sf) => { + await formatFile(sf); + }) + ); + await project.save(); +} + /** * Emit a TS project to JS files. */ -export async function emitProject(project: Project, format = true) { - if (format) { - await Promise.all( - project.getSourceFiles().map(async (sf) => { - await formatFile(sf); - }) - ); +export async function emitProject(project: Project) { + const errors = project.getPreEmitDiagnostics().filter((d) => d.getCategory() === DiagnosticCategory.Error); + if (errors.length > 0) { + console.error('Error compiling generated code:'); + console.error(project.formatDiagnosticsWithColorAndContext(errors.slice(0, 10))); + await project.save(); + throw new PluginError(`Error compiling generated code`); + } + + const result = await project.emit(); + + const emitErrors = result.getDiagnostics().filter((d) => d.getCategory() === DiagnosticCategory.Error); + if (emitErrors.length > 0) { + console.error('Some generated code is not emitted:'); + console.error(project.formatDiagnosticsWithColorAndContext(emitErrors.slice(0, 10))); + await project.save(); + throw new PluginError(`Error emitting generated code`); } - await project.emit(); } diff --git a/packages/server/tests/open-api.test.ts b/packages/server/tests/open-api.test.ts index d651d68b8..487303477 100644 --- a/packages/server/tests/open-api.test.ts +++ b/packages/server/tests/open-api.test.ts @@ -139,7 +139,9 @@ describe('OpenAPI server tests', () => { prisma, }); expect(r.status).toBe(400); + expect((r.body as any).message).toContain('Argument where is missing'); + // with validation r = await handleRequest({ method: 'get', path: '/post/findUnique', diff --git a/packages/server/tests/utils.ts b/packages/server/tests/utils.ts index 48b00c7a5..34f7e48b6 100644 --- a/packages/server/tests/utils.ts +++ b/packages/server/tests/utils.ts @@ -1,12 +1,16 @@ export const schema = ` model User { id String @id @default(cuid()) + createdAt DateTime @default (now()) + updatedAt DateTime @updatedAt email String @unique posts Post[] } model Post { id String @id @default(cuid()) + createdAt DateTime @default (now()) + updatedAt DateTime @updatedAt title String author User? @relation(fields: [authorId], references: [id]) authorId String? diff --git a/packages/testtools/package.json b/packages/testtools/package.json index 7e6a8496d..9f813c240 100644 --- a/packages/testtools/package.json +++ b/packages/testtools/package.json @@ -21,8 +21,8 @@ "@prisma/client": "^4.7.0", "@prisma/generator-helper": "^4.7.1", "@prisma/internals": "^4.7.1", - "@zenstackhq/runtime": "workspace:*", "@zenstackhq/language": "workspace:*", + "@zenstackhq/runtime": "workspace:*", "prisma": "~4.7.0", "tmp": "^0.2.1", "zenstack": "workspace:*" diff --git a/packages/testtools/src/schema.ts b/packages/testtools/src/schema.ts index a75b4799b..40271f069 100644 --- a/packages/testtools/src/schema.ts +++ b/packages/testtools/src/schema.ts @@ -69,16 +69,22 @@ generator js { plugin meta { provider = '@core/model-meta' output = '.zenstack' + compile = true + preserveTsFiles = true } plugin policy { provider = '@core/access-policy' output = '.zenstack' + compile = true + preserveTsFiles = true } plugin zod { provider = '@core/zod' output = '.zenstack/zod' + compile = true + preserveTsFiles = true } `; @@ -94,7 +100,7 @@ export async function loadSchema( extraDependencies: string[] = [], compile = false ) { - const { name: projectRoot } = tmp.dirSync(); + const { name: projectRoot } = tmp.dirSync({ unsafeCleanup: true }); try { const root = getWorkspaceRoot(__dirname); @@ -141,7 +147,7 @@ export async function loadSchema( const policy = require(path.join(projectRoot, '.zenstack/policy')).default; const modelMeta = require(path.join(projectRoot, '.zenstack/model-meta')).default; - const zodSchemas = require(path.join(projectRoot, '.zenstack/zod')).default; + const zodSchemas = require(path.join(projectRoot, '.zenstack/zod')); return { projectDir: projectRoot, diff --git a/tests/integration/test-run/package-lock.json b/tests/integration/test-run/package-lock.json index facee48a5..9f4a1932c 100644 --- a/tests/integration/test-run/package-lock.json +++ b/tests/integration/test-run/package-lock.json @@ -129,12 +129,12 @@ "version": "1.0.0-alpha.102", "license": "MIT", "dependencies": { + "@paralleldrive/cuid2": "^2.2.0", "@types/bcryptjs": "^2.4.2", "@zenstackhq/sdk": "workspace:*", "bcryptjs": "^2.4.3", "change-case": "^4.1.2", "colors": "1.4.0", - "cuid": "^2.1.8", "decimal.js": "^10.4.2", "deepcopy": "^2.1.0", "pluralize": "^8.0.0", @@ -145,7 +145,7 @@ }, "devDependencies": { "@types/bcryptjs": "^2.4.2", - "@types/jest": "^29.0.3", + "@types/jest": "^29.5.0", "@types/node": "^14.18.29", "@types/pluralize": "^0.0.29", "copyfiles": "^2.4.1", @@ -162,6 +162,7 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { + "@paralleldrive/cuid2": "^2.2.0", "@prisma/generator-helper": "^4.0.0", "@prisma/internals": "^4.0.0", "@zenstackhq/language": "workspace:*", @@ -171,7 +172,6 @@ "chevrotain": "^9.1.0", "colors": "1.4.0", "commander": "^8.3.0", - "cuid": "^2.1.8", "get-latest-version": "^5.0.1", "langium": "1.1.0", "mixpanel": "^0.17.0", @@ -196,7 +196,7 @@ }, "devDependencies": { "@types/async-exit-hook": "^2.0.0", - "@types/jest": "^29.2.0", + "@types/jest": "^29.5.0", "@types/node": "^14.18.32", "@types/pluralize": "^0.0.29", "@types/semver": "^7.3.13", @@ -205,6 +205,7 @@ "@types/vscode": "^1.56.0", "@typescript-eslint/eslint-plugin": "^5.42.0", "@typescript-eslint/parser": "^5.42.0", + "@vscode/vsce": "^2.19.0", "@zenstackhq/runtime": "workspace:*", "@zenstackhq/testtools": "workspace:*", "concurrently": "^7.4.0", @@ -213,7 +214,7 @@ "esbuild": "^0.15.12", "eslint": "^8.27.0", "eslint-plugin-jest": "^27.1.7", - "jest": "^29.2.1", + "jest": "^29.5.0", "prisma": "^4.0.0", "renamer": "^4.0.0", "rimraf": "^3.0.2", @@ -222,8 +223,7 @@ "ts-node": "^10.9.1", "tsc-alias": "^1.7.0", "typescript": "^4.8.4", - "vitest": "^0.29.7", - "vsce": "^2.13.0" + "vitest": "^0.29.7" }, "engines": { "vscode": "^1.56.0" @@ -326,8 +326,9 @@ "link": true }, "node_modules/zod": { - "version": "3.19.1", - "license": "MIT", + "version": "3.21.1", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.21.1.tgz", + "integrity": "sha512-+dTu2m6gmCbO9Ahm4ZBDapx2O6ZY9QSPXst2WXjcznPMwf2YNpn3RevLx4KkZp1OPW/ouFcoBtBzFz/LeY69oA==", "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -349,8 +350,9 @@ "@zenstackhq/runtime": { "version": "file:../../../packages/runtime/dist", "requires": { + "@paralleldrive/cuid2": "^2.2.0", "@types/bcryptjs": "^2.4.2", - "@types/jest": "^29.0.3", + "@types/jest": "^29.5.0", "@types/node": "^14.18.29", "@types/pluralize": "^0.0.29", "@zenstackhq/sdk": "workspace:*", @@ -358,7 +360,6 @@ "change-case": "^4.1.2", "colors": "1.4.0", "copyfiles": "^2.4.1", - "cuid": "^2.1.8", "decimal.js": "^10.4.2", "deepcopy": "^2.1.0", "pluralize": "^8.0.0", @@ -401,10 +402,11 @@ "zenstack": { "version": "file:../../../packages/schema/dist", "requires": { + "@paralleldrive/cuid2": "^2.2.0", "@prisma/generator-helper": "^4.0.0", "@prisma/internals": "^4.0.0", "@types/async-exit-hook": "^2.0.0", - "@types/jest": "^29.2.0", + "@types/jest": "^29.5.0", "@types/node": "^14.18.32", "@types/pluralize": "^0.0.29", "@types/semver": "^7.3.13", @@ -413,6 +415,7 @@ "@types/vscode": "^1.56.0", "@typescript-eslint/eslint-plugin": "^5.42.0", "@typescript-eslint/parser": "^5.42.0", + "@vscode/vsce": "^2.19.0", "@zenstackhq/language": "workspace:*", "@zenstackhq/runtime": "workspace:*", "@zenstackhq/sdk": "workspace:*", @@ -424,13 +427,12 @@ "commander": "^8.3.0", "concurrently": "^7.4.0", "copyfiles": "^2.4.1", - "cuid": "^2.1.8", "dotenv": "^16.0.3", "esbuild": "^0.15.12", "eslint": "^8.27.0", "eslint-plugin-jest": "^27.1.7", "get-latest-version": "^5.0.1", - "jest": "^29.2.1", + "jest": "^29.5.0", "langium": "1.1.0", "mixpanel": "^0.17.0", "node-machine-id": "^1.1.12", @@ -450,7 +452,6 @@ "typescript": "^4.8.4", "uuid": "^9.0.0", "vitest": "^0.29.7", - "vsce": "^2.13.0", "vscode-jsonrpc": "^8.0.2", "vscode-languageclient": "^8.0.2", "vscode-languageserver": "^8.0.2", @@ -461,7 +462,9 @@ } }, "zod": { - "version": "3.19.1" + "version": "3.21.1", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.21.1.tgz", + "integrity": "sha512-+dTu2m6gmCbO9Ahm4ZBDapx2O6ZY9QSPXst2WXjcznPMwf2YNpn3RevLx4KkZp1OPW/ouFcoBtBzFz/LeY69oA==" } } } diff --git a/tests/integration/tests/e2e/filter-function-coverage.test.ts b/tests/integration/tests/e2e/filter-function-coverage.test.ts index 049c45e3b..daedbbef3 100644 --- a/tests/integration/tests/e2e/filter-function-coverage.test.ts +++ b/tests/integration/tests/e2e/filter-function-coverage.test.ts @@ -1,4 +1,4 @@ -import { loadSchema, run, type WeakDbClientContract } from '@zenstackhq/testtools'; +import { loadSchema } from '@zenstackhq/testtools'; describe('Filter Function Coverage Tests', () => { it('contains case-sensitive', async () => { diff --git a/tests/integration/tests/e2e/prisma-methods.test.ts b/tests/integration/tests/e2e/prisma-methods.test.ts index b7eb65b6e..3212b6944 100644 --- a/tests/integration/tests/e2e/prisma-methods.test.ts +++ b/tests/integration/tests/e2e/prisma-methods.test.ts @@ -1,5 +1,5 @@ import { AuthUser } from '@zenstackhq/runtime'; -import { loadSchema, run, WeakDbClientContract } from '@zenstackhq/testtools'; +import { WeakDbClientContract, loadSchema, run } from '@zenstackhq/testtools'; describe('Prisma Methods Tests', () => { let getDb: (user?: AuthUser) => WeakDbClientContract; diff --git a/tests/integration/tests/nextjs/test-project/package.json b/tests/integration/tests/nextjs/test-project/package.json index d57d1d392..1a010be9d 100644 --- a/tests/integration/tests/nextjs/test-project/package.json +++ b/tests/integration/tests/nextjs/test-project/package.json @@ -16,7 +16,8 @@ "react": "18.2.0", "react-dom": "18.2.0", "typescript": "4.9.4", - "@prisma/client": "^4.7.0" + "@prisma/client": "^4.7.0", + "@zenstackhq/runtime": "../../../../../../packages/runtime/dist" }, "devDependencies": { "prisma": "^4.7.0" diff --git a/tests/integration/tests/nextjs/test-project/postgres.zmodel b/tests/integration/tests/nextjs/test-project/postgres.zmodel index 5ca059107..ea755a92b 100644 --- a/tests/integration/tests/nextjs/test-project/postgres.zmodel +++ b/tests/integration/tests/nextjs/test-project/postgres.zmodel @@ -7,6 +7,20 @@ generator js { provider = 'prisma-client-js' } +plugin meta { + provider = '@core/model-meta' + output = '.zenstack' + compile = true + preserveTsFiles = true +} + +plugin policy { + provider = '@core/access-policy' + output = '.zenstack' + compile = true + preserveTsFiles = true +} + plugin react { provider = '@zenstackhq/react' output = 'lib/hooks' @@ -16,7 +30,6 @@ model User { id String @id name String posts Post[] - @@allow('create,read', true) @@allow('update,delete', auth() == this) } @@ -27,7 +40,6 @@ model Post { author User? @relation(fields: [authorId], references: [id]) authorId String? published Boolean @default(false) - @@allow('all', auth() == this) @@allow('read', published) } diff --git a/tests/integration/tests/nextjs/test-project/sqlite.zmodel b/tests/integration/tests/nextjs/test-project/sqlite.zmodel index 1648827a1..7d91e20cf 100644 --- a/tests/integration/tests/nextjs/test-project/sqlite.zmodel +++ b/tests/integration/tests/nextjs/test-project/sqlite.zmodel @@ -7,6 +7,20 @@ generator js { provider = 'prisma-client-js' } +plugin meta { + provider = '@core/model-meta' + output = '.zenstack' + compile = true + preserveTsFiles = true +} + +plugin policy { + provider = '@core/access-policy' + output = '.zenstack' + compile = true + preserveTsFiles = true +} + plugin react { provider = '@zenstackhq/react' output = 'lib/hooks' @@ -16,7 +30,6 @@ model User { id String @id name String posts Post[] - @@allow('create,read', true) @@allow('update,delete', auth() == this) } @@ -27,7 +40,6 @@ model Post { author User? @relation(fields: [authorId], references: [id]) authorId String? published Boolean @default(false) - @@allow('all', auth() == this) @@allow('read', published) } diff --git a/tests/integration/tests/schema/cal-com.zmodel b/tests/integration/tests/schema/cal-com.zmodel index 583fd14ab..4e48b3102 100644 --- a/tests/integration/tests/schema/cal-com.zmodel +++ b/tests/integration/tests/schema/cal-com.zmodel @@ -14,16 +14,22 @@ generator client { plugin meta { provider = '@core/model-meta' output = '.zenstack' + compile = true + preserveTsFiles = true } plugin policy { provider = '@core/access-policy' output = '.zenstack' + compile = true + preserveTsFiles = true } plugin zod { provider = '@core/zod' output = '.zenstack/zod' + compile = true + preserveTsFiles = true } enum SchedulingType { diff --git a/tests/integration/tests/schema/todo.zmodel b/tests/integration/tests/schema/todo.zmodel index eac571be3..5c8eab6ad 100644 --- a/tests/integration/tests/schema/todo.zmodel +++ b/tests/integration/tests/schema/todo.zmodel @@ -15,16 +15,22 @@ generator js { plugin meta { provider = '@core/model-meta' output = '.zenstack' + compile = true + preserveTsFiles = true } plugin policy { provider = '@core/access-policy' output = '.zenstack' + compile = true + preserveTsFiles = true } plugin zod { provider = '@core/zod' output = '.zenstack/zod' + compile = true + preserveTsFiles = true } /* diff --git a/tests/integration/tests/trpc/test-project/package.json b/tests/integration/tests/trpc/test-project/package.json index 46ce8aaae..097b1cb86 100644 --- a/tests/integration/tests/trpc/test-project/package.json +++ b/tests/integration/tests/trpc/test-project/package.json @@ -23,7 +23,8 @@ "react-dom": "18.2.0", "superjson": "^1.12.2", "typescript": "4.9.4", - "zod": "3.21.1" + "zod": "3.21.1", + "@zenstackhq/runtime": "../../../../../../packages/runtime/dist" }, "devDependencies": { "prisma": "^4.7.0" diff --git a/tests/integration/tests/trpc/test-project/todo.zmodel b/tests/integration/tests/trpc/test-project/todo.zmodel index 8468b6ef2..04c997427 100644 --- a/tests/integration/tests/trpc/test-project/todo.zmodel +++ b/tests/integration/tests/trpc/test-project/todo.zmodel @@ -7,6 +7,20 @@ generator js { provider = 'prisma-client-js' } +plugin meta { + provider = '@core/model-meta' + output = '.zenstack' + compile = true + preserveTsFiles = true +} + +plugin policy { + provider = '@core/access-policy' + output = '.zenstack' + compile = true + preserveTsFiles = true +} + plugin trpc { provider = '@zenstackhq/trpc' output = 'server/routers/generated' @@ -16,7 +30,6 @@ model User { id String @id name String posts Post[] - @@allow('create,read', true) @@allow('update,delete', auth() == this) } @@ -27,7 +40,6 @@ model Post { author User? @relation(fields: [authorId], references: [id]) authorId String? published Boolean @default(false) - @@allow('all', auth() == this) @@allow('read', published) } diff --git a/tests/integration/tests/with-policy/auth.test.ts b/tests/integration/tests/with-policy/auth.test.ts index 57c9fff08..fb7e0c9b5 100644 --- a/tests/integration/tests/with-policy/auth.test.ts +++ b/tests/integration/tests/with-policy/auth.test.ts @@ -3,7 +3,6 @@ import path from 'path'; describe('With Policy: auth() test', () => { let origDir: string; - const suite = 'undefined-user'; beforeAll(async () => { origDir = path.resolve('.'); diff --git a/tests/integration/tests/with-policy/deep-nested.test.ts b/tests/integration/tests/with-policy/deep-nested.test.ts index 842feddbd..86ac0b5a2 100644 --- a/tests/integration/tests/with-policy/deep-nested.test.ts +++ b/tests/integration/tests/with-policy/deep-nested.test.ts @@ -3,7 +3,6 @@ import path from 'path'; describe('With Policy:deep nested', () => { let origDir: string; - const suite = 'deep-nested'; const model = ` // M1 - M2 - M3 diff --git a/tests/integration/tests/with-policy/field-validation.test.ts b/tests/integration/tests/with-policy/field-validation.test.ts index 38925653d..4e3d8a4e8 100644 --- a/tests/integration/tests/with-policy/field-validation.test.ts +++ b/tests/integration/tests/with-policy/field-validation.test.ts @@ -1,8 +1,7 @@ -import { loadSchema, run, WeakDbClientContract } from '@zenstackhq/testtools'; +import { WeakDbClientContract, loadSchema, run } from '@zenstackhq/testtools'; describe('With Policy: field validation', () => { let db: WeakDbClientContract; - let prisma: WeakDbClientContract; beforeAll(async () => { const { withPolicy, prisma: _prisma } = await loadSchema( @@ -49,7 +48,6 @@ describe('With Policy: field validation', () => { ` ); db = withPolicy(); - prisma = _prisma; }); beforeEach(() => { diff --git a/tests/integration/tests/with-policy/multi-field-unique.test.ts b/tests/integration/tests/with-policy/multi-field-unique.test.ts index a6629357c..3dcc07850 100644 --- a/tests/integration/tests/with-policy/multi-field-unique.test.ts +++ b/tests/integration/tests/with-policy/multi-field-unique.test.ts @@ -3,7 +3,6 @@ import path from 'path'; describe('With Policy: multi-field unique', () => { let origDir: string; - const suite = 'multi-field-unique'; beforeAll(async () => { origDir = path.resolve('.'); diff --git a/tests/integration/tests/with-policy/nested-to-many.test.ts b/tests/integration/tests/with-policy/nested-to-many.test.ts index a3ae19dfb..8eedfd75a 100644 --- a/tests/integration/tests/with-policy/nested-to-many.test.ts +++ b/tests/integration/tests/with-policy/nested-to-many.test.ts @@ -3,7 +3,6 @@ import path from 'path'; describe('With Policy:nested to-many', () => { let origDir: string; - const suite = 'nested-to-many'; beforeAll(async () => { origDir = path.resolve('.'); diff --git a/tests/integration/tests/with-policy/nested-to-one.test.ts b/tests/integration/tests/with-policy/nested-to-one.test.ts index 8eff681d5..f14916667 100644 --- a/tests/integration/tests/with-policy/nested-to-one.test.ts +++ b/tests/integration/tests/with-policy/nested-to-one.test.ts @@ -3,7 +3,6 @@ import path from 'path'; describe('With Policy:nested to-one', () => { let origDir: string; - const suite = 'nested-to-one'; beforeAll(async () => { origDir = path.resolve('.'); diff --git a/tests/integration/tests/with-policy/post-update.test.ts b/tests/integration/tests/with-policy/post-update.test.ts index 73d21713b..dd55caa9c 100644 --- a/tests/integration/tests/with-policy/post-update.test.ts +++ b/tests/integration/tests/with-policy/post-update.test.ts @@ -3,7 +3,6 @@ import path from 'path'; describe('With Policy: post update', () => { let origDir: string; - const suite = 'post-update'; beforeAll(async () => { origDir = path.resolve('.'); diff --git a/tests/integration/tests/with-policy/toplevel-operations.test.ts b/tests/integration/tests/with-policy/toplevel-operations.test.ts index 72171315f..15626e1c2 100644 --- a/tests/integration/tests/with-policy/toplevel-operations.test.ts +++ b/tests/integration/tests/with-policy/toplevel-operations.test.ts @@ -3,7 +3,6 @@ import path from 'path'; describe('With Policy:toplevel operations', () => { let origDir: string; - const suite = 'toplevel'; beforeAll(async () => { origDir = path.resolve('.');