zenstack 0.1.42 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (215) hide show
  1. package/.vscode/extensions.json +7 -0
  2. package/.vscode/launch.json +49 -0
  3. package/.vscode/settings.json +4 -0
  4. package/README.md +1 -0
  5. package/package.json +8 -90
  6. package/packages/internal/jest.config.ts +32 -0
  7. package/packages/internal/package.json +42 -0
  8. package/packages/internal/src/constants.ts +1 -0
  9. package/packages/internal/src/handler/data/guard-utils.ts +7 -0
  10. package/packages/internal/src/handler/data/handler.ts +415 -0
  11. package/packages/internal/src/handler/data/query-processor.ts +504 -0
  12. package/packages/internal/src/handler/index.ts +1 -0
  13. package/packages/internal/src/handler/types.ts +20 -0
  14. package/packages/internal/src/index.ts +3 -0
  15. package/packages/internal/src/request-handler.ts +27 -0
  16. package/packages/internal/src/request.ts +101 -0
  17. package/packages/internal/src/types.ts +40 -0
  18. package/packages/internal/tests/query-processor.test.ts +172 -0
  19. package/{out/cli/tsconfig.template.json → packages/internal/tsconfig.json} +7 -3
  20. package/packages/runtime/auth.d.ts +1 -0
  21. package/packages/runtime/auth.js +3 -0
  22. package/packages/runtime/hooks.d.ts +10 -0
  23. package/packages/runtime/hooks.js +3 -0
  24. package/packages/runtime/index.d.ts +3 -0
  25. package/packages/runtime/index.js +1 -0
  26. package/packages/runtime/package-lock.json +512 -0
  27. package/packages/runtime/package.json +16 -0
  28. package/packages/runtime/server.d.ts +1 -0
  29. package/packages/runtime/server.js +3 -0
  30. package/packages/runtime/types.d.ts +1 -0
  31. package/packages/runtime/types.js +3 -0
  32. package/packages/schema/.eslintrc.json +13 -0
  33. package/packages/schema/.vscodeignore +4 -0
  34. package/packages/schema/asset/logo-dark.png +0 -0
  35. package/packages/schema/asset/logo-light.png +0 -0
  36. package/{bin → packages/schema/bin}/cli +0 -0
  37. package/packages/schema/jest.config.ts +32 -0
  38. package/packages/schema/langium-config.json +14 -0
  39. package/packages/schema/langium-quickstart.md +41 -0
  40. package/packages/schema/language-configuration.json +30 -0
  41. package/packages/schema/package.json +96 -0
  42. package/packages/schema/src/cli/cli-util.ts +80 -0
  43. package/packages/schema/src/cli/index.ts +64 -0
  44. package/packages/schema/src/extension.ts +76 -0
  45. package/packages/schema/src/generator/constants.ts +5 -0
  46. package/packages/schema/src/generator/index.ts +92 -0
  47. package/{out/generator/next-auth/index.js → packages/schema/src/generator/next-auth/index.ts} +46 -58
  48. package/{out → packages/schema/src}/generator/package.template.json +0 -0
  49. package/packages/schema/src/generator/prisma/expression-writer.ts +352 -0
  50. package/packages/schema/src/generator/prisma/index.ts +32 -0
  51. package/packages/schema/src/generator/prisma/plain-expression-builder.ts +91 -0
  52. package/packages/schema/src/generator/prisma/prisma-builder.ts +366 -0
  53. package/packages/schema/src/generator/prisma/query-gard-generator.ts +208 -0
  54. package/packages/schema/src/generator/prisma/schema-generator.ts +300 -0
  55. package/packages/schema/src/generator/react-hooks/index.ts +181 -0
  56. package/packages/schema/src/generator/service/index.ts +107 -0
  57. package/{out → packages/schema/src}/generator/tsconfig.template.json +0 -0
  58. package/packages/schema/src/generator/types.ts +17 -0
  59. package/packages/schema/src/generator/utils.ts +9 -0
  60. package/packages/schema/src/language-server/generated/ast.ts +603 -0
  61. package/{out/language-server/generated/grammar.js → packages/schema/src/language-server/generated/grammar.ts} +5 -8
  62. package/packages/schema/src/language-server/generated/module.ts +24 -0
  63. package/packages/schema/src/language-server/main.ts +12 -0
  64. package/{out → packages/schema/src}/language-server/stdlib.zmodel +0 -0
  65. package/packages/schema/src/language-server/types.ts +9 -0
  66. package/packages/schema/src/language-server/zmodel-index.ts +33 -0
  67. package/packages/schema/src/language-server/zmodel-linker.ts +409 -0
  68. package/packages/schema/src/language-server/zmodel-module.ts +90 -0
  69. package/packages/schema/src/language-server/zmodel-scope.ts +21 -0
  70. package/packages/schema/src/language-server/zmodel-validator.ts +35 -0
  71. package/packages/schema/src/language-server/zmodel.langium +186 -0
  72. package/packages/schema/src/utils/exec-utils.ts +5 -0
  73. package/packages/schema/src/utils/indent-string.ts +6 -0
  74. package/packages/schema/syntaxes/zmodel.json +57 -0
  75. package/packages/schema/syntaxes/zmodel.tmLanguage.json +57 -0
  76. package/packages/schema/tests/generator/expression-writer.test.ts +676 -0
  77. package/packages/schema/tests/generator/prisma-builder.test.ts +138 -0
  78. package/packages/schema/tests/schema/parser.test.ts +423 -0
  79. package/packages/schema/tests/schema/sample-todo.test.ts +14 -0
  80. package/packages/schema/tests/utils.ts +38 -0
  81. package/packages/schema/tsconfig.json +23 -0
  82. package/pnpm-workspace.yaml +3 -0
  83. package/samples/todo/.env +2 -0
  84. package/samples/todo/.eslintrc.json +3 -0
  85. package/samples/todo/.vscode/launch.json +11 -0
  86. package/samples/todo/README.md +34 -0
  87. package/samples/todo/components/AuthGuard.tsx +17 -0
  88. package/samples/todo/components/Avatar.tsx +22 -0
  89. package/samples/todo/components/BreadCrumb.tsx +44 -0
  90. package/samples/todo/components/ManageMembers.tsx +134 -0
  91. package/samples/todo/components/NavBar.tsx +57 -0
  92. package/samples/todo/components/SpaceMembers.tsx +76 -0
  93. package/samples/todo/components/Spaces.tsx +28 -0
  94. package/samples/todo/components/TimeInfo.tsx +17 -0
  95. package/samples/todo/components/Todo.tsx +72 -0
  96. package/samples/todo/components/TodoList.tsx +77 -0
  97. package/samples/todo/lib/context.ts +31 -0
  98. package/samples/todo/next.config.js +10 -0
  99. package/samples/todo/package-lock.json +7527 -0
  100. package/samples/todo/package.json +45 -0
  101. package/samples/todo/pages/_app.tsx +50 -0
  102. package/samples/todo/pages/api/auth/[...nextauth].ts +83 -0
  103. package/samples/todo/pages/api/zenstack/[...path].ts +16 -0
  104. package/samples/todo/pages/create-space.tsx +114 -0
  105. package/samples/todo/pages/index.tsx +32 -0
  106. package/samples/todo/pages/space/[slug]/[listId]/index.tsx +88 -0
  107. package/samples/todo/pages/space/[slug]/index.tsx +169 -0
  108. package/samples/todo/postcss.config.js +6 -0
  109. package/samples/todo/public/avatar.jpg +0 -0
  110. package/samples/todo/public/favicon.ico +0 -0
  111. package/samples/todo/public/logo.png +0 -0
  112. package/samples/todo/public/vercel.svg +4 -0
  113. package/samples/todo/styles/globals.css +7 -0
  114. package/samples/todo/tailwind.config.js +11 -0
  115. package/samples/todo/tsconfig.json +28 -0
  116. package/samples/todo/types/next-auth.d.ts +14 -0
  117. package/samples/todo/types/next.d.ts +16 -0
  118. package/samples/todo/zenstack/migrations/20221014084317_init/migration.sql +153 -0
  119. package/samples/todo/zenstack/migrations/20221020094651_upate_cli/migration.sql +23 -0
  120. package/samples/todo/zenstack/migrations/migration_lock.toml +3 -0
  121. package/samples/todo/zenstack/schema.prisma +126 -0
  122. package/samples/todo/zenstack/schema.zmodel +161 -0
  123. package/tests/integration/jest.config.ts +16 -0
  124. package/tests/integration/package-lock.json +1081 -0
  125. package/tests/integration/package.json +27 -0
  126. package/tests/integration/tests/operation-coverate.test.ts +563 -0
  127. package/tests/integration/tests/operations.zmodel +69 -0
  128. package/tests/integration/tests/todo-e2e.test.ts +577 -0
  129. package/tests/integration/tests/todo.zmodel +123 -0
  130. package/tests/integration/tests/tsconfig.template.json +10 -0
  131. package/tests/integration/tests/utils.ts +133 -0
  132. package/tests/integration/tsconfig.json +10 -0
  133. package/out/cli/cli-util.js +0 -64
  134. package/out/cli/cli-util.js.map +0 -1
  135. package/out/cli/generator.js +0 -1
  136. package/out/cli/generator.js.map +0 -1
  137. package/out/cli/index.js +0 -46
  138. package/out/cli/index.js.map +0 -1
  139. package/out/cli/package.template.json +0 -10
  140. package/out/extension.js +0 -81
  141. package/out/extension.js.map +0 -1
  142. package/out/generator/constants.js +0 -9
  143. package/out/generator/constants.js.map +0 -1
  144. package/out/generator/data-server/index.js +0 -1
  145. package/out/generator/data-server/index.js.map +0 -1
  146. package/out/generator/index.js +0 -98
  147. package/out/generator/index.js.map +0 -1
  148. package/out/generator/next-auth/index.js.map +0 -1
  149. package/out/generator/prisma/expression-writer.js +0 -287
  150. package/out/generator/prisma/expression-writer.js.map +0 -1
  151. package/out/generator/prisma/index.js +0 -38
  152. package/out/generator/prisma/index.js.map +0 -1
  153. package/out/generator/prisma/plain-expression-builder.js +0 -69
  154. package/out/generator/prisma/plain-expression-builder.js.map +0 -1
  155. package/out/generator/prisma/prisma-builder.js +0 -307
  156. package/out/generator/prisma/prisma-builder.js.map +0 -1
  157. package/out/generator/prisma/query-gard-generator.js +0 -159
  158. package/out/generator/prisma/query-gard-generator.js.map +0 -1
  159. package/out/generator/prisma/schema-generator.js +0 -201
  160. package/out/generator/prisma/schema-generator.js.map +0 -1
  161. package/out/generator/query-guard/index.js +0 -2
  162. package/out/generator/query-guard/index.js.map +0 -1
  163. package/out/generator/react-hooks/index.js +0 -179
  164. package/out/generator/react-hooks/index.js.map +0 -1
  165. package/out/generator/server/data/data-generator.js +0 -376
  166. package/out/generator/server/data/data-generator.js.map +0 -1
  167. package/out/generator/server/data/expression-writer.js +0 -287
  168. package/out/generator/server/data/expression-writer.js.map +0 -1
  169. package/out/generator/server/data/plain-expression-builder.js +0 -69
  170. package/out/generator/server/data/plain-expression-builder.js.map +0 -1
  171. package/out/generator/server/data-generator.js +0 -82
  172. package/out/generator/server/data-generator.js.map +0 -1
  173. package/out/generator/server/expression-writer.js +0 -1
  174. package/out/generator/server/expression-writer.js.map +0 -1
  175. package/out/generator/server/function/function-generator.js +0 -50
  176. package/out/generator/server/function/function-generator.js.map +0 -1
  177. package/out/generator/server/function-generator.js +0 -13
  178. package/out/generator/server/function-generator.js.map +0 -1
  179. package/out/generator/server/index.js +0 -88
  180. package/out/generator/server/index.js.map +0 -1
  181. package/out/generator/server/js-expression-builder.js +0 -1
  182. package/out/generator/server/js-expression-builder.js.map +0 -1
  183. package/out/generator/server/plain-expression-builder.js +0 -1
  184. package/out/generator/server/plain-expression-builder.js.map +0 -1
  185. package/out/generator/server/server-code-generator.js +0 -3
  186. package/out/generator/server/server-code-generator.js.map +0 -1
  187. package/out/generator/server/server-code-writer.js +0 -1
  188. package/out/generator/server/server-code-writer.js.map +0 -1
  189. package/out/generator/service/index.js +0 -133
  190. package/out/generator/service/index.js.map +0 -1
  191. package/out/generator/types.js +0 -10
  192. package/out/generator/types.js.map +0 -1
  193. package/out/generator/utils.js +0 -10
  194. package/out/generator/utils.js.map +0 -1
  195. package/out/language-server/generated/ast.js +0 -386
  196. package/out/language-server/generated/ast.js.map +0 -1
  197. package/out/language-server/generated/grammar.js.map +0 -1
  198. package/out/language-server/generated/module.js +0 -23
  199. package/out/language-server/generated/module.js.map +0 -1
  200. package/out/language-server/main.js +0 -12
  201. package/out/language-server/main.js.map +0 -1
  202. package/out/language-server/types.js +0 -3
  203. package/out/language-server/types.js.map +0 -1
  204. package/out/language-server/zmodel-index.js +0 -38
  205. package/out/language-server/zmodel-index.js.map +0 -1
  206. package/out/language-server/zmodel-linker.js +0 -241
  207. package/out/language-server/zmodel-linker.js.map +0 -1
  208. package/out/language-server/zmodel-module.js +0 -51
  209. package/out/language-server/zmodel-module.js.map +0 -1
  210. package/out/language-server/zmodel-scope.js +0 -30
  211. package/out/language-server/zmodel-scope.js.map +0 -1
  212. package/out/language-server/zmodel-validator.js +0 -25
  213. package/out/language-server/zmodel-validator.js.map +0 -1
  214. package/out/utils/indent-string.js +0 -9
  215. package/out/utils/indent-string.js.map +0 -1
