Skip to content

Commit bb87973

Browse files
authored
merge dev to main (v1.10.3) (#1106)
2 parents bda1551 + a543ffe commit bb87973

File tree

21 files changed

+381
-99
lines changed

21 files changed

+381
-99
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "zenstack-monorepo",
3-
"version": "1.10.2",
3+
"version": "1.10.3",
44
"description": "",
55
"scripts": {
66
"build": "pnpm -r build",

packages/ide/jetbrains/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ plugins {
99
}
1010

1111
group = "dev.zenstack"
12-
version = "1.10.2"
12+
version = "1.10.3"
1313

1414
repositories {
1515
mavenCentral()

packages/ide/jetbrains/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "jetbrains",
3-
"version": "1.10.2",
3+
"version": "1.10.3",
44
"displayName": "ZenStack JetBrains IDE Plugin",
55
"description": "ZenStack JetBrains IDE plugin",
66
"homepage": "https://zenstack.dev",

packages/language/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@zenstackhq/language",
3-
"version": "1.10.2",
3+
"version": "1.10.3",
44
"displayName": "ZenStack modeling language compiler",
55
"description": "ZenStack modeling language compiler",
66
"homepage": "https://zenstack.dev",

packages/misc/redwood/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@zenstackhq/redwood",
33
"displayName": "ZenStack RedwoodJS Integration",
4-
"version": "1.10.2",
4+
"version": "1.10.3",
55
"description": "CLI and runtime for integrating ZenStack with RedwoodJS projects.",
66
"repository": {
77
"type": "git",

packages/plugins/openapi/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@zenstackhq/openapi",
33
"displayName": "ZenStack Plugin and Runtime for OpenAPI",
4-
"version": "1.10.2",
4+
"version": "1.10.3",
55
"description": "ZenStack plugin and runtime supporting OpenAPI",
66
"main": "index.js",
77
"repository": {

packages/plugins/swr/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@zenstackhq/swr",
33
"displayName": "ZenStack plugin for generating SWR hooks",
4-
"version": "1.10.2",
4+
"version": "1.10.3",
55
"description": "ZenStack plugin for generating SWR hooks",
66
"main": "index.js",
77
"repository": {

packages/plugins/tanstack-query/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@zenstackhq/tanstack-query",
33
"displayName": "ZenStack plugin for generating tanstack-query hooks",
4-
"version": "1.10.2",
4+
"version": "1.10.3",
55
"description": "ZenStack plugin for generating tanstack-query hooks",
66
"main": "index.js",
77
"exports": {

packages/plugins/trpc/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@zenstackhq/trpc",
33
"displayName": "ZenStack plugin for tRPC",
4-
"version": "1.10.2",
4+
"version": "1.10.3",
55
"description": "ZenStack plugin for tRPC",
66
"main": "index.js",
77
"repository": {

packages/runtime/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@zenstackhq/runtime",
33
"displayName": "ZenStack Runtime Library",
4-
"version": "1.10.2",
4+
"version": "1.10.3",
55
"description": "Runtime of ZenStack for both client-side and server-side environments.",
66
"repository": {
77
"type": "git",

packages/schema/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"publisher": "zenstack",
44
"displayName": "ZenStack Language Tools",
55
"description": "Build scalable web apps with minimum code by defining authorization and validation rules inside the data schema that closer to the database",
6-
"version": "1.10.2",
6+
"version": "1.10.3",
77
"author": {
88
"name": "ZenStack Team"
99
},

packages/schema/src/language-server/validator/attribute-application-validator.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
isEnum,
1616
isReferenceExpr,
1717
} from '@zenstackhq/language/ast';
18-
import { isFutureExpr, isRelationshipField, resolved } from '@zenstackhq/sdk';
18+
import { isDataModelFieldReference, isFutureExpr, isRelationshipField, resolved } from '@zenstackhq/sdk';
1919
import { ValidationAcceptor, streamAst } from 'langium';
2020
import pluralize from 'pluralize';
2121
import { AstValidator } from '../types';
@@ -151,6 +151,19 @@ export default class AttributeApplicationValidator implements AstValidator<Attri
151151
}
152152
}
153153

154+
@check('@@validate')
155+
private _checkValidate(attr: AttributeApplication, accept: ValidationAcceptor) {
156+
const condition = attr.args[0]?.value;
157+
if (
158+
condition &&
159+
streamAst(condition).some(
160+
(node) => isDataModelFieldReference(node) && isDataModel(node.$resolvedType?.decl)
161+
)
162+
) {
163+
accept('error', `\`@@validate\` condition cannot use relation fields`, { node: condition });
164+
}
165+
}
166+
154167
private validatePolicyKinds(
155168
kind: string,
156169
candidates: string[],

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

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,17 @@ import {
33
Expression,
44
ExpressionType,
55
isDataModel,
6+
isDataModelAttribute,
7+
isDataModelField,
68
isEnum,
9+
isLiteralExpr,
710
isMemberAccessExpr,
811
isNullExpr,
912
isThisExpr,
10-
isDataModelField,
11-
isLiteralExpr,
1213
} from '@zenstackhq/language/ast';
1314
import { isDataModelFieldReference, isEnumFieldReference } from '@zenstackhq/sdk';
14-
import { ValidationAcceptor } from 'langium';
15-
import { getContainingDataModel, isAuthInvocation, isCollectionPredicate } from '../../utils/ast-utils';
15+
import { AstNode, ValidationAcceptor } from 'langium';
16+
import { findUpAst, getContainingDataModel, isAuthInvocation, isCollectionPredicate } from '../../utils/ast-utils';
1617
import { AstValidator } from '../types';
1718
import { typeAssignable } from './utils';
1819

@@ -123,6 +124,17 @@ export default class ExpressionValidator implements AstValidator<Expression> {
123124

124125
case '==':
125126
case '!=': {
127+
if (this.isInValidationContext(expr)) {
128+
// in validation context, all fields are optional, so we should allow
129+
// comparing any field against null
130+
if (
131+
(isDataModelFieldReference(expr.left) && isNullExpr(expr.right)) ||
132+
(isDataModelFieldReference(expr.right) && isNullExpr(expr.left))
133+
) {
134+
return;
135+
}
136+
}
137+
126138
if (!!expr.left.$resolvedType?.array !== !!expr.right.$resolvedType?.array) {
127139
accept('error', 'incompatible operand types', { node: expr });
128140
break;
@@ -132,18 +144,24 @@ export default class ExpressionValidator implements AstValidator<Expression> {
132144
// - foo.user.id == userId
133145
// except:
134146
// - future().userId == userId
135-
if(isMemberAccessExpr(expr.left) && isDataModelField(expr.left.member.ref) && expr.left.member.ref.$container != getContainingDataModel(expr)
136-
|| isMemberAccessExpr(expr.right) && isDataModelField(expr.right.member.ref) && expr.right.member.ref.$container != getContainingDataModel(expr))
137-
{
147+
if (
148+
(isMemberAccessExpr(expr.left) &&
149+
isDataModelField(expr.left.member.ref) &&
150+
expr.left.member.ref.$container != getContainingDataModel(expr)) ||
151+
(isMemberAccessExpr(expr.right) &&
152+
isDataModelField(expr.right.member.ref) &&
153+
expr.right.member.ref.$container != getContainingDataModel(expr))
154+
) {
138155
// foo.user.id == auth().id
139156
// foo.user.id == "123"
140157
// foo.user.id == null
141158
// foo.user.id == EnumValue
142-
if(!(this.isNotModelFieldExpr(expr.left) || this.isNotModelFieldExpr(expr.right)))
143-
{
144-
accept('error', 'comparison between fields of different models are not supported', { node: expr });
145-
break;
146-
}
159+
if (!(this.isNotModelFieldExpr(expr.left) || this.isNotModelFieldExpr(expr.right))) {
160+
accept('error', 'comparison between fields of different models are not supported', {
161+
node: expr,
162+
});
163+
break;
164+
}
147165
}
148166

149167
if (
@@ -205,14 +223,17 @@ export default class ExpressionValidator implements AstValidator<Expression> {
205223
}
206224
}
207225

226+
private isInValidationContext(node: AstNode) {
227+
return findUpAst(node, (n) => isDataModelAttribute(n) && n.decl.$refText === '@@validate');
228+
}
208229

209230
private isNotModelFieldExpr(expr: Expression) {
210-
return isLiteralExpr(expr) || isEnumFieldReference(expr) || isNullExpr(expr) || this.isAuthOrAuthMemberAccess(expr)
231+
return (
232+
isLiteralExpr(expr) || isEnumFieldReference(expr) || isNullExpr(expr) || this.isAuthOrAuthMemberAccess(expr)
233+
);
211234
}
212235

213236
private isAuthOrAuthMemberAccess(expr: Expression) {
214237
return isAuthInvocation(expr) || (isMemberAccessExpr(expr) && isAuthInvocation(expr.operand));
215238
}
216-
217239
}
218-

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

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,6 @@ export function isCollectionPredicate(node: AstNode): node is BinaryExpr {
157157
return isBinaryExpr(node) && ['?', '!', '^'].includes(node.operator);
158158
}
159159

160-
161160
export function getContainingDataModel(node: Expression): DataModel | undefined {
162161
let curr: AstNode | undefined = node.$container;
163162
while (curr) {
@@ -167,4 +166,18 @@ export function getContainingDataModel(node: Expression): DataModel | undefined
167166
curr = curr.$container;
168167
}
169168
return undefined;
170-
}
169+
}
170+
171+
/**
172+
* Walk upward from the current AST node to find the first node that satisfies the predicate.
173+
*/
174+
export function findUpAst(node: AstNode, predicate: (node: AstNode) => boolean): AstNode | undefined {
175+
let curr: AstNode | undefined = node;
176+
while (curr) {
177+
if (predicate(curr)) {
178+
return curr;
179+
}
180+
curr = curr.$container;
181+
}
182+
return undefined;
183+
}

0 commit comments

Comments
 (0)