Skip to content

Commit cb50826

Browse files
authored
fix(zmodel): resolve auth() from all loaded and reachable documents (#1428)
1 parent bca13a7 commit cb50826

File tree

5 files changed

+92
-30
lines changed

5 files changed

+92
-30
lines changed

packages/schema/src/language-server/utils.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
import { DataModel, DataModelField, isArrayExpr, isReferenceExpr, ReferenceExpr } from '@zenstackhq/language/ast';
1+
import {
2+
isArrayExpr,
3+
isReferenceExpr,
4+
type DataModel,
5+
type DataModelField,
6+
type ReferenceExpr,
7+
} from '@zenstackhq/language/ast';
28
import { resolved } from '@zenstackhq/sdk';
39

410
/**

packages/schema/src/language-server/zmodel-linker.ts

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,7 @@ import {
3535
isReferenceExpr,
3636
isStringLiteral,
3737
} from '@zenstackhq/language/ast';
38-
import {
39-
getAuthModel,
40-
getContainingModel,
41-
getModelFieldsWithBases,
42-
isAuthInvocation,
43-
isFutureExpr,
44-
} from '@zenstackhq/sdk';
38+
import { getAuthModel, getModelFieldsWithBases, isAuthInvocation, isFutureExpr } from '@zenstackhq/sdk';
4539
import {
4640
AstNode,
4741
AstNodeDescription,
@@ -52,13 +46,14 @@ import {
5246
LangiumServices,
5347
LinkingError,
5448
Reference,
49+
getContainerOfType,
5550
interruptAndCheck,
5651
isReference,
5752
streamContents,
5853
} from 'langium';
5954
import { match } from 'ts-pattern';
6055
import { CancellationToken } from 'vscode-jsonrpc';
61-
import { getAllDataModelsIncludingImports, getContainingDataModel } from '../utils/ast-utils';
56+
import { getAllLoadedAndReachableDataModels, getContainingDataModel } from '../utils/ast-utils';
6257
import { mapBuiltinTypeToExpressionType } from './validator/utils';
6358

6459
interface DefaultReference extends Reference {
@@ -283,15 +278,17 @@ export class ZModelLinker extends DefaultLinker {
283278
// eslint-disable-next-line @typescript-eslint/ban-types
284279
const funcDecl = node.function.ref as FunctionDecl;
285280
if (isAuthInvocation(node)) {
286-
// auth() function is resolved to User model in the current document
287-
const model = getContainingModel(node);
288-
289-
if (model) {
290-
const allDataModels = getAllDataModelsIncludingImports(this.langiumDocuments(), model);
291-
const authModel = getAuthModel(allDataModels);
292-
if (authModel) {
293-
node.$resolvedType = { decl: authModel, nullable: true };
294-
}
281+
// auth() function is resolved against all loaded and reachable documents
282+
283+
// get all data models from loaded and reachable documents
284+
const allDataModels = getAllLoadedAndReachableDataModels(
285+
this.langiumDocuments(),
286+
getContainerOfType(node, isDataModel)
287+
);
288+
289+
const authModel = getAuthModel(allDataModels);
290+
if (authModel) {
291+
node.$resolvedType = { decl: authModel, nullable: true };
295292
}
296293
} else if (isFutureExpr(node)) {
297294
// future() function is resolved to current model

packages/schema/src/language-server/zmodel-scope.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import {
3232
import { match } from 'ts-pattern';
3333
import { CancellationToken } from 'vscode-jsonrpc';
3434
import {
35-
getAllDataModelsIncludingImports,
35+
getAllLoadedAndReachableDataModels,
3636
isCollectionPredicate,
3737
isFutureInvocation,
3838
resolveImportUri,
@@ -219,18 +219,18 @@ export class ZModelScopeProvider extends DefaultScopeProvider {
219219
}
220220

221221
private createScopeForAuthModel(node: AstNode, globalScope: Scope) {
222-
const model = getContainerOfType(node, isModel);
223-
if (model) {
224-
const allDataModels = getAllDataModelsIncludingImports(
225-
this.services.shared.workspace.LangiumDocuments,
226-
model
227-
);
228-
const authModel = getAuthModel(allDataModels);
229-
if (authModel) {
230-
return this.createScopeForModel(authModel, globalScope);
231-
}
222+
// get all data models from loaded and reachable documents
223+
const allDataModels = getAllLoadedAndReachableDataModels(
224+
this.services.shared.workspace.LangiumDocuments,
225+
getContainerOfType(node, isDataModel)
226+
);
227+
228+
const authModel = getAuthModel(allDataModels);
229+
if (authModel) {
230+
return this.createScopeForModel(authModel, globalScope);
231+
} else {
232+
return EMPTY_SCOPE;
232233
}
233-
return EMPTY_SCOPE;
234234
}
235235
}
236236

packages/schema/src/utils/ast-utils.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,3 +280,36 @@ export function findUpAst(node: AstNode, predicate: (node: AstNode) => boolean):
280280
}
281281
return undefined;
282282
}
283+
284+
/**
285+
* Gets all data models from all loaded documents
286+
*/
287+
export function getAllLoadedDataModels(langiumDocuments: LangiumDocuments) {
288+
return langiumDocuments.all
289+
.map((doc) => doc.parseResult.value as Model)
290+
.flatMap((model) => model.declarations.filter(isDataModel))
291+
.toArray();
292+
}
293+
294+
/**
295+
* Gets all data models from loaded and reachable documents
296+
*/
297+
export function getAllLoadedAndReachableDataModels(langiumDocuments: LangiumDocuments, fromModel?: DataModel) {
298+
// get all data models from loaded documents
299+
const allDataModels = getAllLoadedDataModels(langiumDocuments);
300+
301+
if (fromModel) {
302+
// merge data models transitively reached from the current model
303+
const model = getContainerOfType(fromModel, isModel);
304+
if (model) {
305+
const transitiveDataModels = getAllDataModelsIncludingImports(langiumDocuments, model);
306+
transitiveDataModels.forEach((dm) => {
307+
if (!allDataModels.includes(dm)) {
308+
allDataModels.push(dm);
309+
}
310+
});
311+
}
312+
}
313+
314+
return allDataModels;
315+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { FILE_SPLITTER, loadSchema } from '@zenstackhq/testtools';
2+
3+
describe('issue 1388', () => {
4+
it('regression', async () => {
5+
await loadSchema(
6+
`schema.zmodel
7+
import './auth'
8+
import './post'
9+
10+
${FILE_SPLITTER}auth.zmodel
11+
model User {
12+
id String @id @default(cuid())
13+
role String
14+
}
15+
16+
${FILE_SPLITTER}post.zmodel
17+
model Post {
18+
id String @id @default(nanoid(6))
19+
title String
20+
@@deny('all', auth() == null)
21+
@@allow('all', auth().id == 'user1')
22+
}
23+
`
24+
);
25+
});
26+
});

0 commit comments

Comments
 (0)