Skip to content

Commit

Permalink
fix(zod): field with auth() and @default() should be generated as…
Browse files Browse the repository at this point in the history
… optional (#1266)
  • Loading branch information
ymc9 authored Apr 17, 2024
1 parent c557dc9 commit b19bbab
Show file tree
Hide file tree
Showing 9 changed files with 120 additions and 49 deletions.
2 changes: 1 addition & 1 deletion packages/plugins/trpc/src/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ function createAppRouter(
hiddenModels: string[],
generateModelActions: string[] | undefined,
generateClientHelpers: string[] | undefined,
zmodel: Model,
_zmodel: Model,
zodSchemasImport: string,
options: PluginOptions
) {
Expand Down
4 changes: 2 additions & 2 deletions packages/schema/src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,9 @@ export function createProgram() {
.command('generate')
.description('Run code generation.')
.addOption(schemaOption)
.addOption(new Option('-o, --output <path>', 'default output directory for built-in plugins'))
.addOption(new Option('-o, --output <path>', 'default output directory for core plugins'))
.addOption(new Option('--no-default-plugins', 'do not run default plugins'))
.addOption(new Option('--no-compile', 'do not compile the output of built-in plugins'))
.addOption(new Option('--no-compile', 'do not compile the output of core plugins'))
.addOption(noVersionCheckOption)
.addOption(noDependencyCheck)
.action(generateAction);
Expand Down
4 changes: 2 additions & 2 deletions packages/schema/src/plugins/enhancer/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { PluginError, RUNTIME_PACKAGE, createProject, resolvePath, type PluginFunction } from '@zenstackhq/sdk';
import { PluginError, createProject, resolvePath, type PluginFunction } from '@zenstackhq/sdk';
import path from 'path';
import { getDefaultOutputFolder } from '../plugin-utils';
import { EnhancerGenerator } from './enhance';
Expand Down Expand Up @@ -31,7 +31,7 @@ const run: PluginFunction = async (model, options, _dmmf, globalOptions) => {
// resolve it relative to the schema path
prismaClientPath = path.relative(path.dirname(options.schemaPath), prismaClientPathAbs);
} else {
prismaClientPath = `${RUNTIME_PACKAGE}/models`;
prismaClientPath = `.zenstack/models`;
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/schema/src/plugins/zod/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ export class ZodSchemaGenerator {
writer.write(`const baseSchema = z.object(`);
writer.inlineBlock(() => {
scalarFields.forEach((field) => {
writer.writeLine(`${field.name}: ${makeFieldSchema(field, true)},`);
writer.writeLine(`${field.name}: ${makeFieldSchema(field)},`);
});
});
writer.writeLine(');');
Expand Down
18 changes: 12 additions & 6 deletions packages/schema/src/plugins/zod/utils/schema-gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ import {
} from '@zenstackhq/sdk/ast';
import { upperCaseFirst } from 'upper-case-first';
import { name } from '..';
import { isDefaultWithAuth } from '../../enhancer/enhancer-utils';

export function makeFieldSchema(field: DataModelField, respectDefault = false) {
export function makeFieldSchema(field: DataModelField) {
if (isDataModel(field.type.reference?.ref)) {
if (field.type.array) {
// array field is always optional
Expand Down Expand Up @@ -139,15 +140,20 @@ export function makeFieldSchema(field: DataModelField, respectDefault = false) {
}
}

if (respectDefault) {
if (field.attributes.some(isDefaultWithAuth)) {
// field uses `auth()` in `@default()`, this was transformed into a pseudo default
// value, while compiling to zod we should turn it into an optional field instead
// of `.default()`
schema += '.nullish()';
} else {
const schemaDefault = getFieldSchemaDefault(field);
if (schemaDefault) {
if (schemaDefault !== undefined) {
schema += `.default(${schemaDefault})`;
}
}

if (field.type.optional) {
schema += '.nullish()';
if (field.type.optional) {
schema += '.nullish()';
}
}

return schema;
Expand Down
6 changes: 5 additions & 1 deletion packages/sdk/src/prisma.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import type { DMMF } from '@prisma/generator-helper';
import { getDMMF as _getDMMF, type GetDMMFOptions } from '@prisma/internals';
import { DEFAULT_RUNTIME_LOAD_PATH } from '@zenstackhq/runtime';
import path from 'path';
import { RUNTIME_PACKAGE } from './constants';
import type { PluginOptions } from './types';
Expand All @@ -14,7 +15,10 @@ export function getPrismaClientImportSpec(importingFromDir: string, options: Plu
return '@prisma/client';
}

if (options.prismaClientPath.startsWith(RUNTIME_PACKAGE)) {
if (
options.prismaClientPath.startsWith(RUNTIME_PACKAGE) ||
options.prismaClientPath.startsWith(DEFAULT_RUNTIME_LOAD_PATH)
) {
return options.prismaClientPath;
}

Expand Down
38 changes: 36 additions & 2 deletions tests/integration/tests/cli/plugins.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ describe('CLI Plugins Tests', () => {

const PACKAGE_MANAGERS = ['npm' /*, 'pnpm', 'pnpm-workspace'*/] as const;

function zenstackGenerate(pm: (typeof PACKAGE_MANAGERS)[number]) {
function zenstackGenerate(pm: (typeof PACKAGE_MANAGERS)[number], output?: string) {
switch (pm) {
case 'npm':
run(`ZENSTACK_TEST=0 npx zenstack generate`);
run(`ZENSTACK_TEST=0 npx zenstack generate${output ? ' --output ' + output : ''}`);
break;
// case 'pnpm':
// case 'pnpm-workspace':
Expand Down Expand Up @@ -275,4 +275,38 @@ ${BASE_MODEL}
run('npx tsc');
}
});

it('all plugins custom core output path', async () => {
for (const pm of PACKAGE_MANAGERS) {
console.log('[PACKAGE MANAGER]', pm);
await initProject(pm);

let schemaContent = `
generator client {
provider = "prisma-client-js"
}
${BASE_MODEL}
`;
for (const plugin of plugins) {
if (!plugin.includes('trp')) {
schemaContent += `\n${plugin}`;
}
}

schemaContent += `plugin trpc {
provider = '@zenstackhq/trpc'
output = 'lib/trpc'
zodSchemasImport = '../../../zen/zod'
}`;

fs.writeFileSync('schema.zmodel', schemaContent);

// generate
zenstackGenerate(pm, './zen');

// compile
run('npx tsc');
}
});
});
68 changes: 34 additions & 34 deletions tests/integration/tests/regression/issue-1241.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,40 @@ describe('issue 1241', () => {
it('regression', async () => {
const { enhance, prisma } = await loadSchema(
`
model User {
id String @id @default(uuid())
todos Todo[]
@@auth
@@allow('all', true)
}
model Todo {
id String @id @default(uuid())
user_id String
user User @relation(fields: [user_id], references: [id])
images File[] @relation("todo_images")
documents File[] @relation("todo_documents")
@@allow('all', true)
}
model File {
id String @id @default(uuid())
s3_key String @unique
label String
todo_image_id String?
todo_image Todo? @relation("todo_images", fields: [todo_image_id], references: [id])
todo_document_id String?
todo_document Todo? @relation("todo_documents", fields: [todo_document_id], references: [id])
@@allow('all', true)
}
`,
model User {
id String @id @default(uuid())
todos Todo[]
@@auth
@@allow('all', true)
}
model Todo {
id String @id @default(uuid())
user_id String
user User @relation(fields: [user_id], references: [id])
images File[] @relation("todo_images")
documents File[] @relation("todo_documents")
@@allow('all', true)
}
model File {
id String @id @default(uuid())
s3_key String @unique
label String
todo_image_id String?
todo_image Todo? @relation("todo_images", fields: [todo_image_id], references: [id])
todo_document_id String?
todo_document Todo? @relation("todo_documents", fields: [todo_document_id], references: [id])
@@allow('all', true)
}
`,
{ logPrismaQuery: true }
);

Expand Down
27 changes: 27 additions & 0 deletions tests/integration/tests/regression/issue-1265.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { loadSchema } from '@zenstackhq/testtools';

describe('issue 1265', () => {
it('regression', async () => {
const { zodSchemas } = await loadSchema(
`
model User {
id String @id @default(uuid())
posts Post[]
@@allow('all', true)
}
model Post {
id String @id @default(uuid())
title String @default('xyz')
userId String @default(auth().id)
user User @relation(fields: [userId], references: [id])
@@allow('all', true)
}
`,
{ fullZod: true, pushDb: false }
);

expect(zodSchemas.models.PostCreateSchema.safeParse({ title: 'Post 1' }).success).toBeTruthy();
expect(zodSchemas.input.PostInputSchema.create.safeParse({ data: { title: 'Post 1' } }).success).toBeTruthy();
});
});

0 comments on commit b19bbab

Please sign in to comment.