@@ -0,0 +1,676 @@
1
+ import { Project, VariableDeclarationKind } from 'ts-morph';
2
+ import {
3
+ DataModel,
4
+ Enum,
5
+ Expression,
6
+ isDataModel,
7
+ isEnum,
8
+ } from '../../src/language-server/generated/ast';
9
+ import { loadModel } from '../utils';
10
+ import * as tmp from 'tmp';
11
+ import { GUARD_FIELD_NAME } from '../../src/generator/constants';
12
+ import expressionWriter from '../../src/generator/prisma/expression-writer';
13
+
14
+ async function check(
15
+ schema: string,
16
+ getExpr: (model: DataModel) => Expression,
17
+ expected: string
18
+ ) {
19
+ const model = await loadModel(schema);
20
+ const expr = getExpr(
21
+ model.declarations.find(
22
+ (d) => isDataModel(d) && d.name === 'Test'
23
+ ) as DataModel
24
+ );
25
+
26
+ const project = new Project();
27
+
28
+ const { name: sourcePath } = tmp.fileSync({ postfix: '.ts' });
29
+ const sf = project.createSourceFile(sourcePath, undefined, {
30
+ overwrite: true,
31
+ });
32
+
33
+ // inject user variable
34
+ sf.addVariableStatement({
35
+ declarationKind: VariableDeclarationKind.Const,
36
+ declarations: [{ name: 'user', initializer: '{ id: "user1" }' }],
37
+ });
38
+
39
+ // inject enums
40
+ model.declarations
41
+ .filter((d) => isEnum(d))
42
+ .map((e) => {
43
+ sf.addVariableStatement({
44
+ declarationKind: VariableDeclarationKind.Const,
45
+ declarations: [
46
+ {
47
+ name: e.name,
48
+ initializer: `
49
+ {
50
+ ${(e as Enum).fields
51
+ .map((f) => `${f.name}: "${f.name}"`)
52
+ .join(',\n')}
53
+ }
54
+ `,
55
+ },
56
+ ],
57
+ });
58
+ });
59
+
60
+ sf.addVariableStatement({
61
+ declarationKind: VariableDeclarationKind.Const,
62
+ declarations: [
63
+ {
64
+ name: 'expr',
65
+ initializer: (writer) =>
66
+ new expressionWriter(writer).write(expr),
67
+ },
68
+ ],
69
+ });
70
+ sf.formatText();
71
+
72
+ await project.save();
73
+
74
+ if (project.getPreEmitDiagnostics().length > 0) {
75
+ for (const d of project.getPreEmitDiagnostics()) {
76
+ console.warn(`${d.getLineNumber()}: ${d.getMessageText()}`);
77
+ }
78
+ console.log(`Generated source: ${sourcePath}`);
79
+ throw new Error('Compilation errors occurred');
80
+ }
81
+
82
+ const outExpr = sf.getVariableDeclaration('expr');
83
+ // console.log('Generated expr:\n', outExpr?.getText());
84
+
85
+ if (expected) {
86
+ const generatedExpr = outExpr!.getInitializer()!.getText();
87
+ expect(generatedExpr.replace(/\s+/g, '')).toBe(
88
+ expected.replace(/\s+/g, '')
89
+ );
90
+ }
91
+ }
92
+
93
+ describe('Expression Writer Tests', () => {
94
+ it('boolean literal', async () => {
95
+ await check(
96
+ `
97
+ model Test {
98
+ @@allow('all', true)
99
+ }
100
+ `,
101
+ (model) => model.attributes[0].args[1].value,
102
+ `{ ${GUARD_FIELD_NAME}: true }`
103
+ );
104
+
105
+ await check(
106
+ `
107
+ model Test {
108
+ @@allow('all', false)
109
+ }
110
+ `,
111
+ (model) => model.attributes[0].args[1].value,
112
+ `{ ${GUARD_FIELD_NAME}: false }`
113
+ );
114
+ });
115
+
116
+ it('boolean field', async () => {
117
+ await check(
118
+ `
119
+ model Test {
120
+ flag Boolean
121
+ @@allow('all', flag)
122
+ }
123
+ `,
124
+ (model) => model.attributes[0].args[1].value,
125
+ `{ flag: true }`
126
+ );
127
+
128
+ await check(
129
+ `
130
+ model Test {
131
+ flag Boolean
132
+ @@allow('all', !flag)
133
+ }
134
+ `,
135
+ (model) => model.attributes[0].args[1].value,
136
+ `{ NOT: { flag: true } }`
137
+ );
138
+ });
139
+
140
+ it('enum', async () => {
141
+ await check(
142
+ `
143
+ enum Role {
144
+ USER
145
+ ADMIN
146
+ }
147
+ model Test {
148
+ role Role
149
+ @@allow('all', role == ADMIN)
150
+ }
151
+ `,
152
+ (model) => model.attributes[0].args[1].value,
153
+ `{ role: { equals: Role.ADMIN } }`
154
+ );
155
+ });
156
+
157
+ it('field against literal', async () => {
158
+ await check(
159
+ `
160
+ model Test {
161
+ x Int
162
+ @@allow('all', x > 0)
163
+ }
164
+ `,
165
+ (model) => model.attributes[0].args[1].value,
166
+ `{
167
+ x: {
168
+ gt: 0
169
+ }
170
+ }`
171
+ );
172
+
173
+ await check(
174
+ `
175
+ model Test {
176
+ label String
177
+ @@allow('all', label == 'thing')
178
+ }
179
+ `,
180
+ (model) => model.attributes[0].args[1].value,
181
+ `{
182
+ label: {
183
+ equals: 'thing'
184
+ }
185
+ }`
186
+ );
187
+ });
188
+
189
+ it('this reference', async () => {
190
+ await check(
191
+ `
192
+ model Test {
193
+ id String @id
194
+ @@allow('all', auth() == this)
195
+ }
196
+ `,
197
+ (model) => model.attributes[0].args[1].value,
198
+ `{
199
+ id: {
200
+ equals: user?.id
201
+ }
202
+ }`
203
+ );
204
+
205
+ await check(
206
+ `
207
+ model Test {
208
+ id String @id
209
+ @@deny('all', this != auth())
210
+ }
211
+ `,
212
+ (model) => model.attributes[0].args[1].value,
213
+ `{
214
+ id: {
215
+ not: {
216
+ equals: user?.id
217
+ }
218
+ }
219
+ }`
220
+ );
221
+
222
+ await check(
223
+ `
224
+ model Test {
225
+ x Int
226
+ @@allow('all', this.x > 0)
227
+ }
228
+ `,
229
+ (model) => model.attributes[0].args[1].value,
230
+ `{
231
+ x: {
232
+ gt: 0
233
+ }
234
+ }`
235
+ );
236
+ });
237
+
238
+ it('logical', async () => {
239
+ await check(
240
+ `
241
+ model Test {
242
+ x Int
243
+ @@allow('all', x > 0 && x > 1)
244
+ }
245
+ `,
246
+ (model) => model.attributes[0].args[1].value,
247
+ `{
248
+ AND:
249
+ [
250
+ {
251
+ x : {
252
+ gt: 0
253
+ }
254
+ }
255
+ ,
256
+ {
257
+ x : {
258
+ gt: 1
259
+ }
260
+ }
261
+ ]
262
+ }`
263
+ );
264
+
265
+ await check(
266
+ `
267
+ model Test {
268
+ x Int
269
+ @@allow('all', x > 0 || x > 1)
270
+ }
271
+ `,
272
+ (model) => model.attributes[0].args[1].value,
273
+ `{
274
+ OR:
275
+ [
276
+ {
277
+ x : {
278
+ gt: 0
279
+ }
280
+ }
281
+ ,
282
+ {
283
+ x : {
284
+ gt: 1
285
+ }
286
+ }
287
+ ]
288
+ }`
289
+ );
290
+
291
+ await check(
292
+ `
293
+ model Test {
294
+ x Int
295
+ @@allow('all', x > 0 && x > 1 || x > 2)
296
+ }
297
+ `,
298
+ (model) => model.attributes[0].args[1].value,
299
+ `{
300
+ OR:
301
+ [
302
+ {
303
+ AND:
304
+ [
305
+ {
306
+ x : {
307
+ gt: 0
308
+ }
309
+ }
310
+ ,
311
+ {
312
+ x : {
313
+ gt: 1
314
+ }
315
+ }
316
+ ]
317
+ }
318
+ ,
319
+ {
320
+ x : {
321
+ gt: 2
322
+ }
323
+ }
324
+ ]
325
+ }`
326
+ );
327
+
328
+ await check(
329
+ `
330
+ model Test {
331
+ x Int
332
+ @@allow('all', !(x > 0 && x > 1 || !x > 2))
333
+ }
334
+ `,
335
+ (model) => model.attributes[0].args[1].value,
336
+ `{
337
+ NOT:
338
+ {
339
+ OR:
340
+ [
341
+ {
342
+ AND:
343
+ [
344
+ {
345
+ x : {
346
+ gt: 0
347
+ }
348
+ }
349
+ ,
350
+ {
351
+ x : {
352
+ gt: 1
353
+ }
354
+ }
355
+ ]
356
+ }
357
+ ,
358
+ {
359
+ NOT:
360
+ {
361
+ x : {
362
+ gt: 2
363
+ }
364
+ }
365
+ }
366
+ ]
367
+ }
368
+ }`
369
+ );
370
+ });
371
+
372
+ it('to-one relation query', async () => {
373
+ await check(
374
+ `
375
+ model Foo {
376
+ x Int
377
+ }
378
+
379
+ model Test {
380
+ foo Foo
381
+ @@deny('all', foo.x <= 0)
382
+ }
383
+ `,
384
+ (model) => model.attributes[0].args[1].value,
385
+ `{
386
+ foo: {
387
+ is: {
388
+ x : {
389
+ le: 0
390
+ }
391
+ }
392
+ }
393
+ }`
394
+ );
395
+
396
+ await check(
397
+ `
398
+ model Foo {
399
+ x Int
400
+ }
401
+
402
+ model Test {
403
+ foo Foo
404
+ @@deny('all', !(foo.x > 0))
405
+ }
406
+ `,
407
+ (model) => model.attributes[0].args[1].value,
408
+ `{
409
+ NOT:
410
+ {
411
+ foo: {
412
+ is: {
413
+ x : {
414
+ gt: 0
415
+ }
416
+ }
417
+ }
418
+ }
419
+ }`
420
+ );
421
+
422
+ await check(
423
+ `
424
+ model Foo {
425
+ x Boolean
426
+ }
427
+
428
+ model Test {
429
+ foo Foo
430
+ @@deny('all', !foo.x)
431
+ }
432
+ `,
433
+ (model) => model.attributes[0].args[1].value,
434
+ `{
435
+ NOT: {
436
+ foo: {
437
+ is: {
438
+ x: true
439
+ }
440
+ }
441
+ }
442
+ }`
443
+ );
444
+
445
+ await check(
446
+ `
447
+ model Foo {
448
+ bar Bar
449
+ }
450
+
451
+ model Bar {
452
+ x Int
453
+ }
454
+
455
+ model Test {
456
+ foo Foo
457
+ @@deny('all', foo.bar.x <= 0)
458
+ }
459
+ `,
460
+ (model) => model.attributes[0].args[1].value,
461
+ `{
462
+ foo: {
463
+ is: {
464
+ bar: {
465
+ is: {
466
+ x : {
467
+ le: 0
468
+ }
469
+ }
470
+ }
471
+ }
472
+ }
473
+ }`
474
+ );
475
+ });
476
+
477
+ it('to-many relation query', async () => {
478
+ await check(
479
+ `
480
+ model Foo {
481
+ x Int
482
+ }
483
+
484
+ model Test {
485
+ foos Foo[]
486
+ @@deny('all', foos?[x <= 0])
487
+ }
488
+ `,
489
+ (model) => model.attributes[0].args[1].value,
490
+ `{
491
+ foos: {
492
+ some: {
493
+ x: {
494
+ le: 0
495
+ }
496
+ }
497
+ }
498
+ }`
499
+ );
500
+
501
+ await check(
502
+ `
503
+ model Foo {
504
+ x Int
505
+ }
506
+
507
+ model Test {
508
+ foos Foo[]
509
+ @@deny('all', foos![x <= 0])
510
+ }
511
+ `,
512
+ (model) => model.attributes[0].args[1].value,
513
+ `{
514
+ foos: {
515
+ every: {
516
+ x: {
517
+ le: 0
518
+ }
519
+ }
520
+ }
521
+ }`
522
+ );
523
+
524
+ await check(
525
+ `
526
+ model Foo {
527
+ x Int
528
+ }
529
+
530
+ model Test {
531
+ foos Foo[]
532
+ @@deny('all', foos^[x <= 0])
533
+ }
534
+ `,
535
+ (model) => model.attributes[0].args[1].value,
536
+ `{
537
+ foos: {
538
+ none: {
539
+ x: {
540
+ le: 0
541
+ }
542
+ }
543
+ }
544
+ }`
545
+ );
546
+
547
+ await check(
548
+ `
549
+ model Foo {
550
+ bars Bar[]
551
+ }
552
+
553
+ model Bar {
554
+ x Int
555
+ }
556
+
557
+ model Test {
558
+ foo Foo
559
+ @@deny('all', foo.bars?[x <= 0])
560
+ }
561
+ `,
562
+ (model) => model.attributes[0].args[1].value,
563
+ `{
564
+ foo: {
565
+ is: {
566
+ bars: {
567
+ some: {
568
+ x: {
569
+ le: 0
570
+ }
571
+ }
572
+ }
573
+ }
574
+ }
575
+ }`
576
+ );
577
+ });
578
+
579
+ it('auth check', async () => {
580
+ await check(
581
+ `
582
+ model Test {
583
+ @@deny('all', auth() == null)
584
+ }
585
+ `,
586
+ (model) => model.attributes[0].args[1].value,
587
+ `{ ${GUARD_FIELD_NAME}: user == null }`
588
+ );
589
+
590
+ await check(
591
+ `
592
+ model Test {
593
+ @@allow('all', auth() != null)
594
+ }
595
+ `,
596
+ (model) => model.attributes[0].args[1].value,
597
+ `{ ${GUARD_FIELD_NAME}: user != null }`
598
+ );
599
+ });
600
+
601
+ it('auth check against field', async () => {
602
+ await check(
603
+ `
604
+ model User {
605
+ id String @id
606
+ }
607
+
608
+ model Test {
609
+ id String @id
610
+ owner User
611
+ @@allow('all', auth() == owner)
612
+ }
613
+ `,
614
+ (model) => model.attributes[0].args[1].value,
615
+ `{
616
+ owner: {
617
+ is: {
618
+ id: {
619
+ equals: user?.id
620
+ }
621
+ }
622
+ }
623
+ }`
624
+ );
625
+
626
+ await check(
627
+ `
628
+ model User {
629
+ id String @id
630
+ }
631
+
632
+ model Test {
633
+ id String @id
634
+ owner User
635
+ @@deny('all', auth() != owner)
636
+ }
637
+ `,
638
+ (model) => model.attributes[0].args[1].value,
639
+ `{
640
+ owner: {
641
+ is: {
642
+ id: {
643
+ not: {
644
+ equals: user?.id
645
+ }
646
+ }
647
+ }
648
+ }
649
+ }`
650
+ );
651
+
652
+ await check(
653
+ `
654
+ model User {
655
+ id String @id
656
+ }
657
+
658
+ model Test {
659
+ id String @id
660
+ owner User
661
+ @@allow('all', auth().id == owner.id)
662
+ }
663
+ `,
664
+ (model) => model.attributes[0].args[1].value,
665
+ `{
666
+ owner: {
667
+ is: {
668
+ id: {
669
+ equals: user?.id
670
+ }
671
+ }
672
+ }
673
+ }`
674
+ );
675
+ });
676
+ });