Skip to content

Commit 6e9d242

Browse files
authored
chore: disallow this in collection predicate (#1176)
1 parent 35b4be9 commit 6e9d242

File tree

4 files changed

+51
-6
lines changed

4 files changed

+51
-6
lines changed

packages/schema/src/language-server/validator/expression-validator.ts

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ import {
1313
isThisExpr,
1414
} from '@zenstackhq/language/ast';
1515
import { isAuthInvocation, isDataModelFieldReference, isEnumFieldReference } from '@zenstackhq/sdk';
16-
import { ValidationAcceptor } from 'langium';
17-
import { findUpAst, getContainingDataModel, isCollectionPredicate } from '../../utils/ast-utils';
16+
import { ValidationAcceptor, streamAst } from 'langium';
17+
import { findUpAst, getContainingDataModel } from '../../utils/ast-utils';
1818
import { AstValidator } from '../types';
1919
import { typeAssignable } from './utils';
2020

@@ -32,8 +32,6 @@ export default class ExpressionValidator implements AstValidator<Expression> {
3232
'auth() cannot be resolved because no model marked wth "@@auth()" or named "User" is found',
3333
{ node: expr }
3434
);
35-
} else if (isCollectionPredicate(expr)) {
36-
accept('error', 'collection predicate can only be used on an array of model type', { node: expr });
3735
} else {
3836
accept('error', 'expression cannot be resolved', {
3937
node: expr,
@@ -221,6 +219,29 @@ export default class ExpressionValidator implements AstValidator<Expression> {
221219
}
222220
break;
223221
}
222+
223+
case '?':
224+
case '!':
225+
case '^':
226+
this.validateCollectionPredicate(expr, accept);
227+
break;
228+
}
229+
}
230+
231+
private validateCollectionPredicate(expr: BinaryExpr, accept: ValidationAcceptor) {
232+
if (!expr.$resolvedType) {
233+
accept('error', 'collection predicate can only be used on an array of model type', { node: expr });
234+
return;
235+
}
236+
237+
// TODO: revisit this when we implement lambda inside collection predicate
238+
const thisExpr = streamAst(expr).find(isThisExpr);
239+
if (thisExpr) {
240+
accept(
241+
'error',
242+
'using `this` in collection predicate is not supported. To compare entity identity, use id field comparison instead.',
243+
{ node: thisExpr }
244+
);
224245
}
225246
}
226247

packages/schema/tests/schema/validation/datamodel-validation.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,29 @@ describe('Data Model Validation Tests', () => {
7878
).toMatchObject(errorLike('Field of "Unsupported" type cannot be used in expressions'));
7979
});
8080

81+
it('Using `this` in collection predicate', async () => {
82+
expect(
83+
await safelyLoadModel(`
84+
${prelude}
85+
model User {
86+
id String @id
87+
members User[]
88+
@@allow('all', members?[this == auth()])
89+
}
90+
`)
91+
).toMatchObject(errorLike('using `this` in collection predicate is not supported'));
92+
93+
expect(
94+
await loadModel(`
95+
model User {
96+
id String @id
97+
members User[]
98+
@@allow('all', members?[id == auth().id])
99+
}
100+
`)
101+
).toBeTruthy();
102+
});
103+
81104
it('mix array and optional', async () => {
82105
expect(
83106
await safelyLoadModel(`

tests/integration/tests/regression/issue-925.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ describe('Regression: issue 925', () => {
3535
).resolves.toContain("Could not resolve reference to ReferenceTarget named 'test'.");
3636
});
3737

38-
it('reference with this', async () => {
38+
// eslint-disable-next-line jest/no-disabled-tests
39+
it.skip('reference with this', async () => {
3940
await loadModel(
4041
`
4142
model User {

tests/integration/tests/regression/issues.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -600,7 +600,7 @@ model User {
600600
// can be created by anyone, even not logged in
601601
@@allow('create', true)
602602
// can be read by users in the same organization
603-
@@allow('read', orgs?[members?[auth() == this]])
603+
@@allow('read', orgs?[members?[auth().id == id]])
604604
// full access by oneself
605605
@@allow('all', auth() == this)
606606
}

0 commit comments

Comments
 (0)