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,27 @@
1
+ {
2
+ "name": "integration",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "jest --runInBand"
8
+ },
9
+ "keywords": [],
10
+ "author": "",
11
+ "license": "ISC",
12
+ "devDependencies": {
13
+ "@types/jest": "^29.0.3",
14
+ "@types/supertest": "^2.0.12",
15
+ "@types/tmp": "^0.2.3",
16
+ "jest": "^29.0.3",
17
+ "next": "^12.3.1",
18
+ "supertest": "^6.3.0",
19
+ "tmp": "^0.2.1",
20
+ "ts-jest": "^29.0.1",
21
+ "ts-node": "^10.9.1",
22
+ "typescript": "^4.6.2"
23
+ },
24
+ "dependencies": {
25
+ "@types/node": "^14.18.29"
26
+ }
27
+ }
@@ -0,0 +1,563 @@
1
+ import path from 'path';
2
+ import { makeClient, run, setup } from './utils';
3
+
4
+ describe('Operation Coverage Tests', () => {
5
+ let workDir: string;
6
+ let origDir: string;
7
+
8
+ beforeAll(async () => {
9
+ origDir = path.resolve('.');
10
+ workDir = await setup('./tests/operations.zmodel');
11
+ });
12
+
13
+ beforeEach(() => {
14
+ run('npx prisma migrate reset --schema ./zenstack/schema.prisma -f');
15
+ });
16
+
17
+ afterAll(() => {
18
+ process.chdir(origDir);
19
+ });
20
+
21
+ //#region Empty Policy
22
+
23
+ it('empty policy', async () => {
24
+ const client = makeClient('/api/data/EmptyPolicy');
25
+ await client.post('/').send({ data: {} }).expect(403);
26
+ await client
27
+ .get('/')
28
+ .expect((resp) => expect(resp.body).toHaveLength(0));
29
+ });
30
+
31
+ it('nested write empty policy to-many', async () => {
32
+ const client = makeClient('/api/data/M1');
33
+ await client
34
+ .post('/')
35
+ .send({
36
+ data: {
37
+ m2: {
38
+ create: [{}],
39
+ },
40
+ },
41
+ })
42
+ .expect(403);
43
+ });
44
+
45
+ it('nested write empty policy to-one', async () => {
46
+ const client = makeClient('/api/data/M1');
47
+ await client
48
+ .post('/')
49
+ .send({
50
+ data: {
51
+ m3: {
52
+ create: {},
53
+ },
54
+ },
55
+ })
56
+ .expect(403);
57
+ });
58
+
59
+ //#endregion
60
+
61
+ //#region Toplevel operations
62
+
63
+ it('toplevel find and get', async () => {
64
+ await makeClient('/api/data/M4')
65
+ .post('/')
66
+ .send({
67
+ data: {
68
+ id: '1',
69
+ value: 1,
70
+ },
71
+ })
72
+ .expect(201);
73
+
74
+ await makeClient('/api/data/M4')
75
+ .get('/')
76
+ .expect((resp) => expect(resp.body).toHaveLength(0));
77
+
78
+ await makeClient('/api/data/M4/1').get('/').expect(404);
79
+
80
+ await makeClient('/api/data/M4')
81
+ .post('/')
82
+ .send({
83
+ data: {
84
+ id: '2',
85
+ value: 2,
86
+ },
87
+ })
88
+ .expect(201);
89
+
90
+ await makeClient('/api/data/M4')
91
+ .get('/')
92
+ .expect((resp) => expect(resp.body).toHaveLength(1));
93
+
94
+ await makeClient('/api/data/M4/2').get('/').expect(200);
95
+ });
96
+
97
+ it('toplevel create, update and delete', async () => {
98
+ await makeClient('/api/data/M4')
99
+ .post('/')
100
+ .send({
101
+ data: {
102
+ value: 0,
103
+ },
104
+ })
105
+ .expect(403);
106
+
107
+ await makeClient('/api/data/M4')
108
+ .post('/')
109
+ .send({
110
+ data: {
111
+ id: '1',
112
+ value: 1,
113
+ },
114
+ })
115
+ .expect(201);
116
+
117
+ await makeClient('/api/data/M4/1')
118
+ .put('/')
119
+ .send({
120
+ data: {
121
+ value: 1,
122
+ },
123
+ })
124
+ .expect(403);
125
+
126
+ await makeClient('/api/data/M4')
127
+ .post('/')
128
+ .send({
129
+ data: {
130
+ id: '2',
131
+ value: 2,
132
+ },
133
+ })
134
+ .expect(201);
135
+
136
+ await makeClient('/api/data/M4/2')
137
+ .put('/')
138
+ .send({
139
+ data: {
140
+ value: 3,
141
+ },
142
+ })
143
+ .expect(200);
144
+
145
+ await makeClient('/api/data/M4/1').delete('/').expect(403);
146
+ await makeClient('/api/data/M4/2').delete('/').expect(200);
147
+ });
148
+
149
+ //#endregion
150
+
151
+ //#region Nested To-one
152
+
153
+ it('nested to-one writes', async () => {
154
+ await makeClient('/api/data/M5')
155
+ .post('/')
156
+ .send({
157
+ data: {
158
+ m6: {
159
+ create: {
160
+ value: 0,
161
+ },
162
+ },
163
+ },
164
+ })
165
+ .expect(403);
166
+
167
+ await makeClient('/api/data/M5')
168
+ .post('/')
169
+ .send({
170
+ data: {
171
+ id: '1',
172
+ m6: {
173
+ create: {
174
+ id: '1',
175
+ value: 1,
176
+ },
177
+ },
178
+ },
179
+ })
180
+ .expect(201);
181
+
182
+ await makeClient('/api/data/M5/1')
183
+ .put('/')
184
+ .send({
185
+ data: {
186
+ m6: {
187
+ update: {
188
+ value: 2,
189
+ },
190
+ },
191
+ },
192
+ })
193
+ .expect(403);
194
+
195
+ await makeClient('/api/data/M5')
196
+ .post('/')
197
+ .send({
198
+ data: {
199
+ id: '2',
200
+ m6: {
201
+ create: {
202
+ id: '2',
203
+ value: 2,
204
+ },
205
+ },
206
+ },
207
+ })
208
+ .expect(201);
209
+
210
+ await makeClient('/api/data/M5/2')
211
+ .put('/')
212
+ .send({
213
+ data: {
214
+ id: '2',
215
+ m6: {
216
+ delete: true,
217
+ },
218
+ },
219
+ })
220
+ .expect(403);
221
+
222
+ await makeClient('/api/data/M5/2')
223
+ .put('/')
224
+ .send({
225
+ data: {
226
+ m6: {
227
+ update: {
228
+ value: 3,
229
+ },
230
+ },
231
+ },
232
+ })
233
+ .expect(200);
234
+
235
+ await makeClient('/api/data/M6/2').get('/').expect(200);
236
+
237
+ await makeClient('/api/data/M5/2')
238
+ .put('/')
239
+ .send({
240
+ data: {
241
+ id: '2',
242
+ m6: {
243
+ delete: true,
244
+ },
245
+ },
246
+ })
247
+ .expect(200);
248
+
249
+ await makeClient('/api/data/M6/2').get('/').expect(404);
250
+ });
251
+
252
+ //#endregion
253
+
254
+ //#region Nested To-many
255
+
256
+ it('nested to-many create denied', async () => {
257
+ const client = makeClient('/api/data/M5');
258
+
259
+ // single-create
260
+ await client
261
+ .post('/')
262
+ .send({
263
+ data: {
264
+ m7: {
265
+ create: {
266
+ value: 0,
267
+ },
268
+ },
269
+ },
270
+ })
271
+ .expect(403);
272
+
273
+ // multi-create
274
+ await client
275
+ .post('/')
276
+ .send({
277
+ data: {
278
+ m7: {
279
+ create: [
280
+ {
281
+ value: 0,
282
+ },
283
+ {
284
+ value: 1,
285
+ },
286
+ ],
287
+ },
288
+ },
289
+ })
290
+ .expect(403);
291
+ });
292
+
293
+ it('nested to-many create allowed', async () => {
294
+ const client = makeClient('/api/data/M5');
295
+
296
+ // single-create
297
+ await client
298
+ .post('/')
299
+ .send({
300
+ data: {
301
+ m7: {
302
+ create: {
303
+ value: 1,
304
+ },
305
+ },
306
+ },
307
+ })
308
+ .expect(201);
309
+
310
+ // multi-create
311
+ await client
312
+ .post('/')
313
+ .send({
314
+ data: {
315
+ m7: {
316
+ create: [
317
+ {
318
+ value: 1,
319
+ },
320
+ {
321
+ value: 2,
322
+ },
323
+ ],
324
+ },
325
+ },
326
+ })
327
+ .expect(201);
328
+ });
329
+
330
+ it('nested to-many update', async () => {
331
+ // nested entities don't satisify policy before update, so should be excluded
332
+ await makeClient('/api/data/M5')
333
+ .post('/')
334
+ .send({
335
+ data: {
336
+ id: '1',
337
+ m7: {
338
+ create: [
339
+ {
340
+ id: '1',
341
+ value: 1,
342
+ },
343
+ ],
344
+ },
345
+ },
346
+ })
347
+ .expect(201);
348
+
349
+ await makeClient('/api/data/M5/1')
350
+ .put('/')
351
+ .send({
352
+ include: { m7: true },
353
+ data: {
354
+ m7: {
355
+ update: {
356
+ where: { id: '1' },
357
+ data: { value: 2 },
358
+ },
359
+ },
360
+ },
361
+ })
362
+ .expect(200)
363
+ .expect((resp) => {
364
+ expect(resp.body.m7).toEqual(
365
+ expect.arrayContaining([
366
+ expect.objectContaining({ id: '1', value: 1 }),
367
+ ])
368
+ );
369
+ });
370
+
371
+ // nested entities satisify policy before update, so should be included for update
372
+ await makeClient('/api/data/M5')
373
+ .post('/')
374
+ .send({
375
+ data: {
376
+ id: '2',
377
+ m7: {
378
+ create: {
379
+ id: '2',
380
+ value: 2,
381
+ },
382
+ },
383
+ },
384
+ })
385
+ .expect(201);
386
+
387
+ await makeClient('/api/data/M5/2')
388
+ .put('/')
389
+ .send({
390
+ include: { m7: true },
391
+ data: {
392
+ m7: {
393
+ update: {
394
+ where: { id: '2' },
395
+ data: { value: 3 },
396
+ },
397
+ },
398
+ },
399
+ })
400
+ .expect(200)
401
+ .expect((resp) => {
402
+ expect(resp.body.m7).toEqual(
403
+ expect.arrayContaining([
404
+ expect.objectContaining({ id: '2', value: 3 }),
405
+ ])
406
+ );
407
+ });
408
+ });
409
+
410
+ it('nested to-many update with create', async () => {
411
+ await makeClient('/api/data/M5')
412
+ .post('/')
413
+ .send({
414
+ data: {
415
+ id: '1',
416
+ m7: {
417
+ create: {
418
+ value: 1,
419
+ },
420
+ },
421
+ },
422
+ })
423
+ .expect(201);
424
+
425
+ await makeClient('/api/data/M5/1')
426
+ .put('/')
427
+ .send({
428
+ data: {
429
+ m7: {
430
+ create: {
431
+ id: '2',
432
+ value: 0,
433
+ },
434
+ },
435
+ },
436
+ })
437
+ .expect(403);
438
+
439
+ await makeClient('/api/data/M5/1')
440
+ .put('/')
441
+ .send({
442
+ data: {
443
+ m7: {
444
+ create: [
445
+ {
446
+ value: 0,
447
+ },
448
+ {
449
+ value: 1,
450
+ },
451
+ ],
452
+ },
453
+ },
454
+ })
455
+ .expect(403);
456
+
457
+ await makeClient('/api/data/M5/1')
458
+ .put('/')
459
+ .send({
460
+ include: { m7: true },
461
+ data: {
462
+ m7: {
463
+ create: [
464
+ {
465
+ value: 1,
466
+ },
467
+ {
468
+ value: 2,
469
+ },
470
+ ],
471
+ },
472
+ },
473
+ })
474
+ .expect(200)
475
+ .expect((resp) => {
476
+ expect(resp.body.m7).toHaveLength(3);
477
+ });
478
+ });
479
+
480
+ it('nested to-many update with delete', async () => {
481
+ await makeClient('/api/data/M5')
482
+ .post('/')
483
+ .send({
484
+ data: {
485
+ id: '1',
486
+ m7: {
487
+ create: [
488
+ {
489
+ id: '1',
490
+ value: 1,
491
+ },
492
+ {
493
+ id: '2',
494
+ value: 2,
495
+ },
496
+ {
497
+ id: '3',
498
+ value: 3,
499
+ },
500
+ {
501
+ id: '4',
502
+ value: 4,
503
+ },
504
+ {
505
+ id: '5',
506
+ value: 5,
507
+ },
508
+ ],
509
+ },
510
+ },
511
+ })
512
+ .expect(201);
513
+
514
+ await makeClient('/api/data/M5/1')
515
+ .put('/')
516
+ .send({
517
+ data: {
518
+ m7: {
519
+ delete: { id: '1' },
520
+ },
521
+ },
522
+ })
523
+ .expect(403);
524
+
525
+ await makeClient('/api/data/M5/1')
526
+ .put('/')
527
+ .send({
528
+ data: {
529
+ m7: {
530
+ deleteMany: { OR: [{ id: '2' }, { id: '3' }] },
531
+ },
532
+ },
533
+ })
534
+ .expect(403);
535
+
536
+ await makeClient('/api/data/M5/1')
537
+ .put('/')
538
+ .send({
539
+ data: {
540
+ m7: {
541
+ delete: { id: '3' },
542
+ },
543
+ },
544
+ })
545
+ .expect(200);
546
+ await makeClient('/api/data/M7/3').get('/').expect(404);
547
+
548
+ await makeClient('/api/data/M5/1')
549
+ .put('/')
550
+ .send({
551
+ data: {
552
+ m7: {
553
+ deleteMany: { value: { gte: 4 } },
554
+ },
555
+ },
556
+ })
557
+ .expect(200);
558
+ await makeClient('/api/data/M7/4').get('/').expect(404);
559
+ await makeClient('/api/data/M7/5').get('/').expect(404);
560
+ });
561
+
562
+ //#endregion
563
+ });
@@ -0,0 +1,69 @@
1
+ datasource db {
2
+ provider = 'sqlite'
3
+ url = 'file:./operations.db'
4
+ }
5
+
6
+ model EmptyPolicy {
7
+ id String @id @default(uuid())
8
+ }
9
+
10
+ model M1 {
11
+ id String @id @default(uuid())
12
+ m2 M2[]
13
+ m3 M3?
14
+
15
+ @@allow('all', true)
16
+ }
17
+
18
+ model M2 {
19
+ id String @id @default(uuid())
20
+ m1 M1 @relation(fields: [m1Id], references:[id])
21
+ m1Id String
22
+ }
23
+
24
+ model M3 {
25
+ id String @id @default(uuid())
26
+ m1 M1 @relation(fields: [m1Id], references:[id])
27
+ m1Id String @unique
28
+ }
29
+
30
+ model M4 {
31
+ id String @id @default(uuid())
32
+ value Int
33
+
34
+ @@allow('read', value > 1)
35
+ @@allow('create', value > 0)
36
+ @@allow('update', value > 1)
37
+ @@allow('delete', value > 2)
38
+ }
39
+
40
+ model M5 {
41
+ id String @id @default(uuid())
42
+ m6 M6?
43
+ m7 M7[]
44
+
45
+ @@allow('all', true)
46
+ }
47
+
48
+ model M6 {
49
+ id String @id @default(uuid())
50
+ value Int
51
+ m5 M5 @relation(fields: [m5Id], references:[id])
52
+ m5Id String @unique
53
+
54
+ @@allow('read', true)
55
+ @@allow('create', value > 0)
56
+ @@allow('update', value > 1)
57
+ @@allow('delete', value > 2)
58
+ }
59
+
60
+ model M7 {
61
+ id String @id @default(uuid())
62
+ value Int
63
+ m5 M5 @relation(fields: [m5Id], references:[id])
64
+ m5Id String
65
+
66
+ @@allow('create', value > 0)
67
+ @@allow('update', value > 1)
68
+ @@allow('delete', value > 2)
69
+ }