Skip to content

Commit 20087f6

Browse files
committed
fix enum in alias expression
1 parent c199967 commit 20087f6

File tree

2 files changed

+37
-17
lines changed

2 files changed

+37
-17
lines changed

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

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,13 @@ export class ZModelLinker extends DefaultLinker {
144144
}
145145

146146
private resolve(node: AstNode, document: LangiumDocument, extraScopes: ScopeProvider[] = []) {
147+
// if the field has enum declaration type, resolve the rest with that enum's fields on top of the scopes
148+
if (isDataModelField(node) && node.type.reference?.ref && isEnum(node.type.reference.ref)) {
149+
const contextEnum = node.type.reference.ref as Enum;
150+
const enumScope: ScopeProvider = (name) => contextEnum?.fields?.find((f) => f.name === name);
151+
extraScopes = [enumScope, ...extraScopes];
152+
}
153+
147154
switch (node.$type) {
148155
case StringLiteral:
149156
case NumberLiteral:
@@ -160,12 +167,7 @@ export class ZModelLinker extends DefaultLinker {
160167
break;
161168

162169
case ReferenceExpr:
163-
// If the reference comes from an alias, we resolve it against the first matching data model
164-
if (getContainerOfType(node, isAliasDecl)) {
165-
this.resolveAliasExpr(node, document);
166-
} else {
167-
this.resolveReference(node as ReferenceExpr, document, extraScopes);
168-
}
170+
this.resolveReference(node as ReferenceExpr, document, extraScopes);
169171
break;
170172

171173
case MemberAccessExpr:
@@ -265,6 +267,10 @@ export class ZModelLinker extends DefaultLinker {
265267
}
266268

267269
private resolveReference(node: ReferenceExpr, document: LangiumDocument<AstNode>, extraScopes: ScopeProvider[]) {
270+
// If the reference comes from an alias, we resolve it against the first matching data model
271+
if (getContainerOfType(node, isAliasDecl)) {
272+
this.resolveAliasExpr(node as ReferenceExpr, document);
273+
}
268274
this.resolveDefault(node, document, extraScopes);
269275

270276
if (node.target.ref) {
@@ -468,11 +474,6 @@ export class ZModelLinker extends DefaultLinker {
468474
// Find the first model that has the alias reference as a field
469475
const matchingModel = models.find((model) => model.fields.some((f) => f.name === node.$cstNode?.text));
470476
if (!matchingModel) {
471-
this.createLinkingError({
472-
reference: (node as ReferenceExpr).target,
473-
container: node,
474-
property: 'target',
475-
});
476477
return;
477478
}
478479

@@ -481,6 +482,11 @@ export class ZModelLinker extends DefaultLinker {
481482

482483
const visitExpr = (node: Expression) => {
483484
if (isReferenceExpr(node)) {
485+
// enums in alias expressions are already resolved
486+
if (isEnum(node.target.ref?.$container)) {
487+
return;
488+
}
489+
484490
const resolved = this.resolveFromScopeProviders(node, 'target', document, [scopeProvider]);
485491
if (resolved) {
486492
this.resolveToDeclaredType(node, (resolved as DataModelField).type);
@@ -585,6 +591,10 @@ export class ZModelLinker extends DefaultLinker {
585591
//#region Utils
586592

587593
private resolveToDeclaredType(node: AstNode, type: FunctionParamType | DataModelFieldType | TypeDefFieldType) {
594+
// enums from alias expressions are already resolved and do not exist in the scope
595+
if (!type) {
596+
return;
597+
}
588598
let nullable = false;
589599
if (isDataModelFieldType(type) || isTypeDefField(type)) {
590600
nullable = type.optional;

tests/integration/tests/plugins/policy.test.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -174,12 +174,22 @@ model M {
174174

175175
it('complex alias expressions', async () => {
176176
const model = `
177+
enum TaskStatus {
178+
TODO
179+
IN_PROGRESS
180+
DONE
181+
}
182+
183+
alias isInProgress() {
184+
status == IN_PROGRESS
185+
}
186+
177187
alias currentUserId() {
178188
auth().id
179189
}
180190
181191
alias complexAlias() {
182-
auth().cart.tasks?[id == 123] && value >10 && currentUserId() != null
192+
status == IN_PROGRESS && auth().cart.tasks?[id == 123] && value >10 && currentUserId() != null
183193
}
184194
185195
model User {
@@ -196,6 +206,7 @@ model M {
196206
197207
model Task {
198208
id Int @id @default(autoincrement())
209+
status TaskStatus @default(TODO)
199210
cart Cart @relation(fields: [cartId], references: [id])
200211
cartId Int
201212
value Int
@@ -213,12 +224,11 @@ model M {
213224
(policy.policy.task.modelLevel.read.guard as Function)({ user: { cart: { tasks: [{ id: 1 }] } } })
214225
).toEqual(
215226
expect.objectContaining({
216-
AND: [{ AND: [{ OR: [] }, { value: { gt: 10 } }] }, { OR: [] }],
227+
AND: [
228+
{ AND: [{ AND: [{ status: { equals: 'IN_PROGRESS' } }, { OR: [] }] }, { value: { gt: 10 } }] },
229+
{ OR: [] },
230+
],
217231
})
218232
);
219-
220-
expect(
221-
(policy.policy.task.modelLevel.read.guard as Function)({ user: { cart: { tasks: [{ id: 123 }] } } })
222-
).toEqual(expect.objectContaining({ AND: [{ AND: [{ AND: [] }, { value: { gt: 10 } }] }, { OR: [] }] }));
223233
});
224234
});

0 commit comments

Comments
 (0)