Skip to content

Commit 91fe8e7

Browse files
authored
fix: prisma schema generation issue with calling attribute function with literal (#930)
1 parent cdd0f55 commit 91fe8e7

File tree

5 files changed

+50
-18
lines changed

5 files changed

+50
-18
lines changed

packages/schema/src/plugins/prisma/prisma-builder.ts

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -304,17 +304,10 @@ export class FunctionCall {
304304
}
305305

306306
export class FunctionCallArg {
307-
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
308-
constructor(public name: string | undefined, public value: any) {}
307+
constructor(public name: string | undefined, public value: string) {}
309308

310309
toString(): string {
311-
const val =
312-
this.value === null || this.value === undefined
313-
? 'null'
314-
: typeof this.value === 'string'
315-
? `"${this.value}"`
316-
: this.value.toString();
317-
return this.name ? `${this.name}: ${val}` : val;
310+
return this.name ? `${this.name}: ${this.value}` : this.value;
318311
}
319312
}
320313

packages/schema/src/plugins/prisma/schema-generator.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ import {
1818
isArrayExpr,
1919
isInvocationExpr,
2020
isLiteralExpr,
21+
isNullExpr,
2122
isReferenceExpr,
23+
isStringLiteral,
2224
LiteralExpr,
2325
Model,
2426
NumberLiteral,
@@ -35,6 +37,7 @@ import {
3537
PluginOptions,
3638
resolved,
3739
resolvePath,
40+
ZModelCodeGenerator,
3841
} from '@zenstackhq/sdk';
3942
import fs from 'fs';
4043
import { writeFile } from 'fs/promises';
@@ -45,6 +48,7 @@ import { name } from '.';
4548
import { getStringLiteral } from '../../language-server/validator/utils';
4649
import telemetry from '../../telemetry';
4750
import { execSync } from '../../utils/exec-utils';
51+
import { getPackageJson } from '../../utils/pkg-utils';
4852
import {
4953
ModelFieldType,
5054
AttributeArg as PrismaAttributeArg,
@@ -62,8 +66,6 @@ import {
6266
PassThroughAttribute as PrismaPassThroughAttribute,
6367
SimpleField,
6468
} from './prisma-builder';
65-
import { ZModelCodeGenerator } from '@zenstackhq/sdk';
66-
import { getPackageJson } from '../../utils/pkg-utils';
6769

6870
const MODEL_PASSTHROUGH_ATTR = '@@prisma.passthrough';
6971
const FIELD_PASSTHROUGH_ATTR = '@prisma.passthrough';
@@ -377,10 +379,15 @@ export default class PrismaSchemaGenerator {
377379
return new PrismaFunctionCall(
378380
resolved(node.function).name,
379381
node.args.map((arg) => {
380-
if (!isLiteralExpr(arg.value)) {
381-
throw new PluginError(name, 'Function call argument must be literal');
382-
}
383-
return new PrismaFunctionCallArg(arg.name, arg.value.value);
382+
const val = match(arg.value)
383+
.when(isStringLiteral, (v) => `"${v.value}"`)
384+
.when(isLiteralExpr, (v) => v.value.toString())
385+
.when(isNullExpr, () => 'null')
386+
.otherwise(() => {
387+
throw new PluginError(name, 'Function call argument must be literal or null');
388+
});
389+
390+
return new PrismaFunctionCallArg(arg.name, val);
384391
})
385392
);
386393
}

packages/schema/tests/generator/prisma-builder.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ describe('Prisma Builder Tests', () => {
102102
undefined,
103103
new AttributeArgValue(
104104
'FunctionCall',
105-
new FunctionCall('dbgenerated', [new FunctionCallArg(undefined, 'timestamp_id()')])
105+
new FunctionCall('dbgenerated', [new FunctionCallArg(undefined, '"timestamp_id()"')])
106106
)
107107
),
108108
]),

packages/schema/tests/generator/prisma-generator.test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,38 @@ describe('Prisma generator test', () => {
9696
expect(content).toContain('unsupported Unsupported("foo")');
9797
});
9898

99+
it('attribute function', async () => {
100+
const model = await loadModel(`
101+
datasource db {
102+
provider = 'postgresql'
103+
url = env('DATABASE_URL')
104+
}
105+
106+
model User {
107+
id String @id @default(nanoid(6))
108+
x String @default(nanoid())
109+
y String @default(dbgenerated("gen_random_uuid()"))
110+
}
111+
`);
112+
113+
const { name } = tmp.fileSync({ postfix: '.prisma' });
114+
await new PrismaSchemaGenerator().generate(model, {
115+
name: 'Prisma',
116+
provider: '@core/prisma',
117+
schemaPath: 'schema.zmodel',
118+
output: name,
119+
generateClient: false,
120+
});
121+
122+
const content = fs.readFileSync(name, 'utf-8');
123+
// "nanoid()" is only available in later versions of Prisma
124+
await getDMMF({ datamodel: content }, '5.0.0');
125+
126+
expect(content).toContain('@default(nanoid(6))');
127+
expect(content).toContain('@default(nanoid())');
128+
expect(content).toContain('@default(dbgenerated("gen_random_uuid()"))');
129+
});
130+
99131
it('triple slash comments', async () => {
100132
const model = await loadModel(`
101133
datasource db {

packages/sdk/src/prisma.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@ export type GetDMMFOptions = {
8181
/**
8282
* Loads Prisma DMMF with appropriate version
8383
*/
84-
export function getDMMF(options: GetDMMFOptions): Promise<DMMF.Document> {
85-
const prismaVersion = getPrismaVersion();
84+
export function getDMMF(options: GetDMMFOptions, defaultPrismaVersion?: string): Promise<DMMF.Document> {
85+
const prismaVersion = getPrismaVersion() ?? defaultPrismaVersion;
8686
if (prismaVersion && semver.gte(prismaVersion, '5.0.0')) {
8787
const _getDMMF = require('@prisma/internals-v5').getDMMF;
8888
return _getDMMF(options);

0 commit comments

Comments
 (0)