Skip to content

Commit dd36959

Browse files
authored
fix: User model not found when using policy in the imported model (#457)
1 parent 7ddeec5 commit dd36959

File tree

3 files changed

+89
-10
lines changed

3 files changed

+89
-10
lines changed

packages/schema/src/cli/cli-util.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { getLiteral, PluginError } from '@zenstackhq/sdk';
33
import colors from 'colors';
44
import fs from 'fs';
55
import getLatestVersion from 'get-latest-version';
6-
import { getDocument, LangiumDocument, LangiumDocuments } from 'langium';
6+
import { AstNode, getDocument, LangiumDocument, LangiumDocuments, Mutable } from 'langium';
77
import { NodeFileSystem } from 'langium/node';
88
import ora from 'ora';
99
import path from 'path';
@@ -193,7 +193,17 @@ export function eagerLoadAllImports(
193193

194194
export function mergeImportsDeclarations(documents: LangiumDocuments, model: Model) {
195195
const importedModels = resolveTransitiveImports(documents, model);
196-
model.declarations.push(...importedModels.flatMap((m) => m.declarations));
196+
197+
const importedDeclarations = importedModels.flatMap((m) => m.declarations);
198+
199+
importedDeclarations.forEach((d) => {
200+
const mutable = d as Mutable<AstNode>;
201+
// The plugin might use $container to access the model
202+
// need to make sure it is always resolved to the main model
203+
mutable.$container = model;
204+
});
205+
206+
model.declarations.push(...importedDeclarations);
197207
}
198208

199209
export async function getPluginDocuments(services: ZModelServices, fileName: string): Promise<LangiumDocument[]> {

packages/testtools/src/schema.ts

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,18 @@ import tmp from 'tmp';
1111
import { loadDocument } from 'zenstack/cli/cli-util';
1212
import prismaPlugin from 'zenstack/plugins/prisma';
1313

14+
/**
15+
* Use it to represent multiple files in a single string like this
16+
`schema.zmodel
17+
import "user"
18+
${FILE_SPLITTER}user.zmodel
19+
import "schema"
20+
model User {
21+
...
22+
}`
23+
*/
24+
export const FILE_SPLITTER = '#FILE_SPLITTER#';
25+
1426
export type WeakDbOperations = {
1527
[key in keyof DbOperations]: (...args: any[]) => Promise<any>;
1628
};
@@ -112,17 +124,42 @@ export async function loadSchema(
112124
console.log('Workdir:', projectRoot);
113125
process.chdir(projectRoot);
114126

115-
schema = schema.replaceAll('$projectRoot', projectRoot);
116-
117127
let zmodelPath = path.join(projectRoot, 'schema.zmodel');
118-
const content = addPrelude ? `${MODEL_PRELUDE}\n${schema}` : schema;
119-
if (customSchemaFilePath) {
120-
zmodelPath = path.join(projectRoot, customSchemaFilePath);
121-
fs.mkdirSync(path.dirname(zmodelPath), { recursive: true });
122-
fs.writeFileSync(zmodelPath, content);
128+
129+
const files = schema.split(FILE_SPLITTER);
130+
131+
if (files.length > 1) {
132+
// multiple files
133+
files.forEach((file, index) => {
134+
//first line is the file name
135+
const firstLine = file.indexOf('\n');
136+
const fileName = file.substring(0, firstLine).trim();
137+
let fileContent = file.substring(firstLine + 1);
138+
if (index === 0) {
139+
// The first file is the main schema file
140+
zmodelPath = path.join(projectRoot, fileName);
141+
if (addPrelude) {
142+
// plugin need to be added after import statement
143+
fileContent = `${fileContent}\n${MODEL_PRELUDE}`;
144+
}
145+
}
146+
147+
fileContent = fileContent.replaceAll('$projectRoot', projectRoot);
148+
const filePath = path.join(projectRoot, fileName);
149+
fs.writeFileSync(filePath, fileContent);
150+
});
123151
} else {
124-
fs.writeFileSync('schema.zmodel', content);
152+
schema = schema.replaceAll('$projectRoot', projectRoot);
153+
const content = addPrelude ? `${MODEL_PRELUDE}\n${schema}` : schema;
154+
if (customSchemaFilePath) {
155+
zmodelPath = path.join(projectRoot, customSchemaFilePath);
156+
fs.mkdirSync(path.dirname(zmodelPath), { recursive: true });
157+
fs.writeFileSync(zmodelPath, content);
158+
} else {
159+
fs.writeFileSync('schema.zmodel', content);
160+
}
125161
}
162+
126163
run('npm install');
127164

128165
if (customSchemaFilePath) {
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { loadSchema, FILE_SPLITTER } from '@zenstackhq/testtools';
2+
3+
describe('Multiple file test', () => {
4+
it('model loading', async () => {
5+
await loadSchema(
6+
`schema.zmodel
7+
import "post"
8+
${FILE_SPLITTER}post.zmodel
9+
import "user"
10+
model Post {
11+
id Int @id @default(autoincrement())
12+
owner User @relation(fields: [ownerId], references: [id])
13+
ownerId Int
14+
15+
// require login
16+
@@deny('all', auth() == null)
17+
18+
// can be read by owner or space members (only if not private)
19+
@@allow('all', owner == auth())
20+
}
21+
${FILE_SPLITTER}user.zmodel
22+
import "post"
23+
model User {
24+
id Int @id @default(autoincrement())
25+
name String
26+
email String @unique
27+
posts Post[]
28+
}
29+
`
30+
);
31+
});
32+
});

0 commit comments

Comments
 (0)