Skip to content

Commit

Permalink
fix: support string literal keys for object expressions in ZModel
Browse files Browse the repository at this point in the history
  • Loading branch information
ymc9 committed Oct 11, 2023
1 parent 2e15dfb commit 95f82dd
Show file tree
Hide file tree
Showing 24 changed files with 104 additions and 30 deletions.
2 changes: 1 addition & 1 deletion packages/language/src/generated/ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ export function isEnumField(item: unknown): item is EnumField {
export interface FieldInitializer extends AstNode {
readonly $container: ObjectExpr;
readonly $type: 'FieldInitializer';
name: RegularID
name: RegularID | string
value: Expression
}

Expand Down
22 changes: 17 additions & 5 deletions packages/language/src/generated/grammar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1171,11 +1171,23 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel
"feature": "name",
"operator": "=",
"terminal": {
"$type": "RuleCall",
"rule": {
"$ref": "#/rules@47"
},
"arguments": []
"$type": "Alternatives",
"elements": [
{
"$type": "RuleCall",
"rule": {
"$ref": "#/rules@47"
},
"arguments": []
},
{
"$type": "RuleCall",
"rule": {
"$ref": "#/rules@67"
},
"arguments": []
}
]
}
},
{
Expand Down
2 changes: 1 addition & 1 deletion packages/language/src/zmodel.langium
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ ObjectExpr:
'}';

FieldInitializer:
name=RegularID ':' value=(Expression);
name=(RegularID | STRING) ':' value=(Expression);

InvocationExpr:
function=[FunctionDecl] '(' ArgumentList? ')';
Expand Down
1 change: 1 addition & 0 deletions packages/schema/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@
"@typescript-eslint/parser": "^5.42.0",
"@vscode/vsce": "^2.19.0",
"@zenstackhq/runtime": "workspace:*",
"@zenstackhq/testtools": "workspace:*",
"concurrently": "^7.4.0",
"copyfiles": "^2.4.1",
"dotenv": "^16.0.3",
Expand Down
2 changes: 1 addition & 1 deletion packages/schema/tests/generator/expression-writer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { DataModel, Enum, Expression, isDataModel, isEnum } from '@zenstackhq/la
import * as tmp from 'tmp';
import { Project, VariableDeclarationKind } from 'ts-morph';
import { ExpressionWriter } from '../../src/plugins/access-policy/expression-writer';
import { loadModel } from '../utils';
import { loadModel } from '@zenstackhq/testtools';

describe('Expression Writer Tests', () => {
it('boolean literal', async () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/schema/tests/generator/prisma-generator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import path from 'path';
import tmp from 'tmp';
import { loadDocument } from '../../src/cli/cli-util';
import PrismaSchemaGenerator from '../../src/plugins/prisma/schema-generator';
import { loadModel } from '../utils';
import { loadModel } from '@zenstackhq/testtools';

describe('Prisma generator test', () => {
it('datasource coverage', async () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/schema/tests/generator/zmodel-generator.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { loadModel } from '../utils';
import { loadModel } from '@zenstackhq/testtools';
import ZModelCodeGenerator from '../../src/plugins/prisma/zmodel-code-generator';
import { DataModel, DataModelAttribute, DataModelFieldAttribute } from '@zenstackhq/language/ast';

Expand Down
2 changes: 1 addition & 1 deletion packages/schema/tests/schema/abstract.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as fs from 'fs';
import path from 'path';
import { loadModel } from '../utils';
import { loadModel } from '@zenstackhq/testtools';

describe('Abstract Schema Tests', () => {
it('model loading', async () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/schema/tests/schema/cal-com.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as fs from 'fs';
import path from 'path';
import { loadModel } from '../utils';
import { loadModel } from '@zenstackhq/testtools';

describe('Cal.com Schema Tests', () => {
it('model loading', async () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/schema/tests/schema/parser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
StringLiteral,
UnaryExpr,
} from '@zenstackhq/language/ast';
import { loadModel } from '../utils';
import { loadModel } from '@zenstackhq/testtools';

describe('Parsing Tests', () => {
it('data source', async () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/schema/tests/schema/sample-todo.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as fs from 'fs';
import path from 'path';
import { loadModel } from '../utils';
import { loadModel } from '@zenstackhq/testtools';

describe('Sample Todo Schema Tests', () => {
it('model loading', async () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/schema/tests/schema/stdlib.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { SchemaLoadingError } from '@zenstackhq/testtools';
import { NodeFileSystem } from 'langium/node';
import path from 'path';
import { URI } from 'vscode-uri';
import { createZModelServices } from '../../src/language-server/zmodel-module';
import { SchemaLoadingError } from '../utils';

describe('Stdlib Tests', () => {
it('stdlib', async () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/schema/tests/schema/trigger-dev.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as fs from 'fs';
import path from 'path';
import { loadModel } from '../utils';
import { loadModel } from '@zenstackhq/testtools';

describe('Trigger.dev Schema Tests', () => {
it('model loading', async () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/// <reference types="@types/jest" />

import { loadModel, loadModelWithError } from '../../utils';
import { loadModel, loadModelWithError } from '@zenstackhq/testtools';

describe('Attribute tests', () => {
const prelude = `
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { loadModel, loadModelWithError } from '../../utils';
import { loadModel, loadModelWithError } from '@zenstackhq/testtools';

describe('Data Model Validation Tests', () => {
const prelude = `
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { loadModel, loadModelWithError } from '../../utils';
import { loadModel, loadModelWithError } from '@zenstackhq/testtools';

describe('Datasource Validation Tests', () => {
it('missing fields', async () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { loadModelWithError } from '../../utils';
import { loadModelWithError } from '@zenstackhq/testtools';

describe('Enum Validation Tests', () => {
const prelude = `
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { loadModelWithError } from '../../utils';
import { loadModelWithError } from '@zenstackhq/testtools';

describe('Toplevel Schema Validation Tests', () => {
it('too many datasources', async () => {
Expand Down
13 changes: 10 additions & 3 deletions packages/sdk/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,17 @@ export function resolved<T extends AstNode>(ref: Reference<T>): T {
export function getLiteral<T extends string | number | boolean | any = any>(
expr: Expression | ConfigExpr | undefined
): T | undefined {
if (!isLiteralExpr(expr)) {
return getObjectLiteral<T>(expr);
switch (expr?.$type) {
case 'ObjectExpr':
return getObjectLiteral<T>(expr);
case 'StringLiteral':
case 'BooleanLiteral':
return expr.value as T;
case 'NumberLiteral':
return parseFloat(expr.value) as T;
default:
return undefined;
}
return expr.value as T;
}

export function getArray(expr: Expression | ConfigExpr | undefined) {
Expand Down
2 changes: 2 additions & 0 deletions packages/testtools/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@
"@zenstackhq/runtime": "workspace:*",
"@zenstackhq/sdk": "workspace:*",
"json5": "^2.2.3",
"langium": "1.2.0",
"pg": "^8.11.1",
"tmp": "^0.2.1",
"vscode-uri": "^3.0.6",
"zenstack": "workspace:*"
},
"devDependencies": {
Expand Down
3 changes: 2 additions & 1 deletion packages/testtools/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './schema';
export * from './db';
export * from './model';
export * from './schema';
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Model } from '@zenstackhq/language/ast';
import { Model } from '@zenstackhq/sdk/ast';
import * as fs from 'fs';
import { NodeFileSystem } from 'langium/node';
import * as path from 'path';
import * as tmp from 'tmp';
import { URI } from 'vscode-uri';
import { createZModelServices } from '../src/language-server/zmodel-module';
import { mergeBaseModel } from '../src/utils/ast-utils';
import { createZModelServices } from 'zenstack/language-server/zmodel-module';
import { mergeBaseModel } from 'zenstack/utils/ast-utils';

export class SchemaLoadingError extends Error {
constructor(public readonly errors: string[]) {
Expand All @@ -18,7 +18,7 @@ export async function loadModel(content: string, validate = true, verbose = true
fs.writeFileSync(docPath, content);
const { shared } = createZModelServices(NodeFileSystem);
const stdLib = shared.workspace.LangiumDocuments.getOrCreateDocument(
URI.file(path.resolve('src/res/stdlib.zmodel'))
URI.file(path.resolve(__dirname, '../../schema/src/res/stdlib.zmodel'))
);
const doc = shared.workspace.LangiumDocuments.getOrCreateDocument(URI.file(docPath));

Expand Down Expand Up @@ -60,7 +60,9 @@ export async function loadModelWithError(content: string, verbose = false) {
try {
await loadModel(content, true, verbose);
} catch (err) {
expect(err).toBeInstanceOf(SchemaLoadingError);
if (!(err instanceof SchemaLoadingError)) {
throw err;
}
return (err as SchemaLoadingError).errors;
}
throw new Error('No error is thrown');
Expand Down
9 changes: 9 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

40 changes: 40 additions & 0 deletions tests/integration/tests/regression/issue-744.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { getObjectLiteral } from '@zenstackhq/sdk';
import { Plugin, PluginField, isPlugin } from '@zenstackhq/sdk/ast';
import { loadModel } from '@zenstackhq/testtools';

describe('Regression: issue 744', () => {
it('regression', async () => {
const model = await loadModel(
`
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
plugin zod {
provider = '@core/zod'
settings = {
'200': { status: 'ok' },
'x-y-z': 200,
foo: 'bar'
}
}
model Foo {
id String @id @default(cuid())
}
`
);

const plugin = model.declarations.find((d): d is Plugin => isPlugin(d));
const settings = plugin?.fields.find((f): f is PluginField => f.name === 'settings');
const value: any = getObjectLiteral(settings?.value);
expect(value['200']).toMatchObject({ status: 'ok' });
expect(value['x-y-z']).toBe(200);
expect(value.foo).toBe('bar');
});
});

0 comments on commit 95f82dd

Please sign in to comment.