wabe 0.6.9 → 0.6.11

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 (162) hide show
  1. package/README.md +156 -50
  2. package/bucket/b.txt +1 -0
  3. package/dev/index.ts +215 -0
  4. package/dist/authentication/Session.d.ts +4 -1
  5. package/dist/authentication/interface.d.ts +16 -0
  6. package/dist/cron/index.d.ts +0 -1
  7. package/dist/database/DatabaseController.d.ts +41 -13
  8. package/dist/database/interface.d.ts +1 -0
  9. package/dist/email/DevAdapter.d.ts +0 -1
  10. package/dist/email/interface.d.ts +1 -1
  11. package/dist/graphql/resolvers.d.ts +4 -2
  12. package/dist/hooks/index.d.ts +8 -2
  13. package/dist/index.d.ts +0 -1
  14. package/dist/index.js +32144 -32058
  15. package/dist/schema/Schema.d.ts +2 -1
  16. package/dist/server/index.d.ts +4 -2
  17. package/dist/utils/crypto.d.ts +7 -0
  18. package/dist/utils/helper.d.ts +5 -1
  19. package/generated/schema.graphql +22 -14
  20. package/generated/wabe.ts +4 -4
  21. package/package.json +23 -23
  22. package/src/authentication/OTP.test.ts +69 -0
  23. package/src/authentication/OTP.ts +64 -0
  24. package/src/authentication/Session.test.ts +629 -0
  25. package/src/authentication/Session.ts +493 -0
  26. package/src/authentication/defaultAuthentication.ts +209 -0
  27. package/src/authentication/index.ts +3 -0
  28. package/src/authentication/interface.ts +155 -0
  29. package/src/authentication/oauth/GitHub.test.ts +91 -0
  30. package/src/authentication/oauth/GitHub.ts +121 -0
  31. package/src/authentication/oauth/Google.test.ts +91 -0
  32. package/src/authentication/oauth/Google.ts +101 -0
  33. package/src/authentication/oauth/Oauth2Client.test.ts +219 -0
  34. package/src/authentication/oauth/Oauth2Client.ts +135 -0
  35. package/src/authentication/oauth/index.ts +2 -0
  36. package/src/authentication/oauth/utils.test.ts +33 -0
  37. package/src/authentication/oauth/utils.ts +27 -0
  38. package/src/authentication/providers/EmailOTP.test.ts +127 -0
  39. package/src/authentication/providers/EmailOTP.ts +84 -0
  40. package/src/authentication/providers/EmailPassword.test.ts +176 -0
  41. package/src/authentication/providers/EmailPassword.ts +116 -0
  42. package/src/authentication/providers/EmailPasswordSRP.test.ts +208 -0
  43. package/src/authentication/providers/EmailPasswordSRP.ts +179 -0
  44. package/src/authentication/providers/GitHub.ts +24 -0
  45. package/src/authentication/providers/Google.ts +24 -0
  46. package/src/authentication/providers/OAuth.test.ts +185 -0
  47. package/src/authentication/providers/OAuth.ts +106 -0
  48. package/src/authentication/providers/PhonePassword.test.ts +176 -0
  49. package/src/authentication/providers/PhonePassword.ts +115 -0
  50. package/src/authentication/providers/QRCodeOTP.test.ts +77 -0
  51. package/src/authentication/providers/QRCodeOTP.ts +58 -0
  52. package/src/authentication/providers/index.ts +6 -0
  53. package/src/authentication/resolvers/refreshResolver.test.ts +30 -0
  54. package/src/authentication/resolvers/refreshResolver.ts +19 -0
  55. package/src/authentication/resolvers/signInWithResolver.inte.test.ts +59 -0
  56. package/src/authentication/resolvers/signInWithResolver.test.ts +293 -0
  57. package/src/authentication/resolvers/signInWithResolver.ts +92 -0
  58. package/src/authentication/resolvers/signOutResolver.test.ts +38 -0
  59. package/src/authentication/resolvers/signOutResolver.ts +18 -0
  60. package/src/authentication/resolvers/signUpWithResolver.test.ts +180 -0
  61. package/src/authentication/resolvers/signUpWithResolver.ts +65 -0
  62. package/src/authentication/resolvers/verifyChallenge.test.ts +133 -0
  63. package/src/authentication/resolvers/verifyChallenge.ts +62 -0
  64. package/src/authentication/roles.test.ts +49 -0
  65. package/src/authentication/roles.ts +40 -0
  66. package/src/authentication/utils.test.ts +97 -0
  67. package/src/authentication/utils.ts +39 -0
  68. package/src/cache/InMemoryCache.test.ts +62 -0
  69. package/src/cache/InMemoryCache.ts +45 -0
  70. package/src/cron/index.test.ts +17 -0
  71. package/src/cron/index.ts +43 -0
  72. package/src/database/DatabaseController.test.ts +613 -0
  73. package/src/database/DatabaseController.ts +1007 -0
  74. package/src/database/index.test.ts +1372 -0
  75. package/src/database/index.ts +9 -0
  76. package/src/database/interface.ts +302 -0
  77. package/src/email/DevAdapter.ts +7 -0
  78. package/src/email/EmailController.test.ts +29 -0
  79. package/src/email/EmailController.ts +13 -0
  80. package/src/email/index.ts +2 -0
  81. package/src/email/interface.ts +36 -0
  82. package/src/email/templates/sendOtpCode.ts +120 -0
  83. package/src/file/FileController.ts +28 -0
  84. package/src/file/FileDevAdapter.ts +51 -0
  85. package/src/file/hookDeleteFile.ts +25 -0
  86. package/src/file/hookReadFile.ts +66 -0
  87. package/src/file/hookUploadFile.ts +50 -0
  88. package/src/file/index.test.ts +932 -0
  89. package/src/file/index.ts +2 -0
  90. package/src/file/interface.ts +39 -0
  91. package/src/graphql/GraphQLSchema.test.ts +4408 -0
  92. package/src/graphql/GraphQLSchema.ts +880 -0
  93. package/src/graphql/index.ts +2 -0
  94. package/src/graphql/parseGraphqlSchema.ts +85 -0
  95. package/src/graphql/parser.test.ts +203 -0
  96. package/src/graphql/parser.ts +542 -0
  97. package/src/graphql/pointerAndRelationFunction.ts +191 -0
  98. package/src/graphql/resolvers.ts +442 -0
  99. package/src/graphql/tests/aggregation.test.ts +1115 -0
  100. package/src/graphql/tests/e2e.test.ts +590 -0
  101. package/src/graphql/tests/scalars.test.ts +250 -0
  102. package/src/graphql/types.ts +227 -0
  103. package/src/hooks/HookObject.test.ts +122 -0
  104. package/src/hooks/HookObject.ts +165 -0
  105. package/src/hooks/authentication.ts +67 -0
  106. package/src/hooks/createUser.test.ts +77 -0
  107. package/src/hooks/createUser.ts +10 -0
  108. package/src/hooks/defaultFields.test.ts +176 -0
  109. package/src/hooks/defaultFields.ts +32 -0
  110. package/src/hooks/deleteSession.test.ts +181 -0
  111. package/src/hooks/deleteSession.ts +20 -0
  112. package/src/hooks/hashFieldHook.test.ts +152 -0
  113. package/src/hooks/hashFieldHook.ts +89 -0
  114. package/src/hooks/index.test.ts +258 -0
  115. package/src/hooks/index.ts +414 -0
  116. package/src/hooks/permissions.test.ts +412 -0
  117. package/src/hooks/permissions.ts +93 -0
  118. package/src/hooks/protected.test.ts +551 -0
  119. package/src/hooks/protected.ts +60 -0
  120. package/src/hooks/searchableFields.test.ts +147 -0
  121. package/src/hooks/searchableFields.ts +86 -0
  122. package/src/hooks/session.test.ts +134 -0
  123. package/src/hooks/session.ts +76 -0
  124. package/src/hooks/setEmail.test.ts +216 -0
  125. package/src/hooks/setEmail.ts +33 -0
  126. package/src/hooks/setupAcl.test.ts +618 -0
  127. package/src/hooks/setupAcl.ts +25 -0
  128. package/src/index.ts +9 -0
  129. package/src/schema/Schema.test.ts +482 -0
  130. package/src/schema/Schema.ts +757 -0
  131. package/src/schema/defaultResolvers.ts +93 -0
  132. package/src/schema/index.ts +1 -0
  133. package/src/schema/resolvers/meResolver.test.ts +62 -0
  134. package/src/schema/resolvers/meResolver.ts +10 -0
  135. package/src/schema/resolvers/resetPassword.test.ts +341 -0
  136. package/src/schema/resolvers/resetPassword.ts +63 -0
  137. package/src/schema/resolvers/sendEmail.test.ts +118 -0
  138. package/src/schema/resolvers/sendEmail.ts +21 -0
  139. package/src/schema/resolvers/sendOtpCode.test.ts +141 -0
  140. package/src/schema/resolvers/sendOtpCode.ts +52 -0
  141. package/src/security.test.ts +3434 -0
  142. package/src/server/defaultSessionHandler.test.ts +62 -0
  143. package/src/server/defaultSessionHandler.ts +105 -0
  144. package/src/server/generateCodegen.ts +433 -0
  145. package/src/server/index.test.ts +532 -0
  146. package/src/server/index.ts +334 -0
  147. package/src/server/interface.ts +11 -0
  148. package/src/server/routes/authHandler.ts +169 -0
  149. package/src/server/routes/index.ts +39 -0
  150. package/src/utils/crypto.test.ts +41 -0
  151. package/src/utils/crypto.ts +105 -0
  152. package/src/utils/export.ts +11 -0
  153. package/src/utils/helper.ts +204 -0
  154. package/src/utils/index.test.ts +11 -0
  155. package/src/utils/index.ts +189 -0
  156. package/src/utils/preload.ts +8 -0
  157. package/src/utils/testHelper.ts +116 -0
  158. package/tsconfig.json +32 -0
  159. package/bunfig.toml +0 -4
  160. package/dist/ai/index.d.ts +0 -1
  161. package/dist/ai/interface.d.ts +0 -9
  162. /package/dist/server/{defaultHandlers.d.ts → defaultSessionHandler.d.ts} +0 -0
@@ -0,0 +1,4408 @@
1
+ import { beforeAll, describe, expect, it } from 'bun:test'
2
+ import getPort from 'get-port'
3
+ import { GraphQLObjectType, GraphQLSchema } from 'graphql'
4
+ import { gql } from 'graphql-request'
5
+ import { v4 as uuid } from 'uuid'
6
+ import { Schema, type SchemaInterface } from '../schema'
7
+ import { Wabe } from '../server'
8
+ import { type DevWabeTypes, getAnonymousClient, getGraphqlClient } from '../utils/helper'
9
+ import { GraphQLSchema as WabeGraphQLSchema } from './GraphQLSchema'
10
+ import { getTypeFromGraphQLSchema } from './parseGraphqlSchema'
11
+ import { getDatabaseAdapter } from '../utils/testHelper'
12
+
13
+ const createWabe = async (schema: SchemaInterface<DevWabeTypes>) => {
14
+ const databaseId = uuid()
15
+
16
+ const port = await getPort()
17
+
18
+ const wabe = new Wabe({
19
+ isProduction: false,
20
+ port,
21
+ schema,
22
+ rootKey: 'dev',
23
+ database: {
24
+ // @ts-expect-error
25
+ adapter: await getDatabaseAdapter(databaseId),
26
+ },
27
+ security: {
28
+ disableCSRFProtection: true,
29
+ },
30
+ })
31
+
32
+ await wabe.start()
33
+
34
+ const client = getGraphqlClient(port)
35
+
36
+ return { client, wabe, port }
37
+ }
38
+
39
+ describe('GraphqlSchema', () => {
40
+ let schema: GraphQLSchema
41
+
42
+ beforeAll(() => {
43
+ const wabeSchema = new Schema({
44
+ schema: {
45
+ classes: [
46
+ {
47
+ name: 'TestClass2',
48
+ fields: {
49
+ field1: {
50
+ type: 'Object',
51
+ required: true,
52
+ object: {
53
+ name: 'TestObject',
54
+ fields: {
55
+ testSubObject: {
56
+ type: 'Array',
57
+ typeValue: 'Object',
58
+ required: true,
59
+ object: {
60
+ name: 'FieldsObject',
61
+ required: true,
62
+ fields: {
63
+ name: {
64
+ type: 'String',
65
+ required: true,
66
+ },
67
+ },
68
+ },
69
+ },
70
+ },
71
+ },
72
+ },
73
+ },
74
+ },
75
+ {
76
+ name: 'TestClass',
77
+ fields: { field1: { type: 'String' } },
78
+ },
79
+ {
80
+ name: 'SecondClass',
81
+ fields: {
82
+ pointer: { type: 'Pointer', class: 'TestClass' },
83
+ },
84
+ },
85
+ {
86
+ name: 'ThirdClass',
87
+ fields: {
88
+ pointer: {
89
+ type: 'Pointer',
90
+ class: 'FourthClass',
91
+ },
92
+ },
93
+ },
94
+ {
95
+ name: 'FourthClass',
96
+ fields: {
97
+ pointer: {
98
+ type: 'Pointer',
99
+ class: 'ThirdClass',
100
+ },
101
+ },
102
+ },
103
+ {
104
+ name: 'FifthClass',
105
+ fields: {
106
+ relation: {
107
+ type: 'Relation',
108
+ class: 'SixthClass',
109
+ },
110
+ },
111
+ },
112
+ {
113
+ name: 'SixthClass',
114
+ fields: {
115
+ field6: { type: 'String' },
116
+ },
117
+ },
118
+ {
119
+ name: 'TestClassRequired',
120
+ fields: {
121
+ field7: {
122
+ type: 'String',
123
+ required: true,
124
+ },
125
+ field8: {
126
+ type: 'Array',
127
+ required: true,
128
+ requiredValue: true,
129
+ typeValue: 'Int',
130
+ },
131
+ field9: {
132
+ type: 'Array',
133
+ required: true,
134
+ typeValue: 'Object',
135
+ object: {
136
+ name: 'TestObjectArray',
137
+ fields: {
138
+ field10: {
139
+ type: 'Int',
140
+ required: true,
141
+ },
142
+ },
143
+ },
144
+ },
145
+ },
146
+ },
147
+ {
148
+ name: 'TestClassFile',
149
+ fields: {
150
+ file: {
151
+ type: 'File',
152
+ required: true,
153
+ },
154
+ },
155
+ },
156
+ ],
157
+ resolvers: {
158
+ mutations: {
159
+ customMutation: {
160
+ type: 'Boolean',
161
+ resolve: () => true,
162
+ },
163
+ mutationWithCustomTypes: {
164
+ type: 'Array',
165
+ typeValue: 'Object',
166
+ required: true,
167
+ typeValueRequired: true,
168
+ outputObject: {
169
+ name: 'TestMutation',
170
+ fields: {
171
+ name: {
172
+ type: 'String',
173
+ },
174
+ },
175
+ },
176
+ resolve: () => {
177
+ return [{ name: 'test' }]
178
+ },
179
+ },
180
+ },
181
+ queries: {
182
+ customQuery: {
183
+ type: 'Boolean',
184
+ resolve: () => true,
185
+ },
186
+ queryWithCustomTypes: {
187
+ type: 'Array',
188
+ typeValue: 'Object',
189
+ required: true,
190
+ typeValueRequired: true,
191
+ outputObject: {
192
+ name: 'TestQuery',
193
+ fields: {
194
+ name: {
195
+ type: 'String',
196
+ },
197
+ },
198
+ },
199
+ resolve: () => {
200
+ return [{ name: 'test' }]
201
+ },
202
+ },
203
+ },
204
+ },
205
+ },
206
+ } as any)
207
+
208
+ const graphqlSchema = new WabeGraphQLSchema(wabeSchema)
209
+
210
+ const types = graphqlSchema.createSchema()
211
+
212
+ schema = new GraphQLSchema({
213
+ query: new GraphQLObjectType({
214
+ name: 'Query',
215
+ fields: types.queries,
216
+ }),
217
+ mutation: new GraphQLObjectType({
218
+ name: 'Mutation',
219
+ fields: types.mutations,
220
+ }),
221
+ types: [...types.scalars, ...types.enums, ...types.objects],
222
+ })
223
+ })
224
+
225
+ it('should work with fragments in all operations (query, create, update, delete) and nested fragments', async () => {
226
+ const { wabe } = await createWabe({
227
+ classes: [
228
+ {
229
+ name: 'TestClass',
230
+ fields: {
231
+ field: {
232
+ type: 'String',
233
+ },
234
+ field1: {
235
+ type: 'String',
236
+ },
237
+ field2: {
238
+ type: 'String',
239
+ },
240
+ },
241
+ },
242
+ ],
243
+ })
244
+
245
+ const rootClient = getGraphqlClient(wabe.config.port)
246
+
247
+ // Define fragments for all operations
248
+ const fragments = `
249
+ fragment BasicFields on TestClass {
250
+ id
251
+ field
252
+ }
253
+
254
+ fragment ExtendedFields on TestClass {
255
+ ...BasicFields
256
+ field1
257
+ field2
258
+ }
259
+ `
260
+
261
+ // Test CREATE operation with fragments
262
+ const createResult = await rootClient.request<any>(gql`
263
+ ${fragments}
264
+ mutation createTestClass {
265
+ createTestClass(input: { fields: { field: "testField", field1: "value1", field2: "value2" } }) {
266
+ testClass {
267
+ ...ExtendedFields
268
+ }
269
+ }
270
+ }
271
+ `)
272
+
273
+ expect(createResult.createTestClass.testClass.field).toBe('testField')
274
+ expect(createResult.createTestClass.testClass.field1).toBe('value1')
275
+ expect(createResult.createTestClass.testClass.field2).toBe('value2')
276
+ expect(createResult.createTestClass.testClass.id).toBeDefined()
277
+
278
+ const objectId = createResult.createTestClass.testClass.id
279
+
280
+ // Test QUERY operation with fragments (simple fragment)
281
+ const queryResult = await rootClient.request<any>(gql`
282
+ fragment Test on TestClass {
283
+ id
284
+ field
285
+ }
286
+
287
+ query testClasses {
288
+ testClasses {
289
+ edges {
290
+ node {
291
+ ...Test
292
+ }
293
+ }
294
+ }
295
+ }
296
+ `)
297
+
298
+ expect(queryResult.testClasses.edges[0].node.field).toBe('testField')
299
+ expect(queryResult.testClasses.edges[0].node.id).toBe(objectId)
300
+
301
+ // Test UPDATE operation with fragments
302
+ const updateResult = await rootClient.request<any>(gql`
303
+ ${fragments}
304
+ mutation updateTestClass {
305
+ updateTestClass(
306
+ input: {
307
+ id: "${objectId}"
308
+ fields: { field: "updatedField", field1: "updated1", field2: "updated2" }
309
+ }
310
+ ) {
311
+ testClass {
312
+ ...ExtendedFields
313
+ }
314
+ }
315
+ }
316
+ `)
317
+
318
+ expect(updateResult.updateTestClass.testClass.field).toBe('updatedField')
319
+ expect(updateResult.updateTestClass.testClass.field1).toBe('updated1')
320
+ expect(updateResult.updateTestClass.testClass.field2).toBe('updated2')
321
+ expect(updateResult.updateTestClass.testClass.id).toBe(objectId)
322
+
323
+ // Test DELETE operation with fragments
324
+ const deleteResult = await rootClient.request<any>(gql`
325
+ ${fragments}
326
+ mutation deleteTestClass {
327
+ deleteTestClass(
328
+ input: { id: "${objectId}" }
329
+ ) {
330
+ testClass {
331
+ ...ExtendedFields
332
+ }
333
+ }
334
+ }
335
+ `)
336
+
337
+ expect(deleteResult.deleteTestClass.testClass.field).toBe('updatedField')
338
+ expect(deleteResult.deleteTestClass.testClass.field1).toBe('updated1')
339
+ expect(deleteResult.deleteTestClass.testClass.field2).toBe('updated2')
340
+ expect(deleteResult.deleteTestClass.testClass.id).toBe(objectId)
341
+
342
+ await wabe.close()
343
+ })
344
+
345
+ it('should work with fragments in all operations (query, create, update, delete) and nested fragments', async () => {
346
+ const { wabe } = await createWabe({
347
+ classes: [
348
+ {
349
+ name: 'TestClass',
350
+ fields: {
351
+ field1: {
352
+ type: 'String',
353
+ },
354
+ field2: {
355
+ type: 'String',
356
+ },
357
+ field3: {
358
+ type: 'String',
359
+ },
360
+ field4: {
361
+ type: 'String',
362
+ },
363
+ },
364
+ },
365
+ ],
366
+ })
367
+
368
+ const rootClient = getGraphqlClient(wabe.config.port)
369
+
370
+ // Define fragments for all operations
371
+ const fragments = `
372
+ fragment BasicFields on TestClass {
373
+ id
374
+ field1
375
+ }
376
+
377
+ fragment ExtendedFields on TestClass {
378
+ ...BasicFields
379
+ field2
380
+ }
381
+
382
+ fragment FullFields on TestClass {
383
+ ...ExtendedFields
384
+ field3
385
+ }
386
+
387
+ fragment CompleteFields on TestClass {
388
+ ...FullFields
389
+ field4
390
+ }
391
+ `
392
+
393
+ // Test CREATE operation with fragments of fragments
394
+ const createResult = await rootClient.request<any>(gql`
395
+ ${fragments}
396
+ mutation createTestClass {
397
+ createTestClass(input: { fields: { field1: "created1", field2: "created2", field3: "created3", field4: "created4" } }) {
398
+ testClass {
399
+ ...CompleteFields
400
+ }
401
+ }
402
+ }
403
+ `)
404
+
405
+ expect(createResult.createTestClass.testClass.field1).toBe('created1')
406
+ expect(createResult.createTestClass.testClass.field2).toBe('created2')
407
+ expect(createResult.createTestClass.testClass.field3).toBe('created3')
408
+ expect(createResult.createTestClass.testClass.field4).toBe('created4')
409
+ expect(createResult.createTestClass.testClass.id).toBeDefined()
410
+
411
+ const objectId = createResult.createTestClass.testClass.id
412
+
413
+ // Test QUERY operation with fragments of fragments
414
+ const queryResult = await rootClient.request<any>(gql`
415
+ ${fragments}
416
+ query testClass {
417
+ testClass(id: "${objectId}") {
418
+ ...CompleteFields
419
+ }
420
+ }
421
+ `)
422
+
423
+ expect(queryResult.testClass.field1).toBe('created1')
424
+ expect(queryResult.testClass.field2).toBe('created2')
425
+ expect(queryResult.testClass.field3).toBe('created3')
426
+ expect(queryResult.testClass.field4).toBe('created4')
427
+ expect(queryResult.testClass.id).toBe(objectId)
428
+
429
+ // Test UPDATE operation with fragments of fragments
430
+ const updateResult = await rootClient.request<any>(gql`
431
+ ${fragments}
432
+ mutation updateTestClass {
433
+ updateTestClass(
434
+ input: {
435
+ id: "${objectId}"
436
+ fields: { field1: "updated1", field2: "updated2", field3: "updated3", field4: "updated4" }
437
+ }
438
+ ) {
439
+ testClass {
440
+ ...CompleteFields
441
+ }
442
+ }
443
+ }
444
+ `)
445
+
446
+ expect(updateResult.updateTestClass.testClass.field1).toBe('updated1')
447
+ expect(updateResult.updateTestClass.testClass.field2).toBe('updated2')
448
+ expect(updateResult.updateTestClass.testClass.field3).toBe('updated3')
449
+ expect(updateResult.updateTestClass.testClass.field4).toBe('updated4')
450
+ expect(updateResult.updateTestClass.testClass.id).toBe(objectId)
451
+
452
+ // Test DELETE operation with fragments of fragments
453
+ const deleteResult = await rootClient.request<any>(gql`
454
+ ${fragments}
455
+ mutation deleteTestClass {
456
+ deleteTestClass(input: { id: "${objectId}" }) {
457
+ testClass {
458
+ ...CompleteFields
459
+ }
460
+ }
461
+ }
462
+ `)
463
+
464
+ expect(deleteResult.deleteTestClass.testClass.field1).toBe('updated1')
465
+ expect(deleteResult.deleteTestClass.testClass.field2).toBe('updated2')
466
+ expect(deleteResult.deleteTestClass.testClass.field3).toBe('updated3')
467
+ expect(deleteResult.deleteTestClass.testClass.field4).toBe('updated4')
468
+ expect(deleteResult.deleteTestClass.testClass.id).toBe(objectId)
469
+
470
+ await wabe.close()
471
+ })
472
+
473
+ it('should work with fragments across related classes in all operations', async () => {
474
+ const { wabe } = await createWabe({
475
+ classes: [
476
+ {
477
+ name: 'PremierClasse',
478
+ fields: {
479
+ field1: {
480
+ type: 'String',
481
+ },
482
+ },
483
+ },
484
+ {
485
+ name: 'DeuxiemeClasse',
486
+ fields: {
487
+ field2: {
488
+ type: 'String',
489
+ },
490
+ premierClasses: {
491
+ type: 'Relation',
492
+ // @ts-expect-error
493
+ class: 'PremierClasse',
494
+ },
495
+ },
496
+ },
497
+ ],
498
+ })
499
+
500
+ const rootClient = getGraphqlClient(wabe.config.port)
501
+
502
+ // Define fragments for related classes
503
+ const fragments = `
504
+ fragment TestPremierClasse on PremierClasse {
505
+ id
506
+ field1
507
+ }
508
+
509
+ fragment TestDeuxiemeClasse on DeuxiemeClasse {
510
+ id
511
+ field2
512
+ premierClasses {
513
+ edges {
514
+ node {
515
+ ...TestPremierClasse
516
+ }
517
+ }
518
+ }
519
+ }
520
+ `
521
+
522
+ // Create a PremierClasse object first
523
+ const premierClasseResult = await rootClient.request<any>(gql`
524
+ mutation createPremierClasse {
525
+ createPremierClasse(input: { fields: { field1: "premierValue" } }) {
526
+ premierClasse {
527
+ id
528
+ field1
529
+ }
530
+ }
531
+ }
532
+ `)
533
+
534
+ const premierClasseId = premierClasseResult.createPremierClasse.premierClasse.id
535
+
536
+ // Test CREATE operation with fragments across related classes
537
+ const createResult = await rootClient.request<any>(gql`
538
+ ${fragments}
539
+ mutation createDeuxiemeClasse {
540
+ createDeuxiemeClasse(
541
+ input: {
542
+ fields: {
543
+ field2: "deuxiemeValue"
544
+ premierClasses: {
545
+ createAndAdd: [{ field1: "nestedPremierValue" }]
546
+ }
547
+ }
548
+ }
549
+ ) {
550
+ deuxiemeClasse {
551
+ ...TestDeuxiemeClasse
552
+ }
553
+ }
554
+ }
555
+ `)
556
+
557
+ expect(createResult.createDeuxiemeClasse.deuxiemeClasse.field2).toBe('deuxiemeValue')
558
+ expect(createResult.createDeuxiemeClasse.deuxiemeClasse.id).toBeDefined()
559
+ expect(createResult.createDeuxiemeClasse.deuxiemeClasse.premierClasses.edges.length).toBe(1)
560
+ expect(
561
+ createResult.createDeuxiemeClasse.deuxiemeClasse.premierClasses.edges[0].node.field1,
562
+ ).toBe('nestedPremierValue')
563
+ expect(
564
+ createResult.createDeuxiemeClasse.deuxiemeClasse.premierClasses.edges[0].node.id,
565
+ ).toBeDefined()
566
+
567
+ const deuxiemeClasseId = createResult.createDeuxiemeClasse.deuxiemeClasse.id
568
+
569
+ // Test QUERY operation with fragments across related classes
570
+ const queryResult = await rootClient.request<any>(gql`
571
+ ${fragments}
572
+ query deuxiemeClass {
573
+ deuxiemeClasse(id: "${deuxiemeClasseId}") {
574
+ ...TestDeuxiemeClasse
575
+ }
576
+ }
577
+ `)
578
+
579
+ expect(queryResult.deuxiemeClasse.field2).toBe('deuxiemeValue')
580
+ expect(queryResult.deuxiemeClasse.id).toBe(deuxiemeClasseId)
581
+ expect(queryResult.deuxiemeClasse.premierClasses.edges.length).toBe(1)
582
+ expect(queryResult.deuxiemeClasse.premierClasses.edges[0].node.field1).toBe(
583
+ 'nestedPremierValue',
584
+ )
585
+
586
+ // Test UPDATE operation with fragments across related classes
587
+ const updateResult = await rootClient.request<any>(gql`
588
+ ${fragments}
589
+ mutation updateDeuxiemeClasse {
590
+ updateDeuxiemeClasse(
591
+ input: {
592
+ id: "${deuxiemeClasseId}"
593
+ fields: {
594
+ field2: "updatedDeuxiemeValue"
595
+ premierClasses: {
596
+ add: ["${premierClasseId}"]
597
+ }
598
+ }
599
+ }
600
+ ) {
601
+ deuxiemeClasse {
602
+ ...TestDeuxiemeClasse
603
+ }
604
+ }
605
+ }
606
+ `)
607
+
608
+ expect(updateResult.updateDeuxiemeClasse.deuxiemeClasse.field2).toBe('updatedDeuxiemeValue')
609
+ expect(updateResult.updateDeuxiemeClasse.deuxiemeClasse.id).toBe(deuxiemeClasseId)
610
+ expect(updateResult.updateDeuxiemeClasse.deuxiemeClasse.premierClasses.edges.length).toBe(2)
611
+
612
+ // Test DELETE operation with fragments across related classes
613
+ const deleteResult = await rootClient.request<any>(gql`
614
+ ${fragments}
615
+ mutation deleteDeuxiemeClasse {
616
+ deleteDeuxiemeClasse(input: { id: "${deuxiemeClasseId}" }) {
617
+ deuxiemeClasse {
618
+ ...TestDeuxiemeClasse
619
+ }
620
+ }
621
+ }
622
+ `)
623
+
624
+ expect(deleteResult.deleteDeuxiemeClasse.deuxiemeClasse.field2).toBe('updatedDeuxiemeValue')
625
+ expect(deleteResult.deleteDeuxiemeClasse.deuxiemeClasse.id).toBe(deuxiemeClasseId)
626
+ expect(deleteResult.deleteDeuxiemeClasse.deuxiemeClasse.premierClasses.edges.length).toBe(2)
627
+
628
+ await wabe.close()
629
+ })
630
+
631
+ it('should be able to only get ok output on query / mutation that returns connection object', async () => {
632
+ const { wabe, client } = await createWabe({
633
+ classes: [
634
+ {
635
+ name: 'TestClass',
636
+ fields: {
637
+ field1: {
638
+ type: 'String',
639
+ },
640
+ },
641
+ permissions: {
642
+ read: {
643
+ authorizedRoles: [],
644
+ requireAuthentication: true,
645
+ },
646
+ },
647
+ },
648
+ ],
649
+ })
650
+
651
+ await client.request(gql`
652
+ mutation deleteTestClasses {
653
+ deleteTestClasses(input: { where: { field1: { equalTo: "field1" } } }) {
654
+ ok
655
+ }
656
+ }
657
+ `)
658
+
659
+ await client.request(gql`
660
+ query testsClasses {
661
+ testClasses {
662
+ ok
663
+ }
664
+ }
665
+ `)
666
+
667
+ await wabe.close()
668
+ })
669
+
670
+ it('should set correctly the where input on array field', async () => {
671
+ const { wabe } = await createWabe({
672
+ classes: [
673
+ {
674
+ name: 'TestClassOnly',
675
+ fields: {
676
+ field1: {
677
+ type: 'Object',
678
+ object: {
679
+ name: 'TestObject',
680
+ required: true,
681
+ fields: {
682
+ field1: {
683
+ type: 'Array',
684
+ typeValue: 'String',
685
+ required: true,
686
+ requiredValue: true,
687
+ },
688
+ },
689
+ },
690
+ },
691
+ },
692
+ },
693
+ ],
694
+ })
695
+
696
+ expect(
697
+ getTypeFromGraphQLSchema({
698
+ schema: wabe.config.graphqlSchema || ({} as any),
699
+ type: 'Type',
700
+ name: 'TestClassOnlyTestObjectWhereInput',
701
+ }).input.field1,
702
+ ).toEqual('ArrayWhereInput')
703
+
704
+ await wabe.close()
705
+ })
706
+
707
+ it('should set correctly the where input on object field', async () => {
708
+ const { wabe } = await createWabe({
709
+ classes: [
710
+ {
711
+ name: 'TestClassOnly',
712
+ fields: {
713
+ field1: {
714
+ type: 'Object',
715
+ object: {
716
+ name: 'TestObject',
717
+ required: true,
718
+ fields: {
719
+ field1: {
720
+ type: 'Object',
721
+ object: {
722
+ name: 'TestTata',
723
+ fields: {
724
+ field2: {
725
+ type: 'String',
726
+ },
727
+ },
728
+ },
729
+ },
730
+ },
731
+ },
732
+ },
733
+ },
734
+ },
735
+ ],
736
+ })
737
+
738
+ expect(
739
+ getTypeFromGraphQLSchema({
740
+ schema: wabe.config.graphqlSchema || ({} as any),
741
+ type: 'Type',
742
+ name: 'TestClassOnlyTestObjectWhereInput',
743
+ }).input.field1,
744
+ ).toEqual('TestClassOnlyTestObjectField1WhereInput')
745
+
746
+ expect(
747
+ getTypeFromGraphQLSchema({
748
+ schema: wabe.config.graphqlSchema || ({} as any),
749
+ type: 'Type',
750
+ name: 'TestClassOnlyTestObjectField1WhereInput',
751
+ }).input.field2,
752
+ ).toEqual('StringWhereInput')
753
+
754
+ await wabe.close()
755
+ })
756
+
757
+ it('should request totalCount on relation', async () => {
758
+ const { wabe } = await createWabe({
759
+ classes: [
760
+ {
761
+ name: 'TestClass1',
762
+ fields: {
763
+ field1: {
764
+ type: 'Relation',
765
+ // @ts-expect-error
766
+ class: 'TestClass2',
767
+ },
768
+ },
769
+ },
770
+ {
771
+ name: 'TestClass2',
772
+ fields: {
773
+ field2: {
774
+ type: 'String',
775
+ },
776
+ },
777
+ },
778
+ ],
779
+ })
780
+
781
+ const rootClient = getGraphqlClient(wabe.config.port)
782
+
783
+ const result1 = await rootClient.request<any>(gql`
784
+ mutation createTestClass1 {
785
+ createTestClass1(input: { fields: { field1: { createAndAdd: [{ field2: "field2" }] } } }) {
786
+ testClass1 {
787
+ id
788
+ }
789
+ }
790
+ }
791
+ `)
792
+
793
+ const result2 = await rootClient.request<any>(gql`
794
+ query testClass1 {
795
+ testClass1(id: "${result1.createTestClass1.testClass1.id}") {
796
+ field1 {
797
+ totalCount
798
+ }
799
+ }
800
+ }
801
+ `)
802
+
803
+ expect(result2.testClass1.field1.totalCount).toEqual(1)
804
+
805
+ await wabe.close()
806
+ })
807
+
808
+ it('should request relation object on single object query', async () => {
809
+ const { wabe } = await createWabe({
810
+ classes: [
811
+ {
812
+ name: 'TestClass1',
813
+ fields: {
814
+ field1: {
815
+ type: 'Relation',
816
+ // @ts-expect-error
817
+ class: 'TestClass2',
818
+ },
819
+ },
820
+ },
821
+ {
822
+ name: 'TestClass2',
823
+ fields: {
824
+ field2: {
825
+ type: 'String',
826
+ },
827
+ },
828
+ },
829
+ ],
830
+ })
831
+
832
+ const rootClient = getGraphqlClient(wabe.config.port)
833
+
834
+ const result1 = await rootClient.request<any>(gql`
835
+ mutation createTestClass1 {
836
+ createTestClass1(input: { fields: { field1: { createAndAdd: [{ field2: "field2" }] } } }) {
837
+ testClass1 {
838
+ id
839
+ }
840
+ }
841
+ }
842
+ `)
843
+
844
+ const result2 = await rootClient.request<any>(gql`
845
+ query testClass1 {
846
+ testClass1(id: "${result1.createTestClass1.testClass1.id}") {
847
+ field1 {
848
+ edges {
849
+ node {
850
+ field2
851
+ }
852
+ }
853
+ }
854
+ }
855
+ }
856
+ `)
857
+
858
+ expect(result2.testClass1.field1.edges[0].node.field2).toBe('field2')
859
+
860
+ await wabe.close()
861
+ })
862
+
863
+ it('should have FileInput input on Create and Update fields input when we use a File field', async () => {
864
+ const { wabe } = await createWabe({
865
+ classes: [
866
+ {
867
+ name: 'TestClass',
868
+ fields: {
869
+ file: {
870
+ type: 'File',
871
+ required: true,
872
+ },
873
+ },
874
+ },
875
+ ],
876
+ })
877
+
878
+ expect(
879
+ getTypeFromGraphQLSchema({
880
+ schema: wabe.config.graphqlSchema || ({} as any),
881
+ type: 'Type',
882
+ name: 'TestClassCreateFieldsInput',
883
+ }).input.file,
884
+ ).toEqual('FileInput!')
885
+
886
+ expect(
887
+ getTypeFromGraphQLSchema({
888
+ schema: wabe.config.graphqlSchema || ({} as any),
889
+ type: 'Type',
890
+ name: 'TestClassUpdateFieldsInput',
891
+ }).input.file,
892
+ ).toEqual('FileInput!')
893
+
894
+ await wabe.close()
895
+ })
896
+
897
+ it('should have FileInfo object on the wabe object when we use a File field', async () => {
898
+ const { wabe } = await createWabe({
899
+ classes: [
900
+ {
901
+ name: 'TestClass',
902
+ fields: {
903
+ file: {
904
+ type: 'File',
905
+ required: true,
906
+ },
907
+ },
908
+ },
909
+ ],
910
+ })
911
+
912
+ expect(
913
+ getTypeFromGraphQLSchema({
914
+ schema: wabe.config.graphqlSchema || ({} as any),
915
+ type: 'Type',
916
+ name: 'TestClass',
917
+ }).input.file,
918
+ ).toEqual('FileInfo!')
919
+
920
+ await wabe.close()
921
+ })
922
+
923
+ it('should be able to create a phone field that check correctly if the phone is valid across the world', async () => {
924
+ const { client, wabe } = await createWabe({
925
+ classes: [
926
+ {
927
+ name: 'TestClass',
928
+ fields: {
929
+ phone: {
930
+ type: 'Phone',
931
+ },
932
+ },
933
+ },
934
+ ],
935
+ })
936
+
937
+ // French mobile valid
938
+ await client.request<any>(gql`
939
+ mutation createTestClass {
940
+ createTestClass(input: { fields: { phone: "+33612345678" } }) {
941
+ testClass {
942
+ phone
943
+ }
944
+ }
945
+ }
946
+ `)
947
+
948
+ // USA californian valid
949
+ await client.request<any>(gql`
950
+ mutation createTestClass {
951
+ createTestClass(input: { fields: { phone: "+14155552671" } }) {
952
+ testClass {
953
+ phone
954
+ }
955
+ }
956
+ }
957
+ `)
958
+
959
+ // Brasil mobile valid
960
+ await client.request<any>(gql`
961
+ mutation createTestClass {
962
+ createTestClass(input: { fields: { phone: "+5511998765432" } }) {
963
+ testClass {
964
+ phone
965
+ }
966
+ }
967
+ }
968
+ `)
969
+
970
+ // French mobile not valid
971
+ expect(
972
+ client.request<any>(gql`
973
+ mutation createTestClass {
974
+ createTestClass(input: { fields: { phone: "+3361234578" } }) {
975
+ testClass {
976
+ phone
977
+ }
978
+ }
979
+ }
980
+ `),
981
+ ).rejects.toThrow('Expected value of type "Phone", found "+3361234578"')
982
+
983
+ // USA californian not valid
984
+ expect(
985
+ client.request<any>(gql`
986
+ mutation createTestClass {
987
+ createTestClass(input: { fields: { phone: "+1415555267" } }) {
988
+ testClass {
989
+ phone
990
+ }
991
+ }
992
+ }
993
+ `),
994
+ ).rejects.toThrow('Expected value of type "Phone", found "+1415555267"')
995
+
996
+ // Brasil mobile not valid
997
+ expect(
998
+ client.request<any>(gql`
999
+ mutation createTestClass {
1000
+ createTestClass(input: { fields: { phone: "+5511234567" } }) {
1001
+ testClass {
1002
+ phone
1003
+ }
1004
+ }
1005
+ }
1006
+ `),
1007
+ ).rejects.toThrow('Expected value of type "Phone", found "+5511234567"')
1008
+
1009
+ await wabe.close()
1010
+ })
1011
+
1012
+ it('should be able to create an array in an object', async () => {
1013
+ const { wabe } = await createWabe({
1014
+ classes: [
1015
+ {
1016
+ name: 'TestClass',
1017
+ fields: {
1018
+ field1: {
1019
+ type: 'Array',
1020
+ typeValue: 'Object',
1021
+ object: {
1022
+ name: 'SubObject',
1023
+ fields: {
1024
+ field2: {
1025
+ type: 'Array',
1026
+ typeValue: 'String',
1027
+ required: true,
1028
+ requiredValue: false,
1029
+ },
1030
+ field3: { type: 'Int' },
1031
+ },
1032
+ },
1033
+ },
1034
+ },
1035
+ },
1036
+ ],
1037
+ })
1038
+
1039
+ expect(
1040
+ getTypeFromGraphQLSchema({
1041
+ schema: wabe.config.graphqlSchema || ({} as any),
1042
+ type: 'Type',
1043
+ name: 'TestClassSubObject',
1044
+ }).input,
1045
+ ).toEqual({
1046
+ field2: '[String]!',
1047
+ field3: 'Int',
1048
+ })
1049
+
1050
+ await wabe.close()
1051
+ })
1052
+
1053
+ // It is useful when we have the permission to create but not to read the data
1054
+ // We should be able to create a new object without return any data
1055
+ // Just use the "ok" field
1056
+ it('should be able to create a new object with mutation without return any data', async () => {
1057
+ const { wabe } = await createWabe({
1058
+ classes: [
1059
+ {
1060
+ name: 'TestClass',
1061
+ fields: {
1062
+ name: {
1063
+ type: 'String',
1064
+ },
1065
+ },
1066
+ permissions: {
1067
+ // Anyone can create a new object
1068
+ create: {
1069
+ requireAuthentication: false,
1070
+ },
1071
+ // No one can read the data
1072
+ read: {
1073
+ authorizedRoles: [],
1074
+ requireAuthentication: true,
1075
+ },
1076
+ },
1077
+ },
1078
+ ],
1079
+ })
1080
+
1081
+ const anonymousClient = getAnonymousClient(wabe.config.port)
1082
+
1083
+ const res = await anonymousClient.request<any>(
1084
+ gql`
1085
+ mutation createTestClass {
1086
+ createTestClass(input: { fields: { name: "A" } }) {
1087
+ ok
1088
+ }
1089
+ }
1090
+ `,
1091
+ {},
1092
+ )
1093
+
1094
+ expect(res.createTestClass.ok).toBe(true)
1095
+
1096
+ await wabe.close()
1097
+ })
1098
+
1099
+ it('should be able to update an object with mutation without return any data', async () => {
1100
+ const { client, wabe } = await createWabe({
1101
+ classes: [
1102
+ {
1103
+ name: 'TestClass',
1104
+ fields: {
1105
+ name: {
1106
+ type: 'String',
1107
+ },
1108
+ },
1109
+ permissions: {
1110
+ // Anyone can create a new object
1111
+ create: {
1112
+ requireAuthentication: false,
1113
+ },
1114
+ update: {
1115
+ requireAuthentication: false,
1116
+ },
1117
+ // No one can read the data
1118
+ read: {
1119
+ authorizedRoles: [],
1120
+ requireAuthentication: true,
1121
+ },
1122
+ },
1123
+ },
1124
+ ],
1125
+ })
1126
+
1127
+ const res = await client.request<any>(
1128
+ gql`
1129
+ mutation createTestClass {
1130
+ createTestClass(input: { fields: { name: "A" } }) {
1131
+ testClass {
1132
+ id
1133
+ }
1134
+ }
1135
+ }
1136
+ `,
1137
+ {},
1138
+ )
1139
+
1140
+ const anonymousClient = getAnonymousClient(wabe.config.port)
1141
+
1142
+ const res2 = await anonymousClient.request<any>(
1143
+ gql`
1144
+ mutation updateTestClass {
1145
+ updateTestClass(
1146
+ input: {
1147
+ fields: { name: "A" },
1148
+ id: "${res.createTestClass.testClass.id}"
1149
+ }
1150
+ ) {
1151
+ ok
1152
+ }
1153
+ }
1154
+ `,
1155
+ {},
1156
+ )
1157
+
1158
+ expect(res2.updateTestClass.ok).toBe(true)
1159
+
1160
+ await wabe.close()
1161
+ })
1162
+
1163
+ it('should be able to delete an object with mutation without return any data', async () => {
1164
+ const { client, wabe } = await createWabe({
1165
+ classes: [
1166
+ {
1167
+ name: 'TestClass',
1168
+ fields: {
1169
+ name: {
1170
+ type: 'String',
1171
+ },
1172
+ },
1173
+ permissions: {
1174
+ create: {
1175
+ requireAuthentication: false,
1176
+ },
1177
+ delete: {
1178
+ requireAuthentication: false,
1179
+ },
1180
+ },
1181
+ },
1182
+ ],
1183
+ })
1184
+
1185
+ const res = await client.request<any>(
1186
+ gql`
1187
+ mutation createTestClass {
1188
+ createTestClass(input: { fields: { name: "A" } }) {
1189
+ testClass {
1190
+ id
1191
+ }
1192
+ }
1193
+ }
1194
+ `,
1195
+ {},
1196
+ )
1197
+
1198
+ const anonymousClient = getAnonymousClient(wabe.config.port)
1199
+
1200
+ const res2 = await anonymousClient.request<any>(
1201
+ gql`
1202
+ mutation deleteTestClass {
1203
+ deleteTestClass(
1204
+ input: {
1205
+ id: "${res.createTestClass.testClass.id}"
1206
+ }
1207
+ ) {
1208
+ ok
1209
+ }
1210
+ }
1211
+ `,
1212
+ {},
1213
+ )
1214
+
1215
+ expect(res2.deleteTestClass.ok).toBe(true)
1216
+
1217
+ await wabe.close()
1218
+ })
1219
+
1220
+ it('should order the element in the query by name and age ASC using order enum', async () => {
1221
+ const { client, wabe } = await createWabe({
1222
+ classes: [
1223
+ {
1224
+ name: 'TestClass',
1225
+ fields: {
1226
+ name: {
1227
+ type: 'String',
1228
+ },
1229
+ age: {
1230
+ type: 'Int',
1231
+ },
1232
+ },
1233
+ },
1234
+ ],
1235
+ })
1236
+
1237
+ await client.request<any>(
1238
+ gql`
1239
+ mutation createTestClasses {
1240
+ createTestClasses(
1241
+ input: {
1242
+ fields: [
1243
+ { name: "A", age: 20 }
1244
+ { name: "B", age: 19 }
1245
+ { name: "C", age: 18 }
1246
+ { name: "D", age: 17 }
1247
+ ]
1248
+ }
1249
+ ) {
1250
+ edges {
1251
+ node {
1252
+ name
1253
+ }
1254
+ }
1255
+ }
1256
+ }
1257
+ `,
1258
+ {},
1259
+ )
1260
+
1261
+ const res = await client.request<any>(
1262
+ gql`
1263
+ query testClasses {
1264
+ testClasses(order: [name_DESC, age_ASC]) {
1265
+ edges {
1266
+ node {
1267
+ name
1268
+ }
1269
+ }
1270
+ }
1271
+ }
1272
+ `,
1273
+ {},
1274
+ )
1275
+
1276
+ expect(res.testClasses.edges[0].node.name).toBe('D')
1277
+ expect(res.testClasses.edges[1].node.name).toBe('C')
1278
+ expect(res.testClasses.edges[2].node.name).toBe('B')
1279
+ expect(res.testClasses.edges[3].node.name).toBe('A')
1280
+
1281
+ await wabe.close()
1282
+ })
1283
+
1284
+ it('should order the element in the query by name ASC using order enum', async () => {
1285
+ const { client, wabe } = await createWabe({
1286
+ classes: [
1287
+ {
1288
+ name: 'TestClass',
1289
+ fields: {
1290
+ name: {
1291
+ type: 'String',
1292
+ },
1293
+ },
1294
+ },
1295
+ ],
1296
+ })
1297
+
1298
+ await client.request<any>(
1299
+ gql`
1300
+ mutation createTestClasses {
1301
+ createTestClasses(
1302
+ input: { fields: [{ name: "test1" }, { name: "test2" }, { name: "test3" }, { name: "test4" }] }
1303
+ ) {
1304
+ edges {
1305
+ node {
1306
+ name
1307
+ }
1308
+ }
1309
+ }
1310
+ }
1311
+ `,
1312
+ {},
1313
+ )
1314
+
1315
+ const res = await client.request<any>(
1316
+ gql`
1317
+ query testClasses {
1318
+ testClasses(where: { name: { equalTo: "test1" } }, order: [name_ASC]) {
1319
+ edges {
1320
+ node {
1321
+ name
1322
+ }
1323
+ }
1324
+ }
1325
+ }
1326
+ `,
1327
+ {},
1328
+ )
1329
+
1330
+ expect(res.testClasses.edges[0].node.name).toBe('test1')
1331
+
1332
+ await wabe.close()
1333
+ })
1334
+
1335
+ it('should use the searchUsers to search all testClasses for corresponding term', async () => {
1336
+ const { client, wabe } = await createWabe({
1337
+ classes: [
1338
+ {
1339
+ name: 'TestClass',
1340
+ fields: {
1341
+ name: {
1342
+ type: 'String',
1343
+ },
1344
+ age: {
1345
+ type: 'Int',
1346
+ },
1347
+ },
1348
+ searchableFields: ['name'],
1349
+ },
1350
+ ],
1351
+ })
1352
+
1353
+ await client.request<any>(
1354
+ gql`
1355
+ mutation createTestClass {
1356
+ createTestClass(input: { fields: { name: "test", age: 30 } }) {
1357
+ testClass {
1358
+ name
1359
+ }
1360
+ }
1361
+ }
1362
+ `,
1363
+ {},
1364
+ )
1365
+
1366
+ const res = await client.request<any>(
1367
+ gql`
1368
+ query testClasses {
1369
+ testClasses(where: { AND: [{ age: { equalTo: 30 } }, { search: { contains: "t" } }] }) {
1370
+ totalCount
1371
+ }
1372
+ }
1373
+ `,
1374
+ )
1375
+
1376
+ expect(res.testClasses.totalCount).toEqual(1)
1377
+
1378
+ const res2 = await client.request<any>(
1379
+ gql`
1380
+ query testClasses {
1381
+ testClasses(where: { search: { contains: "invalid" } }) {
1382
+ totalCount
1383
+ }
1384
+ }
1385
+ `,
1386
+ )
1387
+
1388
+ expect(res2.testClasses.totalCount).toEqual(0)
1389
+
1390
+ const res3 = await client.request<any>(
1391
+ gql`
1392
+ query testClasses {
1393
+ testClasses(where: { search: { contains: "test" } }) {
1394
+ totalCount
1395
+ }
1396
+ }
1397
+ `,
1398
+ )
1399
+
1400
+ expect(res3.testClasses.totalCount).toEqual(1)
1401
+
1402
+ const res4 = await client.request<any>(
1403
+ gql`
1404
+ query testClasses {
1405
+ testClasses(where: { AND: [{ age: { equalTo: 1111 } }, { search: { contains: "test" } }] }) {
1406
+ totalCount
1407
+ }
1408
+ }
1409
+ `,
1410
+ )
1411
+
1412
+ expect(res4.testClasses.totalCount).toEqual(0)
1413
+
1414
+ const res5 = await client.request<any>(
1415
+ gql`
1416
+ query testClasses {
1417
+ testClasses(where: { AND: [{ age: { equalTo: 30 } }, { search: { contains: "" } }] }) {
1418
+ totalCount
1419
+ }
1420
+ }
1421
+ `,
1422
+ )
1423
+
1424
+ expect(res5.testClasses.totalCount).toEqual(1)
1425
+
1426
+ await wabe.close()
1427
+ })
1428
+
1429
+ it('should contain totalCount elements in query multiple objects', () => {
1430
+ expect(
1431
+ getTypeFromGraphQLSchema({
1432
+ schema,
1433
+ type: 'Type',
1434
+ name: 'TestClassConnection',
1435
+ }).input.totalCount,
1436
+ ).toEqual('Int')
1437
+ })
1438
+
1439
+ it('should totalCount all elements corresponding to where object', async () => {
1440
+ const { client, wabe } = await createWabe({
1441
+ classes: [
1442
+ {
1443
+ name: 'TestClass',
1444
+ fields: {
1445
+ field1: {
1446
+ type: 'Object',
1447
+ object: {
1448
+ name: 'SubObject',
1449
+ fields: {
1450
+ field2: { type: 'String' },
1451
+ field3: { type: 'Int' },
1452
+ },
1453
+ },
1454
+ },
1455
+ },
1456
+ },
1457
+ ],
1458
+ })
1459
+
1460
+ await client.request<any>(
1461
+ gql`
1462
+ mutation createTestClass {
1463
+ createTestClass(input: { fields: { field1: { field2: "test", field3: 1 } } }) {
1464
+ testClass {
1465
+ field1 {
1466
+ field2
1467
+ }
1468
+ }
1469
+ }
1470
+ }
1471
+ `,
1472
+ {},
1473
+ )
1474
+
1475
+ const res = await client.request<any>(
1476
+ gql`
1477
+ query testClasses {
1478
+ testClasses {
1479
+ totalCount
1480
+ }
1481
+ }
1482
+ `,
1483
+ )
1484
+
1485
+ expect(res.testClasses.totalCount).toEqual(1)
1486
+
1487
+ await wabe.close()
1488
+ })
1489
+
1490
+ it('should request an object with pointer in same class (issue #5)', async () => {
1491
+ const { client, wabe } = await createWabe({
1492
+ classes: [],
1493
+ })
1494
+
1495
+ await client.request<any>(gql`
1496
+ mutation createOnboarding {
1497
+ createUser(
1498
+ input: {
1499
+ fields: {
1500
+ authentication: { emailPassword: { email: "test@gmail.com", password: "password" } }
1501
+ }
1502
+ }
1503
+ ) {
1504
+ user {
1505
+ email
1506
+ }
1507
+ }
1508
+ }
1509
+ `)
1510
+
1511
+ const res = await client.request<any>(gql`
1512
+ query users {
1513
+ users {
1514
+ edges {
1515
+ node {
1516
+ authentication {
1517
+ emailPassword {
1518
+ email
1519
+ password
1520
+ }
1521
+ }
1522
+ }
1523
+ }
1524
+ }
1525
+ }
1526
+ `)
1527
+
1528
+ expect(res.users.edges[0].node.authentication.emailPassword).toEqual({
1529
+ email: 'test@gmail.com',
1530
+ password: expect.any(String),
1531
+ })
1532
+
1533
+ await wabe.close()
1534
+ })
1535
+
1536
+ it('should support custom output types for queries and mutations', () => {
1537
+ expect(
1538
+ getTypeFromGraphQLSchema({
1539
+ schema,
1540
+ type: 'Query',
1541
+ name: 'queryWithCustomTypes',
1542
+ }).output,
1543
+ ).toEqual('[TestQuery!]!')
1544
+
1545
+ expect(
1546
+ getTypeFromGraphQLSchema({
1547
+ schema,
1548
+ type: 'Mutation',
1549
+ name: 'mutationWithCustomTypes',
1550
+ }).output,
1551
+ ).toEqual('[TestMutation!]!')
1552
+ })
1553
+
1554
+ it('should have required field on object fields', () => {
1555
+ expect(
1556
+ getTypeFromGraphQLSchema({
1557
+ schema,
1558
+ type: 'Type',
1559
+ name: 'TestClassRequired',
1560
+ }).input.field7,
1561
+ ).toEqual('String!')
1562
+
1563
+ expect(
1564
+ getTypeFromGraphQLSchema({
1565
+ schema,
1566
+ type: 'Type',
1567
+ name: 'TestClassRequired',
1568
+ }).input.field8,
1569
+ ).toEqual('[Int!]!')
1570
+
1571
+ expect(
1572
+ getTypeFromGraphQLSchema({
1573
+ schema,
1574
+ type: 'Type',
1575
+ name: 'TestClassRequired',
1576
+ }).input.field9,
1577
+ ).toEqual('[TestClassRequiredTestObjectArray]!')
1578
+ })
1579
+
1580
+ it('should support object of array of object', () => {
1581
+ expect(
1582
+ getTypeFromGraphQLSchema({
1583
+ schema,
1584
+ type: 'Type',
1585
+ name: 'TestClass2',
1586
+ }).input.field1,
1587
+ ).toEqual('TestClass2TestObject!')
1588
+
1589
+ expect(
1590
+ getTypeFromGraphQLSchema({
1591
+ schema,
1592
+ type: 'Type',
1593
+ name: 'TestClass2TestObject',
1594
+ }).input.testSubObject,
1595
+ ).toEqual('[TestClass2TestObjectFieldsObject!]!')
1596
+
1597
+ expect(
1598
+ getTypeFromGraphQLSchema({
1599
+ schema,
1600
+ type: 'Type',
1601
+ name: 'TestClass2TestObjectFieldsObject',
1602
+ }).input,
1603
+ ).toEqual({ name: 'String!' })
1604
+ })
1605
+
1606
+ it('should support an array of object in graphql schema', async () => {
1607
+ const { client, wabe } = await createWabe({
1608
+ classes: [
1609
+ {
1610
+ name: 'TestClass',
1611
+ fields: {
1612
+ field1: {
1613
+ type: 'Array',
1614
+ typeValue: 'Object',
1615
+ object: {
1616
+ name: 'Field1Object',
1617
+ fields: {
1618
+ name: {
1619
+ type: 'String',
1620
+ },
1621
+ },
1622
+ },
1623
+ },
1624
+ },
1625
+ },
1626
+ ],
1627
+ })
1628
+
1629
+ const res = await client.request<any>(gql`
1630
+ mutation createTestClass {
1631
+ createTestClass(input: { fields: { field1: [{ name: "test" }, { name: "test2" }] } }) {
1632
+ testClass {
1633
+ id
1634
+ field1 {
1635
+ name
1636
+ }
1637
+ }
1638
+ }
1639
+ }
1640
+ `)
1641
+
1642
+ expect(res.createTestClass.testClass.field1).toEqual([{ name: 'test' }, { name: 'test2' }])
1643
+
1644
+ await wabe.close()
1645
+ })
1646
+
1647
+ it('should return an array in a query', async () => {
1648
+ const { client, wabe } = await createWabe({
1649
+ classes: [
1650
+ {
1651
+ name: 'TestClass',
1652
+ fields: {
1653
+ field1: {
1654
+ type: 'String',
1655
+ },
1656
+ },
1657
+ },
1658
+ ],
1659
+ resolvers: {
1660
+ queries: {
1661
+ testQuery: {
1662
+ resolve: () => {
1663
+ return ['test']
1664
+ },
1665
+ type: 'Array',
1666
+ typeValue: 'String',
1667
+ },
1668
+ },
1669
+ },
1670
+ })
1671
+
1672
+ const res = await client.request<any>(gql`
1673
+ query testQuery {
1674
+ testQuery
1675
+ }
1676
+ `)
1677
+
1678
+ expect(res.testQuery).toEqual(['test'])
1679
+
1680
+ await wabe.close()
1681
+ })
1682
+
1683
+ it('should return an object in a query', async () => {
1684
+ const { client, wabe } = await createWabe({
1685
+ classes: [
1686
+ {
1687
+ name: 'TestClass',
1688
+ fields: {
1689
+ field1: {
1690
+ type: 'String',
1691
+ },
1692
+ },
1693
+ },
1694
+ ],
1695
+ resolvers: {
1696
+ queries: {
1697
+ testQuery: {
1698
+ resolve: () => {
1699
+ return { test: 'test' }
1700
+ },
1701
+ type: 'Object',
1702
+ outputObject: {
1703
+ name: 'TestQueryOutput',
1704
+ fields: {
1705
+ test: {
1706
+ type: 'String',
1707
+ },
1708
+ },
1709
+ },
1710
+ },
1711
+ },
1712
+ },
1713
+ })
1714
+
1715
+ const res = await client.request<any>(gql`
1716
+ query testQuery {
1717
+ testQuery {
1718
+ test
1719
+ }
1720
+ }
1721
+ `)
1722
+
1723
+ expect(res.testQuery.test).toEqual('test')
1724
+
1725
+ await wabe.close()
1726
+ })
1727
+
1728
+ it('should return an array in a mutation', async () => {
1729
+ const { client, wabe } = await createWabe({
1730
+ classes: [
1731
+ {
1732
+ name: 'TestClass',
1733
+ fields: {
1734
+ field1: {
1735
+ type: 'String',
1736
+ },
1737
+ },
1738
+ },
1739
+ ],
1740
+ resolvers: {
1741
+ mutations: {
1742
+ testMutation: {
1743
+ resolve: () => {
1744
+ return ['test']
1745
+ },
1746
+ type: 'Array',
1747
+ typeValue: 'String',
1748
+ },
1749
+ },
1750
+ },
1751
+ })
1752
+
1753
+ const res = await client.request<any>(gql`
1754
+ mutation testMutation {
1755
+ testMutation
1756
+ }
1757
+ `)
1758
+
1759
+ expect(res.testMutation).toEqual(['test'])
1760
+
1761
+ await wabe.close()
1762
+ })
1763
+
1764
+ it('should return an object in a mutation', async () => {
1765
+ const { client, wabe } = await createWabe({
1766
+ classes: [
1767
+ {
1768
+ name: 'TestClass',
1769
+ fields: {
1770
+ field1: {
1771
+ type: 'String',
1772
+ },
1773
+ },
1774
+ },
1775
+ ],
1776
+ resolvers: {
1777
+ mutations: {
1778
+ testMutation: {
1779
+ resolve: () => {
1780
+ return { test: 'test' }
1781
+ },
1782
+ type: 'Object',
1783
+ outputObject: {
1784
+ name: 'TestMutationOutput',
1785
+ fields: {
1786
+ test: {
1787
+ type: 'String',
1788
+ },
1789
+ },
1790
+ },
1791
+ },
1792
+ },
1793
+ },
1794
+ })
1795
+
1796
+ const res = await client.request<any>(gql`
1797
+ mutation testMutation {
1798
+ testMutation {
1799
+ test
1800
+ }
1801
+ }
1802
+ `)
1803
+
1804
+ expect(res.testMutation.test).toEqual('test')
1805
+
1806
+ await wabe.close()
1807
+ })
1808
+
1809
+ it('should have a custom enum as value in type', async () => {
1810
+ const { client, wabe } = await createWabe({
1811
+ classes: [
1812
+ {
1813
+ name: 'TestClass',
1814
+ fields: {
1815
+ field1: {
1816
+ // @ts-expect-error
1817
+ type: 'CustomEnum',
1818
+ },
1819
+ },
1820
+ },
1821
+ ],
1822
+ enums: [
1823
+ {
1824
+ name: 'CustomEnum',
1825
+ values: {
1826
+ Value1: 'Value1',
1827
+ Value2: 'Value2',
1828
+ },
1829
+ },
1830
+ ],
1831
+ })
1832
+
1833
+ await client.request<any>(gql`
1834
+ mutation createTestClass {
1835
+ createTestClass(input: { fields: { field1: Value1 } }) {
1836
+ testClass {
1837
+ field1
1838
+ }
1839
+ }
1840
+ }
1841
+ `)
1842
+
1843
+ const res = await client.request<any>(gql`
1844
+ query testClasses {
1845
+ testClasses(where: { field1: { equalTo: "Value1" } }) {
1846
+ edges {
1847
+ node {
1848
+ id
1849
+ field1
1850
+ }
1851
+ }
1852
+ }
1853
+ }
1854
+ `)
1855
+
1856
+ expect(res.testClasses.edges.length).toBe(1)
1857
+ expect(res.testClasses.edges[0].node.field1).toBe('Value1')
1858
+
1859
+ const resNotEqual = await client.request<any>(gql`
1860
+ query testClasses {
1861
+ testClasses(where: { field1: { notEqualTo: "Value1" } }) {
1862
+ edges {
1863
+ node {
1864
+ id
1865
+ field1
1866
+ }
1867
+ }
1868
+ }
1869
+ }
1870
+ `)
1871
+
1872
+ expect(resNotEqual.testClasses.edges.length).toBe(0)
1873
+
1874
+ await wabe.close()
1875
+ })
1876
+
1877
+ it('should have correct WhereInput object', () => {
1878
+ expect(
1879
+ getTypeFromGraphQLSchema({
1880
+ schema,
1881
+ type: 'Type',
1882
+ name: 'TestClassWhereInput',
1883
+ }).input,
1884
+ ).toEqual({
1885
+ id: 'IdWhereInput',
1886
+ AND: '[TestClassWhereInput]',
1887
+ OR: '[TestClassWhereInput]',
1888
+ field1: 'StringWhereInput',
1889
+ acl: 'TestClassACLObjectWhereInput',
1890
+ createdAt: 'DateWhereInput',
1891
+ updatedAt: 'DateWhereInput',
1892
+ search: 'SearchWhereInput',
1893
+ })
1894
+ })
1895
+
1896
+ it('should have ConnectionObject on field of relation in ObjectType', () => {
1897
+ expect(
1898
+ getTypeFromGraphQLSchema({
1899
+ schema,
1900
+ type: 'Type',
1901
+ name: 'FifthClass',
1902
+ }).input.relation,
1903
+ ).toEqual('SixthClassConnection')
1904
+ })
1905
+
1906
+ it('should have a TestClassRelationInput', () => {
1907
+ expect(
1908
+ getTypeFromGraphQLSchema({
1909
+ schema,
1910
+ type: 'Type',
1911
+ name: 'TestClassRelationInput',
1912
+ }).input.createAndAdd,
1913
+ ).toEqual('[TestClassCreateFieldsInput!]')
1914
+ })
1915
+
1916
+ it('should have a RelationInput on SixthClass on field relation of FifthClass', () => {
1917
+ expect(
1918
+ getTypeFromGraphQLSchema({
1919
+ schema,
1920
+ type: 'Type',
1921
+ name: 'FifthClassInput',
1922
+ }).input.relation,
1923
+ ).toEqual('SixthClassRelationInput')
1924
+ })
1925
+
1926
+ it('should have the pointer in the object when there is a circular dependency in pointer', () => {
1927
+ expect(
1928
+ getTypeFromGraphQLSchema({
1929
+ schema,
1930
+ type: 'Type',
1931
+ name: 'ThirdClass',
1932
+ }).input.pointer,
1933
+ ).toEqual('FourthClass')
1934
+
1935
+ expect(
1936
+ getTypeFromGraphQLSchema({
1937
+ schema,
1938
+ type: 'Type',
1939
+ name: 'FourthClass',
1940
+ }).input.pointer,
1941
+ ).toEqual('ThirdClass')
1942
+
1943
+ expect(
1944
+ getTypeFromGraphQLSchema({
1945
+ schema,
1946
+ type: 'Type',
1947
+ name: 'ThirdClassInput',
1948
+ }).input.pointer,
1949
+ ).toEqual('FourthClassPointerInput')
1950
+
1951
+ expect(
1952
+ getTypeFromGraphQLSchema({
1953
+ schema,
1954
+ type: 'Type',
1955
+ name: 'FourthClassInput',
1956
+ }).input.pointer,
1957
+ ).toEqual('ThirdClassPointerInput')
1958
+
1959
+ expect(
1960
+ getTypeFromGraphQLSchema({
1961
+ schema,
1962
+ type: 'Type',
1963
+ name: 'ThirdClassPointerInput',
1964
+ }).input.createAndLink,
1965
+ ).toEqual('ThirdClassCreateFieldsInput')
1966
+
1967
+ expect(
1968
+ getTypeFromGraphQLSchema({
1969
+ schema,
1970
+ type: 'Type',
1971
+ name: 'FourthClassPointerInput',
1972
+ }).input.createAndLink,
1973
+ ).toEqual('FourthClassCreateFieldsInput')
1974
+ })
1975
+
1976
+ it('should have TestClassPointerInput', () => {
1977
+ expect(
1978
+ getTypeFromGraphQLSchema({
1979
+ schema,
1980
+ type: 'Type',
1981
+ name: 'TestClassPointerInput',
1982
+ }).input.createAndLink,
1983
+ ).toEqual('TestClassCreateFieldsInput')
1984
+ })
1985
+
1986
+ it('should have a type with a pointer to TestClass', () => {
1987
+ expect(
1988
+ getTypeFromGraphQLSchema({
1989
+ schema,
1990
+ type: 'Type',
1991
+ name: 'SecondClass',
1992
+ }).input.pointer,
1993
+ ).toEqual('TestClass')
1994
+ })
1995
+
1996
+ it('should have pointer input on SecondClassCreateFieldsInput', () => {
1997
+ expect(
1998
+ getTypeFromGraphQLSchema({
1999
+ schema,
2000
+ type: 'Type',
2001
+ name: 'SecondClassCreateFieldsInput',
2002
+ }).input.pointer,
2003
+ ).toEqual('TestClassPointerInput')
2004
+ })
2005
+
2006
+ it('should have pointer input on SecondClassUpdateFieldsInput', () => {
2007
+ expect(
2008
+ getTypeFromGraphQLSchema({
2009
+ schema,
2010
+ type: 'Type',
2011
+ name: 'SecondClassUpdateFieldsInput',
2012
+ }).input.pointer,
2013
+ ).toEqual('TestClassPointerInput')
2014
+ })
2015
+
2016
+ it('should have ClassCreateInputFieldsInput', () => {
2017
+ expect(
2018
+ getTypeFromGraphQLSchema({
2019
+ schema,
2020
+ type: 'Type',
2021
+ name: 'TestClassCreateFieldsInput',
2022
+ }).input.field1,
2023
+ ).toEqual('String')
2024
+ })
2025
+
2026
+ it('should have ClassUpdateInputFieldsInput', () => {
2027
+ expect(
2028
+ getTypeFromGraphQLSchema({
2029
+ schema,
2030
+ type: 'Type',
2031
+ name: 'TestClassUpdateFieldsInput',
2032
+ }).input.field1,
2033
+ ).toEqual('String')
2034
+ })
2035
+
2036
+ it('should get correct CreateTestClassPaylod type', () => {
2037
+ expect(
2038
+ getTypeFromGraphQLSchema({
2039
+ schema,
2040
+ type: 'Type',
2041
+ name: 'CreateTestClassPayload',
2042
+ }).input,
2043
+ ).toEqual({
2044
+ ok: 'Boolean',
2045
+ testClass: 'TestClass',
2046
+ })
2047
+ })
2048
+
2049
+ it('should return graphql relay standard output for default get query', () => {
2050
+ expect(
2051
+ getTypeFromGraphQLSchema({
2052
+ schema,
2053
+ type: 'Query',
2054
+ name: 'testClass',
2055
+ }),
2056
+ ).toEqual({
2057
+ input: {
2058
+ id: 'ID',
2059
+ },
2060
+ output: 'TestClass',
2061
+ })
2062
+ })
2063
+
2064
+ it('should return graphql relay standard output for default get query (multiple)', () => {
2065
+ expect(
2066
+ getTypeFromGraphQLSchema({
2067
+ schema,
2068
+ type: 'Query',
2069
+ name: 'testClasses',
2070
+ }),
2071
+ ).toEqual({
2072
+ input: {
2073
+ first: 'Int',
2074
+ offset: 'Int',
2075
+ where: 'TestClassWhereInput',
2076
+ order: '[TestClassOrder!]',
2077
+ },
2078
+ output: 'TestClassConnection!',
2079
+ })
2080
+ })
2081
+
2082
+ it('should return graphql relay standard output for default create mutation', () => {
2083
+ expect(
2084
+ getTypeFromGraphQLSchema({
2085
+ schema,
2086
+ type: 'Mutation',
2087
+ name: 'createTestClass',
2088
+ }),
2089
+ ).toEqual({
2090
+ input: { input: 'CreateTestClassInput!' },
2091
+ output: 'CreateTestClassPayload',
2092
+ })
2093
+ })
2094
+
2095
+ it('should return graphql relay standard output for default creates mutation (multiple)', () => {
2096
+ expect(
2097
+ getTypeFromGraphQLSchema({
2098
+ schema,
2099
+ type: 'Mutation',
2100
+ name: 'createTestClasses',
2101
+ }),
2102
+ ).toEqual({
2103
+ input: { input: 'CreateTestClassesInput!' },
2104
+ output: 'TestClassConnection!',
2105
+ })
2106
+ })
2107
+
2108
+ it('should return graphql relay standard output for default update mutation (clientMutationId, type)', () => {
2109
+ expect(
2110
+ getTypeFromGraphQLSchema({
2111
+ schema,
2112
+ type: 'Mutation',
2113
+ name: 'updateTestClass',
2114
+ }),
2115
+ ).toEqual({
2116
+ input: { input: 'UpdateTestClassInput!' },
2117
+ output: 'UpdateTestClassPayload',
2118
+ })
2119
+ })
2120
+
2121
+ it('should return graphql relay standard output for default updates (multiple) mutation', () => {
2122
+ expect(
2123
+ getTypeFromGraphQLSchema({
2124
+ schema,
2125
+ type: 'Mutation',
2126
+ name: 'updateTestClasses',
2127
+ }),
2128
+ ).toEqual({
2129
+ input: { input: 'UpdateTestClassesInput!' },
2130
+ output: 'TestClassConnection!',
2131
+ })
2132
+ })
2133
+
2134
+ it('should return graphql relay standard output for default delete mutation (clientMutationId, type)', () => {
2135
+ expect(
2136
+ getTypeFromGraphQLSchema({
2137
+ schema,
2138
+ type: 'Mutation',
2139
+ name: 'deleteTestClass',
2140
+ }),
2141
+ ).toEqual({
2142
+ input: { input: 'DeleteTestClassInput!' },
2143
+ output: 'DeleteTestClassPayload',
2144
+ })
2145
+ })
2146
+
2147
+ it('should return graphql relay standard output for default deletes (multiple) mutation', () => {
2148
+ expect(
2149
+ getTypeFromGraphQLSchema({
2150
+ schema,
2151
+ type: 'Mutation',
2152
+ name: 'deleteTestClasses',
2153
+ }),
2154
+ ).toEqual({
2155
+ input: { input: 'DeleteTestClassesInput!' },
2156
+ output: 'TestClassConnection!',
2157
+ })
2158
+ })
2159
+
2160
+ it('should not create input for mutation when there is no field', () => {
2161
+ expect(
2162
+ getTypeFromGraphQLSchema({
2163
+ schema,
2164
+ type: 'Mutation',
2165
+ name: 'customMutation',
2166
+ }),
2167
+ ).toEqual({
2168
+ input: {},
2169
+ output: 'Boolean',
2170
+ })
2171
+ })
2172
+
2173
+ it('should create custom query with no args', () => {
2174
+ expect(
2175
+ getTypeFromGraphQLSchema({
2176
+ schema,
2177
+ type: 'Query',
2178
+ name: 'customQuery',
2179
+ }),
2180
+ ).toEqual({
2181
+ input: {},
2182
+ output: 'Boolean',
2183
+ })
2184
+ })
2185
+
2186
+ it('should create mutation with sub input', async () => {
2187
+ const { client, wabe } = await createWabe({
2188
+ classes: [
2189
+ {
2190
+ name: 'TestClass',
2191
+ fields: { field1: { type: 'String' } },
2192
+ },
2193
+ ],
2194
+ resolvers: {
2195
+ mutations: {
2196
+ customMutation: {
2197
+ type: 'Int',
2198
+ args: {
2199
+ input: {
2200
+ sum: {
2201
+ type: 'Object',
2202
+ object: {
2203
+ name: 'Sum',
2204
+ fields: {
2205
+ a: {
2206
+ type: 'Int',
2207
+ required: true,
2208
+ },
2209
+ b: {
2210
+ type: 'Int',
2211
+ required: true,
2212
+ },
2213
+ },
2214
+ },
2215
+ },
2216
+ },
2217
+ },
2218
+ resolve: (_: any, args: any) => args.input.sum.a + args.input.sum.b,
2219
+ },
2220
+ },
2221
+ },
2222
+ })
2223
+
2224
+ const request = await client.request<any>(
2225
+ gql`
2226
+ mutation customMutation {
2227
+ customMutation(input: { sum: { a: 1, b: 2 } })
2228
+ }
2229
+ `,
2230
+ {},
2231
+ )
2232
+
2233
+ expect(request.customMutation).toBe(3)
2234
+
2235
+ await wabe.close()
2236
+ })
2237
+
2238
+ it('should create mutation with sub sub input', async () => {
2239
+ const { client, wabe } = await createWabe({
2240
+ classes: [
2241
+ {
2242
+ name: 'TestClass',
2243
+ fields: { field1: { type: 'String' } },
2244
+ },
2245
+ ],
2246
+ resolvers: {
2247
+ mutations: {
2248
+ customMutation: {
2249
+ type: 'Int',
2250
+ args: {
2251
+ input: {
2252
+ subObject: {
2253
+ type: 'Object',
2254
+ object: {
2255
+ name: 'SubObject',
2256
+ fields: {
2257
+ sum: {
2258
+ type: 'Object',
2259
+ object: {
2260
+ name: 'Sum',
2261
+ fields: {
2262
+ a: {
2263
+ type: 'Int',
2264
+ required: true,
2265
+ },
2266
+ b: {
2267
+ type: 'Int',
2268
+ required: true,
2269
+ },
2270
+ },
2271
+ },
2272
+ },
2273
+ },
2274
+ },
2275
+ },
2276
+ },
2277
+ },
2278
+ resolve: (_: any, args: any) => args.input.subObject.sum.a + args.input.subObject.sum.b,
2279
+ },
2280
+ },
2281
+ },
2282
+ })
2283
+
2284
+ const request = await client.request<any>(
2285
+ gql`
2286
+ mutation customMutation {
2287
+ customMutation(input: { subObject: { sum: { a: 1, b: 2 } } })
2288
+ }
2289
+ `,
2290
+ {},
2291
+ )
2292
+
2293
+ expect(request.customMutation).toBe(3)
2294
+
2295
+ await wabe.close()
2296
+ })
2297
+
2298
+ it('should create custom mutation with sub object and correct input name', async () => {
2299
+ const { client, wabe } = await createWabe({
2300
+ classes: [
2301
+ {
2302
+ name: 'TestClass',
2303
+ fields: { field1: { type: 'String' } },
2304
+ },
2305
+ ],
2306
+ resolvers: {
2307
+ mutations: {
2308
+ customMutation: {
2309
+ type: 'Int',
2310
+ args: {
2311
+ input: {
2312
+ sum: {
2313
+ type: 'Object',
2314
+ object: {
2315
+ name: 'Sum',
2316
+ fields: {
2317
+ a: {
2318
+ type: 'Int',
2319
+ required: true,
2320
+ },
2321
+ b: {
2322
+ type: 'Int',
2323
+ required: true,
2324
+ },
2325
+ },
2326
+ },
2327
+ },
2328
+ },
2329
+ },
2330
+ resolve: (_: any, args: any) => args.input.sum.a + args.input.sum.b,
2331
+ },
2332
+ },
2333
+ },
2334
+ })
2335
+
2336
+ const request = await client.request<any>(
2337
+ gql`
2338
+ mutation customMutation($sum: CustomMutationSumInput!) {
2339
+ customMutation(input: { sum: $sum })
2340
+ }
2341
+ `,
2342
+ {
2343
+ sum: {
2344
+ a: 1,
2345
+ b: 2,
2346
+ },
2347
+ },
2348
+ )
2349
+
2350
+ expect(request.customMutation).toBe(3)
2351
+
2352
+ const request2 = await client.request<any>(
2353
+ gql`
2354
+ mutation customMutation($input: CustomMutationInput!) {
2355
+ customMutation(input: $input)
2356
+ }
2357
+ `,
2358
+ {
2359
+ input: {
2360
+ sum: {
2361
+ a: 1,
2362
+ b: 2,
2363
+ },
2364
+ },
2365
+ },
2366
+ )
2367
+
2368
+ expect(request2.customMutation).toBe(3)
2369
+
2370
+ await wabe.close()
2371
+ })
2372
+
2373
+ it('should create a sub object with the good type', async () => {
2374
+ const { client, wabe } = await createWabe({
2375
+ classes: [
2376
+ {
2377
+ name: 'TestClass',
2378
+ fields: {
2379
+ field1: {
2380
+ type: 'Object',
2381
+ object: {
2382
+ name: 'SubObject',
2383
+ fields: {
2384
+ field2: { type: 'String' },
2385
+ field3: { type: 'Int' },
2386
+ },
2387
+ },
2388
+ },
2389
+ },
2390
+ },
2391
+ ],
2392
+ })
2393
+
2394
+ await client.request<any>(
2395
+ gql`
2396
+ mutation createTestClass {
2397
+ createTestClass(input: { fields: { field1: { field2: "test", field3: 1 } } }) {
2398
+ testClass {
2399
+ field1 {
2400
+ field2
2401
+ }
2402
+ }
2403
+ }
2404
+ }
2405
+ `,
2406
+ {},
2407
+ )
2408
+
2409
+ const request = await client.request<any>(
2410
+ gql`
2411
+ query testClasses($field1WhereInput: TestClassSubObjectWhereInput) {
2412
+ testClasses(where: { field1: $field1WhereInput }) {
2413
+ edges {
2414
+ node {
2415
+ field1 {
2416
+ field2
2417
+ field3
2418
+ }
2419
+ }
2420
+ }
2421
+ }
2422
+ }
2423
+ `,
2424
+ {
2425
+ field1WhereInput: {
2426
+ field2: { equalTo: 'test' },
2427
+ },
2428
+ },
2429
+ )
2430
+
2431
+ expect(request.testClasses.edges[0].node.field1.field2).toBe('test')
2432
+ expect(request.testClasses.edges[0].node.field1.field3).toBe(1)
2433
+
2434
+ await wabe.close()
2435
+ })
2436
+
2437
+ it('should create an object with a pointer (createAndLink)', async () => {
2438
+ const { client, wabe } = await createWabe({
2439
+ classes: [
2440
+ {
2441
+ name: 'TestClass',
2442
+ fields: {
2443
+ field1: {
2444
+ type: 'String',
2445
+ },
2446
+ },
2447
+ },
2448
+ {
2449
+ name: 'TestClass2',
2450
+ fields: {
2451
+ name: {
2452
+ type: 'String',
2453
+ },
2454
+ field2: {
2455
+ type: 'Pointer',
2456
+ // @ts-expect-error
2457
+ class: 'TestClass',
2458
+ },
2459
+ },
2460
+ },
2461
+ ],
2462
+ })
2463
+
2464
+ const res = await client.request<any>(
2465
+ gql`
2466
+ mutation createTestClass {
2467
+ createTestClass2(
2468
+ input: { fields: { name: "name", field2: { createAndLink: { field1: "field1" } } } }
2469
+ ) {
2470
+ testClass2 {
2471
+ name
2472
+ field2 {
2473
+ field1
2474
+ }
2475
+ }
2476
+ }
2477
+ }
2478
+ `,
2479
+ {},
2480
+ )
2481
+
2482
+ expect(res.createTestClass2.testClass2.name).toBe('name')
2483
+ expect(res.createTestClass2.testClass2.field2.field1).toBe('field1')
2484
+
2485
+ await wabe.close()
2486
+ })
2487
+
2488
+ it('should link an object to a pointer', async () => {
2489
+ const { client, wabe } = await createWabe({
2490
+ classes: [
2491
+ {
2492
+ name: 'TestClass',
2493
+ fields: {
2494
+ field1: {
2495
+ type: 'String',
2496
+ },
2497
+ },
2498
+ },
2499
+ {
2500
+ name: 'TestClass2',
2501
+ fields: {
2502
+ name: {
2503
+ type: 'String',
2504
+ },
2505
+ field2: {
2506
+ type: 'Pointer',
2507
+ // @ts-expect-error
2508
+ class: 'TestClass',
2509
+ },
2510
+ },
2511
+ },
2512
+ ],
2513
+ })
2514
+
2515
+ const {
2516
+ createTestClass: {
2517
+ testClass: { id: idOfTestClass },
2518
+ },
2519
+ } = await client.request<any>(
2520
+ gql`
2521
+ mutation createTestClass {
2522
+ createTestClass(input: { fields: { field1: "field1" } }) {
2523
+ testClass {
2524
+ id
2525
+ }
2526
+ }
2527
+ }
2528
+ `,
2529
+ {},
2530
+ )
2531
+
2532
+ const res = await client.request<any>(
2533
+ gql`
2534
+ mutation createTestClass {
2535
+ createTestClass2(
2536
+ input: {
2537
+ fields: {
2538
+ name: "name"
2539
+ field2: {
2540
+ link: "${idOfTestClass}"
2541
+ }
2542
+ }
2543
+ }
2544
+ ) {
2545
+ testClass2 {
2546
+ name
2547
+ field2 {
2548
+ field1
2549
+ }
2550
+ }
2551
+ }
2552
+ }
2553
+ `,
2554
+ {},
2555
+ )
2556
+
2557
+ expect(res.createTestClass2.testClass2.name).toBe('name')
2558
+ expect(res.createTestClass2.testClass2.field2.field1).toBe('field1')
2559
+
2560
+ await wabe.close()
2561
+ })
2562
+
2563
+ it('should link a pointer on create multiple object', async () => {
2564
+ const { client, wabe } = await createWabe({
2565
+ classes: [
2566
+ {
2567
+ name: 'TestClass',
2568
+ fields: {
2569
+ field1: {
2570
+ type: 'String',
2571
+ },
2572
+ },
2573
+ },
2574
+ {
2575
+ name: 'TestClass2',
2576
+ fields: {
2577
+ name: {
2578
+ type: 'String',
2579
+ },
2580
+ field2: {
2581
+ type: 'Pointer',
2582
+ // @ts-expect-error
2583
+ class: 'TestClass',
2584
+ },
2585
+ },
2586
+ },
2587
+ ],
2588
+ })
2589
+
2590
+ const res = await client.request<any>(
2591
+ gql`
2592
+ mutation createTestClass2s {
2593
+ createTestClass2s(
2594
+ input: { fields: [{ name: "name", field2: { createAndLink: { field1: "field1" } } }] }
2595
+ ) {
2596
+ edges {
2597
+ node {
2598
+ name
2599
+ field2 {
2600
+ field1
2601
+ }
2602
+ }
2603
+ }
2604
+ }
2605
+ }
2606
+ `,
2607
+ {},
2608
+ )
2609
+
2610
+ expect(res.createTestClass2s.edges[0].node.name).toBe('name')
2611
+ expect(res.createTestClass2s.edges[0].node.field2.field1).toBe('field1')
2612
+
2613
+ await wabe.close()
2614
+ })
2615
+
2616
+ it('should filter an object (on query) with pointer field', async () => {
2617
+ const { client, wabe } = await createWabe({
2618
+ classes: [
2619
+ {
2620
+ name: 'TestClass',
2621
+ fields: {
2622
+ field1: {
2623
+ type: 'String',
2624
+ },
2625
+ },
2626
+ },
2627
+ {
2628
+ name: 'TestClass2',
2629
+ fields: {
2630
+ name: {
2631
+ type: 'String',
2632
+ },
2633
+ field2: {
2634
+ type: 'Pointer',
2635
+ // @ts-expect-error
2636
+ class: 'TestClass',
2637
+ },
2638
+ },
2639
+ },
2640
+ ],
2641
+ })
2642
+
2643
+ const res = await client.request<any>(
2644
+ gql`
2645
+ mutation createTestClass {
2646
+ createTestClass2(
2647
+ input: { fields: { name: "name", field2: { createAndLink: { field1: "field1" } } } }
2648
+ ) {
2649
+ testClass2 {
2650
+ name
2651
+ field2 {
2652
+ field1
2653
+ }
2654
+ }
2655
+ }
2656
+ }
2657
+ `,
2658
+ {},
2659
+ )
2660
+
2661
+ expect(res.createTestClass2.testClass2.name).toBe('name')
2662
+ expect(res.createTestClass2.testClass2.field2.field1).toBe('field1')
2663
+
2664
+ const queryRes = await client.request<any>(gql`
2665
+ query testClass2s {
2666
+ testClass2s(where: { field2: { field1: { equalTo: "field1" } } }) {
2667
+ edges {
2668
+ node {
2669
+ name
2670
+ }
2671
+ }
2672
+ }
2673
+ }
2674
+ `)
2675
+
2676
+ expect(queryRes.testClass2s.edges.length).toBe(1)
2677
+ expect(queryRes.testClass2s.edges[0].node.name).toBe('name')
2678
+
2679
+ await wabe.close()
2680
+ })
2681
+
2682
+ it('should filter an object (on updates) with pointer field', async () => {
2683
+ const { client, wabe } = await createWabe({
2684
+ classes: [
2685
+ {
2686
+ name: 'TestClass',
2687
+ fields: {
2688
+ field1: {
2689
+ type: 'String',
2690
+ },
2691
+ },
2692
+ },
2693
+ {
2694
+ name: 'TestClass2',
2695
+ fields: {
2696
+ name: {
2697
+ type: 'String',
2698
+ },
2699
+ field2: {
2700
+ type: 'Pointer',
2701
+ // @ts-expect-error
2702
+ class: 'TestClass',
2703
+ },
2704
+ },
2705
+ },
2706
+ ],
2707
+ })
2708
+
2709
+ const res = await client.request<any>(
2710
+ gql`
2711
+ mutation createTestClass {
2712
+ createTestClass2(
2713
+ input: { fields: { name: "name", field2: { createAndLink: { field1: "field1" } } } }
2714
+ ) {
2715
+ testClass2 {
2716
+ name
2717
+ field2 {
2718
+ field1
2719
+ }
2720
+ }
2721
+ }
2722
+ }
2723
+ `,
2724
+ {},
2725
+ )
2726
+
2727
+ expect(res.createTestClass2.testClass2.name).toBe('name')
2728
+ expect(res.createTestClass2.testClass2.field2.field1).toBe('field1')
2729
+
2730
+ const updateRes = await client.request<any>(gql`
2731
+ mutation updateTestClass2s {
2732
+ updateTestClass2s(
2733
+ input: { fields: { name: "name2" }, where: { field2: { field1: { equalTo: "field1" } } } }
2734
+ ) {
2735
+ edges {
2736
+ node {
2737
+ name
2738
+ }
2739
+ }
2740
+ }
2741
+ }
2742
+ `)
2743
+
2744
+ expect(updateRes.updateTestClass2s.edges.length).toBe(1)
2745
+ expect(updateRes.updateTestClass2s.edges[0].node.name).toBe('name2')
2746
+
2747
+ await wabe.close()
2748
+ })
2749
+
2750
+ it('should filter an object (on deletes) with pointer field', async () => {
2751
+ const { client, wabe } = await createWabe({
2752
+ classes: [
2753
+ {
2754
+ name: 'TestClass',
2755
+ fields: {
2756
+ field1: {
2757
+ type: 'String',
2758
+ },
2759
+ },
2760
+ },
2761
+ {
2762
+ name: 'TestClass2',
2763
+ fields: {
2764
+ name: {
2765
+ type: 'String',
2766
+ },
2767
+ field2: {
2768
+ type: 'Pointer',
2769
+ // @ts-expect-error
2770
+ class: 'TestClass',
2771
+ },
2772
+ },
2773
+ },
2774
+ ],
2775
+ })
2776
+
2777
+ const res = await client.request<any>(
2778
+ gql`
2779
+ mutation createTestClass {
2780
+ createTestClass2(
2781
+ input: { fields: { name: "name", field2: { createAndLink: { field1: "field1" } } } }
2782
+ ) {
2783
+ testClass2 {
2784
+ name
2785
+ field2 {
2786
+ field1
2787
+ }
2788
+ }
2789
+ }
2790
+ }
2791
+ `,
2792
+ {},
2793
+ )
2794
+
2795
+ expect(res.createTestClass2.testClass2.name).toBe('name')
2796
+ expect(res.createTestClass2.testClass2.field2.field1).toBe('field1')
2797
+
2798
+ const deleteRes = await client.request<any>(gql`
2799
+ mutation deleteTestClass2s {
2800
+ deleteTestClass2s(input: { where: { field2: { field1: { equalTo: "field1" } } } }) {
2801
+ edges {
2802
+ node {
2803
+ name
2804
+ }
2805
+ }
2806
+ }
2807
+ }
2808
+ `)
2809
+
2810
+ expect(deleteRes.deleteTestClass2s.edges.length).toBe(1)
2811
+ expect(deleteRes.deleteTestClass2s.edges[0].node.name).toBe('name')
2812
+
2813
+ await wabe.close()
2814
+ })
2815
+
2816
+ it('should create and link a pointer on update', async () => {
2817
+ const { client, wabe } = await createWabe({
2818
+ classes: [
2819
+ {
2820
+ name: 'TestClass',
2821
+ fields: {
2822
+ field1: {
2823
+ type: 'String',
2824
+ },
2825
+ },
2826
+ },
2827
+ {
2828
+ name: 'TestClass2',
2829
+ fields: {
2830
+ name: {
2831
+ type: 'String',
2832
+ },
2833
+ field2: {
2834
+ type: 'Pointer',
2835
+ // @ts-expect-error
2836
+ class: 'TestClass',
2837
+ },
2838
+ },
2839
+ },
2840
+ ],
2841
+ })
2842
+
2843
+ const res = await client.request<any>(
2844
+ gql`
2845
+ mutation createTestClass {
2846
+ createTestClass2(
2847
+ input: { fields: { name: "name", field2: { createAndLink: { field1: "field1" } } } }
2848
+ ) {
2849
+ testClass2 {
2850
+ id
2851
+ name
2852
+ field2 {
2853
+ field1
2854
+ }
2855
+ }
2856
+ }
2857
+ }
2858
+ `,
2859
+ {},
2860
+ )
2861
+
2862
+ expect(res.createTestClass2.testClass2.name).toBe('name')
2863
+ expect(res.createTestClass2.testClass2.field2.field1).toBe('field1')
2864
+
2865
+ const resAfterUpdate = await client.request<any>(gql`
2866
+ mutation updateTestClass {
2867
+ updateTestClass2(
2868
+ input: {
2869
+ fields: {
2870
+ field2: {
2871
+ createAndLink: { field1: "field1AfterUpdate" }
2872
+ }
2873
+ }
2874
+ id: "${res.createTestClass2.testClass2.id}"
2875
+ }
2876
+ ) {
2877
+ testClass2 {
2878
+ name
2879
+ field2 {
2880
+ field1
2881
+ }
2882
+ }
2883
+ }
2884
+ }
2885
+ `)
2886
+
2887
+ expect(resAfterUpdate.updateTestClass2.testClass2.name).toBe('name')
2888
+ expect(resAfterUpdate.updateTestClass2.testClass2.field2.field1).toBe('field1AfterUpdate')
2889
+
2890
+ await wabe.close()
2891
+ })
2892
+
2893
+ it('should link a pointer on update', async () => {
2894
+ const { client, wabe } = await createWabe({
2895
+ classes: [
2896
+ {
2897
+ name: 'TestClass',
2898
+ fields: {
2899
+ field1: {
2900
+ type: 'String',
2901
+ },
2902
+ },
2903
+ },
2904
+ {
2905
+ name: 'TestClass2',
2906
+ fields: {
2907
+ name: {
2908
+ type: 'String',
2909
+ },
2910
+ field2: {
2911
+ type: 'Pointer',
2912
+ // @ts-expect-error
2913
+ class: 'TestClass',
2914
+ },
2915
+ },
2916
+ },
2917
+ ],
2918
+ })
2919
+
2920
+ const {
2921
+ createTestClass: {
2922
+ testClass: { id: idOfTestClass },
2923
+ },
2924
+ } = await client.request<any>(
2925
+ gql`
2926
+ mutation createTestClass {
2927
+ createTestClass(input: { fields: { field1: "field1" } }) {
2928
+ testClass {
2929
+ id
2930
+ }
2931
+ }
2932
+ }
2933
+ `,
2934
+ {},
2935
+ )
2936
+
2937
+ const res = await client.request<any>(
2938
+ gql`
2939
+ mutation createTestClass {
2940
+ createTestClass2(input: { fields: { name: "name" } }) {
2941
+ testClass2 {
2942
+ id
2943
+ name
2944
+ }
2945
+ }
2946
+ }
2947
+ `,
2948
+ {},
2949
+ )
2950
+
2951
+ const resAfterUpdate = await client.request<any>(
2952
+ gql`
2953
+ mutation updateTestClass {
2954
+ updateTestClass2(input: {
2955
+ id: "${res.createTestClass2.testClass2.id}"
2956
+ fields: {
2957
+ field2: { link: "${idOfTestClass}" }
2958
+ }
2959
+ }){
2960
+ testClass2 {
2961
+ name
2962
+ field2{
2963
+ field1
2964
+ }
2965
+ }
2966
+ }
2967
+ }
2968
+ `,
2969
+ {},
2970
+ )
2971
+
2972
+ expect(resAfterUpdate.updateTestClass2.testClass2.name).toBe('name')
2973
+ expect(resAfterUpdate.updateTestClass2.testClass2.field2.field1).toBe('field1')
2974
+
2975
+ await wabe.close()
2976
+ })
2977
+
2978
+ it('should unlink a pointer on update', async () => {
2979
+ const { client, wabe } = await createWabe({
2980
+ classes: [
2981
+ {
2982
+ name: 'TestClass',
2983
+ fields: {
2984
+ field1: {
2985
+ type: 'String',
2986
+ },
2987
+ },
2988
+ },
2989
+ {
2990
+ name: 'TestClass2',
2991
+ fields: {
2992
+ name: {
2993
+ type: 'String',
2994
+ },
2995
+ field2: {
2996
+ type: 'Pointer',
2997
+ // @ts-expect-error
2998
+ class: 'TestClass',
2999
+ },
3000
+ },
3001
+ },
3002
+ ],
3003
+ })
3004
+
3005
+ const {
3006
+ createTestClass: {
3007
+ testClass: { id: idOfTestClass },
3008
+ },
3009
+ } = await client.request<any>(
3010
+ gql`
3011
+ mutation createTestClass {
3012
+ createTestClass(input: { fields: { field1: "field1" } }) {
3013
+ testClass {
3014
+ id
3015
+ }
3016
+ }
3017
+ }
3018
+ `,
3019
+ {},
3020
+ )
3021
+
3022
+ const res = await client.request<any>(
3023
+ gql`
3024
+ mutation createTestClass {
3025
+ createTestClass2(input: { fields: { name: "name", field2: { link : "${idOfTestClass}"} }}) {
3026
+ testClass2 {
3027
+ id
3028
+ name
3029
+ field2 {
3030
+ field1
3031
+ }
3032
+ }
3033
+ }
3034
+ }
3035
+ `,
3036
+ {},
3037
+ )
3038
+
3039
+ expect(res.createTestClass2.testClass2.field2.field1).toBe('field1')
3040
+
3041
+ const res2 = await client.request<any>(
3042
+ gql`
3043
+ mutation updateTestClass2 {
3044
+ updateTestClass2(input: {
3045
+ id: "${res.createTestClass2.testClass2.id}"
3046
+ fields: {
3047
+ field2: { unlink: true }
3048
+ }
3049
+ }){
3050
+ testClass2 {
3051
+ name
3052
+ field2{
3053
+ field1
3054
+ }
3055
+ }
3056
+ }
3057
+ }
3058
+ `,
3059
+ {},
3060
+ )
3061
+
3062
+ expect(res2.updateTestClass2.testClass2.field2).toBeNull()
3063
+
3064
+ await wabe.close()
3065
+ })
3066
+
3067
+ it('should link a pointer on update multiple object', async () => {
3068
+ const { client, wabe } = await createWabe({
3069
+ classes: [
3070
+ {
3071
+ name: 'TestClass',
3072
+ fields: {
3073
+ field1: {
3074
+ type: 'String',
3075
+ },
3076
+ },
3077
+ },
3078
+ {
3079
+ name: 'TestClass2',
3080
+ fields: {
3081
+ name: {
3082
+ type: 'String',
3083
+ },
3084
+ field2: {
3085
+ type: 'Pointer',
3086
+ // @ts-expect-error
3087
+ class: 'TestClass',
3088
+ },
3089
+ },
3090
+ },
3091
+ ],
3092
+ })
3093
+
3094
+ await client.request<any>(
3095
+ gql`
3096
+ mutation createTestClass2s {
3097
+ createTestClass2s(input: { fields: [{ name: "name" }, { name: "name2" }] }) {
3098
+ edges {
3099
+ node {
3100
+ name
3101
+ }
3102
+ }
3103
+ }
3104
+ }
3105
+ `,
3106
+ {},
3107
+ )
3108
+
3109
+ const resAfterUpdate = await client.request<any>(
3110
+ gql`
3111
+ mutation updateTestClass2s {
3112
+ updateTestClass2s(
3113
+ input: {
3114
+ where: { name: { equalTo: "name" } }
3115
+ fields: { field2: { createAndLink: { field1: "field1UpdateMultiple" } } }
3116
+ }
3117
+ ) {
3118
+ edges {
3119
+ node {
3120
+ name
3121
+ field2 {
3122
+ field1
3123
+ }
3124
+ }
3125
+ }
3126
+ }
3127
+ }
3128
+ `,
3129
+ {},
3130
+ )
3131
+
3132
+ expect(resAfterUpdate.updateTestClass2s.edges[0].node.name).toBe('name')
3133
+ expect(resAfterUpdate.updateTestClass2s.edges[0].node.field2.field1).toBe(
3134
+ 'field1UpdateMultiple',
3135
+ )
3136
+
3137
+ await wabe.close()
3138
+ })
3139
+
3140
+ it('should return pointer data on delete an element', async () => {
3141
+ const { client, wabe } = await createWabe({
3142
+ classes: [
3143
+ {
3144
+ name: 'TestClass',
3145
+ fields: {
3146
+ field1: {
3147
+ type: 'String',
3148
+ },
3149
+ },
3150
+ },
3151
+ {
3152
+ name: 'TestClass2',
3153
+ fields: {
3154
+ name: {
3155
+ type: 'String',
3156
+ },
3157
+ field2: {
3158
+ type: 'Pointer',
3159
+ // @ts-expect-error
3160
+ class: 'TestClass',
3161
+ },
3162
+ },
3163
+ },
3164
+ ],
3165
+ })
3166
+
3167
+ const res = await client.request<any>(
3168
+ gql`
3169
+ mutation createTestClass {
3170
+ createTestClass2(
3171
+ input: { fields: { name: "name", field2: { createAndLink: { field1: "field1" } } } }
3172
+ ) {
3173
+ testClass2 {
3174
+ id
3175
+ name
3176
+ field2 {
3177
+ field1
3178
+ }
3179
+ }
3180
+ }
3181
+ }
3182
+ `,
3183
+ {},
3184
+ )
3185
+
3186
+ expect(res.createTestClass2.testClass2.name).toBe('name')
3187
+ expect(res.createTestClass2.testClass2.field2.field1).toBe('field1')
3188
+
3189
+ const resAfterDelete = await client.request<any>(gql`
3190
+ mutation deleteTestClass2 {
3191
+ deleteTestClass2(input: {id: "${res.createTestClass2.testClass2.id}"}) {
3192
+ testClass2 {
3193
+ name
3194
+ field2 {
3195
+ field1
3196
+ }
3197
+ }
3198
+ }
3199
+ }
3200
+ `)
3201
+
3202
+ expect(resAfterDelete.deleteTestClass2.testClass2.name).toBe('name')
3203
+ expect(resAfterDelete.deleteTestClass2.testClass2.field2.field1).toBe('field1')
3204
+
3205
+ await wabe.close()
3206
+ })
3207
+
3208
+ it('should createAndAdd an object on a relation field (on create)', async () => {
3209
+ const { client, wabe } = await createWabe({
3210
+ classes: [
3211
+ {
3212
+ name: 'TestClass',
3213
+ fields: {
3214
+ field1: {
3215
+ type: 'String',
3216
+ },
3217
+ },
3218
+ },
3219
+ {
3220
+ name: 'TestClass2',
3221
+ fields: {
3222
+ name: {
3223
+ type: 'String',
3224
+ },
3225
+ field2: {
3226
+ type: 'Relation',
3227
+ // @ts-expect-error
3228
+ class: 'TestClass',
3229
+ },
3230
+ },
3231
+ },
3232
+ ],
3233
+ })
3234
+
3235
+ const res = await client.request<any>(gql`
3236
+ mutation createTestClass2 {
3237
+ createTestClass2(
3238
+ input: {
3239
+ fields: {
3240
+ name: "name"
3241
+ field2: { createAndAdd: [{ field1: "field1" }, { field1: "field2" }] }
3242
+ }
3243
+ }
3244
+ ) {
3245
+ testClass2 {
3246
+ name
3247
+ field2 {
3248
+ edges {
3249
+ node {
3250
+ field1
3251
+ }
3252
+ }
3253
+ }
3254
+ }
3255
+ }
3256
+ }
3257
+ `)
3258
+
3259
+ const field2AfterUpdate1 = (await wabe.controllers.database.getObjects({
3260
+ // @ts-expect-error
3261
+ className: 'TestClass2',
3262
+ context: {
3263
+ wabe,
3264
+ isRoot: true,
3265
+ },
3266
+ })) as any
3267
+
3268
+ expect(field2AfterUpdate1[0]?.field2.length).toBe(2)
3269
+
3270
+ expect(res.createTestClass2.testClass2.name).toBe('name')
3271
+ expect(res.createTestClass2.testClass2.field2.edges[0].node.field1).toBe('field1')
3272
+
3273
+ await wabe.close()
3274
+ })
3275
+
3276
+ it('should add an object on a relation field (on create)', async () => {
3277
+ const { client, wabe } = await createWabe({
3278
+ classes: [
3279
+ {
3280
+ name: 'TestClass',
3281
+ fields: {
3282
+ field1: {
3283
+ type: 'String',
3284
+ },
3285
+ },
3286
+ },
3287
+ {
3288
+ name: 'TestClass2',
3289
+ fields: {
3290
+ name: {
3291
+ type: 'String',
3292
+ },
3293
+ field2: {
3294
+ type: 'Relation',
3295
+ // @ts-expect-error
3296
+ class: 'TestClass',
3297
+ },
3298
+ },
3299
+ },
3300
+ ],
3301
+ })
3302
+
3303
+ const res = await client.request<any>(gql`
3304
+ mutation createTestClass {
3305
+ createTestClass(input: { fields: { field1: "field1" } }) {
3306
+ testClass {
3307
+ id
3308
+ }
3309
+ }
3310
+ }
3311
+ `)
3312
+
3313
+ const resAfterAdd = await client.request<any>(gql`
3314
+ mutation createTestClass2 {
3315
+ createTestClass2(
3316
+ input: {
3317
+ fields: {
3318
+ name: "name"
3319
+ field2: { add: ["${res.createTestClass.testClass.id}"] }
3320
+ }
3321
+ }
3322
+ ) {
3323
+ testClass2 {
3324
+ name
3325
+ field2 {
3326
+ edges {
3327
+ node {
3328
+ field1
3329
+ }
3330
+ }
3331
+ }
3332
+ }
3333
+ }
3334
+ }
3335
+ `)
3336
+
3337
+ expect(resAfterAdd.createTestClass2.testClass2.name).toBe('name')
3338
+ expect(resAfterAdd.createTestClass2.testClass2.field2.edges[0].node.field1).toBe('field1')
3339
+
3340
+ await wabe.close()
3341
+ })
3342
+
3343
+ it('should createAndAdd an object on a relation field (on createMany)', async () => {
3344
+ const { client, wabe } = await createWabe({
3345
+ classes: [
3346
+ {
3347
+ name: 'TestClass',
3348
+ fields: {
3349
+ field1: {
3350
+ type: 'String',
3351
+ },
3352
+ },
3353
+ },
3354
+ {
3355
+ name: 'TestClass2',
3356
+ fields: {
3357
+ name: {
3358
+ type: 'String',
3359
+ },
3360
+ field2: {
3361
+ type: 'Relation',
3362
+ // @ts-expect-error
3363
+ class: 'TestClass',
3364
+ },
3365
+ },
3366
+ },
3367
+ ],
3368
+ })
3369
+
3370
+ const res = await client.request<any>(gql`
3371
+ mutation createTestClass2s {
3372
+ createTestClass2s(
3373
+ input: { fields: [{ name: "name", field2: { createAndAdd: [{ field1: "field1" }] } }] }
3374
+ ) {
3375
+ edges {
3376
+ node {
3377
+ name
3378
+ field2 {
3379
+ edges {
3380
+ node {
3381
+ field1
3382
+ }
3383
+ }
3384
+ }
3385
+ }
3386
+ }
3387
+ }
3388
+ }
3389
+ `)
3390
+
3391
+ expect(res.createTestClass2s.edges[0].node.name).toBe('name')
3392
+ expect(res.createTestClass2s.edges[0].node.field2.edges[0].node.field1).toBe('field1')
3393
+
3394
+ await wabe.close()
3395
+ })
3396
+
3397
+ it('should add an object on a relation field (on createMany)', async () => {
3398
+ const { client, wabe } = await createWabe({
3399
+ classes: [
3400
+ {
3401
+ name: 'TestClass',
3402
+ fields: {
3403
+ field1: {
3404
+ type: 'String',
3405
+ },
3406
+ },
3407
+ },
3408
+ {
3409
+ name: 'TestClass2',
3410
+ fields: {
3411
+ name: {
3412
+ type: 'String',
3413
+ },
3414
+ field2: {
3415
+ type: 'Relation',
3416
+ // @ts-expect-error
3417
+ class: 'TestClass',
3418
+ },
3419
+ },
3420
+ },
3421
+ ],
3422
+ })
3423
+
3424
+ const res = await client.request<any>(gql`
3425
+ mutation createTestClass {
3426
+ createTestClass(input: { fields: { field1: "field1" } }) {
3427
+ testClass {
3428
+ id
3429
+ }
3430
+ }
3431
+ }
3432
+ `)
3433
+
3434
+ const resAfterAdd = await client.request<any>(gql`
3435
+ mutation createTestClass2s {
3436
+ createTestClass2s(
3437
+ input: {
3438
+ fields: [
3439
+ {
3440
+ name: "name"
3441
+ field2: { add: ["${res.createTestClass.testClass.id}"] }
3442
+ }
3443
+ ]
3444
+ }
3445
+ ) {
3446
+ edges {
3447
+ node {
3448
+ name
3449
+ field2 {
3450
+ edges {
3451
+ node {
3452
+ field1
3453
+ }
3454
+ }
3455
+ }
3456
+ }
3457
+ }
3458
+ }
3459
+ }
3460
+ `)
3461
+
3462
+ expect(resAfterAdd.createTestClass2s.edges[0].node.name).toBe('name')
3463
+ expect(resAfterAdd.createTestClass2s.edges[0].node.field2.edges[0].node.field1).toBe('field1')
3464
+
3465
+ await wabe.close()
3466
+ })
3467
+
3468
+ it('should createAndAdd an object on a relation field (on update)', async () => {
3469
+ const { client, wabe } = await createWabe({
3470
+ classes: [
3471
+ {
3472
+ name: 'TestClass',
3473
+ fields: {
3474
+ field1: {
3475
+ type: 'String',
3476
+ },
3477
+ },
3478
+ },
3479
+ {
3480
+ name: 'TestClass2',
3481
+ fields: {
3482
+ name: {
3483
+ type: 'String',
3484
+ },
3485
+ field2: {
3486
+ type: 'Relation',
3487
+ // @ts-expect-error
3488
+ class: 'TestClass',
3489
+ },
3490
+ },
3491
+ },
3492
+ ],
3493
+ })
3494
+
3495
+ const resAfterAdd = await client.request<any>(gql`
3496
+ mutation createTestClass2 {
3497
+ createTestClass2(input: { fields: { name: "name" } }) {
3498
+ testClass2 {
3499
+ id
3500
+ name
3501
+ }
3502
+ }
3503
+ }
3504
+ `)
3505
+
3506
+ const resAfterUpdate = await client.request<any>(gql`
3507
+ mutation updateTestClass2 {
3508
+ updateTestClass2(
3509
+ input: {
3510
+ id: "${resAfterAdd.createTestClass2.testClass2.id}"
3511
+ fields: {
3512
+ field2: { createAndAdd: [{ field1: "field1" }] }
3513
+ }
3514
+ }
3515
+ ) {
3516
+ testClass2 {
3517
+ id
3518
+ name
3519
+ field2 {
3520
+ edges {
3521
+ node {
3522
+ field1
3523
+ }
3524
+ }
3525
+ }
3526
+ }
3527
+ }
3528
+ }
3529
+ `)
3530
+
3531
+ expect(resAfterUpdate.updateTestClass2.testClass2.name).toBe('name')
3532
+ expect(resAfterUpdate.updateTestClass2.testClass2.field2.edges[0].node.field1).toBe('field1')
3533
+
3534
+ await wabe.close()
3535
+ })
3536
+
3537
+ it('should add an object on a relation field (on update)', async () => {
3538
+ const { client, wabe } = await createWabe({
3539
+ classes: [
3540
+ {
3541
+ name: 'TestClass',
3542
+ fields: {
3543
+ field1: {
3544
+ type: 'String',
3545
+ },
3546
+ },
3547
+ },
3548
+ {
3549
+ name: 'TestClass2',
3550
+ fields: {
3551
+ name: {
3552
+ type: 'String',
3553
+ },
3554
+ field2: {
3555
+ type: 'Relation',
3556
+ // @ts-expect-error
3557
+ class: 'TestClass',
3558
+ },
3559
+ },
3560
+ },
3561
+ ],
3562
+ })
3563
+
3564
+ const res = await client.request<any>(gql`
3565
+ mutation createTestClass {
3566
+ createTestClass(input: { fields: { field1: "field1" } }) {
3567
+ testClass {
3568
+ id
3569
+ }
3570
+ }
3571
+ }
3572
+ `)
3573
+
3574
+ const res2 = await client.request<any>(gql`
3575
+ mutation createTestClass {
3576
+ createTestClass(input: { fields: { field1: "field1" } }) {
3577
+ testClass {
3578
+ id
3579
+ }
3580
+ }
3581
+ }
3582
+ `)
3583
+
3584
+ const resAfterAdd = await client.request<any>(gql`
3585
+ mutation createTestClass2 {
3586
+ createTestClass2(input: { fields: { name: "name" } }) {
3587
+ testClass2 {
3588
+ id
3589
+ name
3590
+ }
3591
+ }
3592
+ }
3593
+ `)
3594
+
3595
+ await client.request<any>(gql`
3596
+ mutation updateTestClass2 {
3597
+ updateTestClass2(
3598
+ input: {
3599
+ id: "${resAfterAdd.createTestClass2.testClass2.id}"
3600
+ fields: {
3601
+ field2: { add: ["${res.createTestClass.testClass.id}"] }
3602
+ }
3603
+ }
3604
+ ) {
3605
+ testClass2 {
3606
+ id
3607
+ name
3608
+ field2 {
3609
+ edges {
3610
+ node {
3611
+ field1
3612
+ }
3613
+ }
3614
+ }
3615
+ }
3616
+ }
3617
+ }
3618
+ `)
3619
+
3620
+ const field2AfterUpdate1 = (await wabe.controllers.database.getObjects({
3621
+ // @ts-expect-error
3622
+ className: 'TestClass2',
3623
+ context: {
3624
+ wabe,
3625
+ isRoot: true,
3626
+ },
3627
+ })) as any
3628
+
3629
+ expect(field2AfterUpdate1[0]?.field2.length).toBe(1)
3630
+
3631
+ const resAfterUpdate2 = await client.request<any>(gql`
3632
+ mutation updateTestClass2 {
3633
+ updateTestClass2(
3634
+ input: {
3635
+ id: "${resAfterAdd.createTestClass2.testClass2.id}"
3636
+ fields: {
3637
+ field2: { add: ["${res2.createTestClass.testClass.id}"] }
3638
+ }
3639
+ }
3640
+ ) {
3641
+ testClass2 {
3642
+ id
3643
+ name
3644
+ field2 {
3645
+ edges {
3646
+ node {
3647
+ field1
3648
+ }
3649
+ }
3650
+ }
3651
+ }
3652
+ }
3653
+ }
3654
+ `)
3655
+
3656
+ const field2AfterUpdate2 = (await wabe.controllers.database.getObjects({
3657
+ // @ts-expect-error
3658
+ className: 'TestClass2',
3659
+ context: {
3660
+ wabe,
3661
+ isRoot: true,
3662
+ },
3663
+ // @ts-expect-error
3664
+ select: { field2: true },
3665
+ })) as any
3666
+
3667
+ expect(field2AfterUpdate2[0].field2).toEqual([
3668
+ { id: res.createTestClass.testClass.id },
3669
+ { id: res2.createTestClass.testClass.id },
3670
+ ])
3671
+ expect(field2AfterUpdate2[0]?.field2.length).toBe(2)
3672
+
3673
+ expect(resAfterUpdate2.updateTestClass2.testClass2.name).toBe('name')
3674
+ expect(resAfterUpdate2.updateTestClass2.testClass2.field2.edges.length).toBe(2)
3675
+ expect(resAfterUpdate2.updateTestClass2.testClass2.field2.edges[0].node.field1).toBe('field1')
3676
+
3677
+ await wabe.close()
3678
+ })
3679
+
3680
+ it('should remove an object on a relation field (on update)', async () => {
3681
+ const { client, wabe } = await createWabe({
3682
+ classes: [
3683
+ {
3684
+ name: 'TestClass',
3685
+ fields: {
3686
+ field1: {
3687
+ type: 'String',
3688
+ },
3689
+ },
3690
+ },
3691
+ {
3692
+ name: 'TestClass2',
3693
+ fields: {
3694
+ name: {
3695
+ type: 'String',
3696
+ },
3697
+ field2: {
3698
+ type: 'Relation',
3699
+ // @ts-expect-error
3700
+ class: 'TestClass',
3701
+ },
3702
+ },
3703
+ },
3704
+ ],
3705
+ })
3706
+
3707
+ const resAfterAdd = await client.request<any>(gql`
3708
+ mutation createTestClass2 {
3709
+ createTestClass2(
3710
+ input: { fields: { name: "name", field2: { createAndAdd: [{ field1: "field1" }] } } }
3711
+ ) {
3712
+ testClass2 {
3713
+ id
3714
+ name
3715
+ field2 {
3716
+ edges {
3717
+ node {
3718
+ id
3719
+ field1
3720
+ }
3721
+ }
3722
+ }
3723
+ }
3724
+ }
3725
+ }
3726
+ `)
3727
+
3728
+ const resAfterUpdate = await client.request<any>(gql`
3729
+ mutation updateTestClass2 {
3730
+ updateTestClass2(
3731
+ input: {
3732
+ id: "${resAfterAdd.createTestClass2.testClass2.id}"
3733
+ fields: {
3734
+ field2: { remove: ["${resAfterAdd.createTestClass2.testClass2.field2.edges[0].node.id}"] }
3735
+ }
3736
+ }
3737
+ ) {
3738
+ testClass2 {
3739
+ id
3740
+ name
3741
+ field2 {
3742
+ edges {
3743
+ node {
3744
+ id
3745
+ }
3746
+ }
3747
+ }
3748
+ }
3749
+ }
3750
+ }
3751
+ `)
3752
+
3753
+ expect(resAfterUpdate.updateTestClass2.testClass2.name).toBe('name')
3754
+ expect(resAfterUpdate.updateTestClass2.testClass2.field2.edges.length).toEqual(0)
3755
+
3756
+ await wabe.close()
3757
+ })
3758
+
3759
+ it('should createAndAdd an object on a relation field (on updateMany)', async () => {
3760
+ const { client, wabe } = await createWabe({
3761
+ classes: [
3762
+ {
3763
+ name: 'TestClass',
3764
+ fields: {
3765
+ field1: {
3766
+ type: 'String',
3767
+ },
3768
+ },
3769
+ },
3770
+ {
3771
+ name: 'TestClass2',
3772
+ fields: {
3773
+ name: {
3774
+ type: 'String',
3775
+ },
3776
+ field2: {
3777
+ type: 'Relation',
3778
+ // @ts-expect-error
3779
+ class: 'TestClass',
3780
+ },
3781
+ },
3782
+ },
3783
+ ],
3784
+ })
3785
+
3786
+ const resAfterAdd = await client.request<any>(gql`
3787
+ mutation createTestClass2 {
3788
+ createTestClass2(input: { fields: { name: "name" } }) {
3789
+ testClass2 {
3790
+ id
3791
+ name
3792
+ }
3793
+ }
3794
+ }
3795
+ `)
3796
+
3797
+ const resAfterUpdate = await client.request<any>(gql`
3798
+ mutation updateTestClass2s {
3799
+ updateTestClass2s(
3800
+ input: {
3801
+ where: {id: {equalTo: "${resAfterAdd.createTestClass2.testClass2.id}"}}
3802
+ fields: {
3803
+ field2: { createAndAdd: [{ field1: "field1" }] }
3804
+ }
3805
+ }
3806
+ ) {
3807
+ edges {
3808
+ node {
3809
+ id
3810
+ name
3811
+ field2 {
3812
+ edges {
3813
+ node {
3814
+ field1
3815
+ }
3816
+ }
3817
+ }
3818
+ }
3819
+ }
3820
+ }
3821
+ }
3822
+ `)
3823
+
3824
+ expect(resAfterUpdate.updateTestClass2s.edges[0].node.name).toBe('name')
3825
+ expect(resAfterUpdate.updateTestClass2s.edges[0].node.field2.edges[0].node.field1).toBe(
3826
+ 'field1',
3827
+ )
3828
+
3829
+ await wabe.close()
3830
+ })
3831
+
3832
+ it('should add an object on a relation field (on updateMany)', async () => {
3833
+ const { client, wabe } = await createWabe({
3834
+ classes: [
3835
+ {
3836
+ name: 'TestClass',
3837
+ fields: {
3838
+ field1: {
3839
+ type: 'String',
3840
+ },
3841
+ },
3842
+ },
3843
+ {
3844
+ name: 'TestClass2',
3845
+ fields: {
3846
+ name: {
3847
+ type: 'String',
3848
+ },
3849
+ field2: {
3850
+ type: 'Relation',
3851
+ // @ts-expect-error
3852
+ class: 'TestClass',
3853
+ },
3854
+ },
3855
+ },
3856
+ ],
3857
+ })
3858
+
3859
+ const res = await client.request<any>(gql`
3860
+ mutation createTestClass {
3861
+ createTestClass(input: { fields: { field1: "field1" } }) {
3862
+ testClass {
3863
+ id
3864
+ }
3865
+ }
3866
+ }
3867
+ `)
3868
+
3869
+ const res2 = await client.request<any>(gql`
3870
+ mutation createTestClass {
3871
+ createTestClass(input: { fields: { field1: "field1" } }) {
3872
+ testClass {
3873
+ id
3874
+ }
3875
+ }
3876
+ }
3877
+ `)
3878
+
3879
+ const resAfterAdd = await client.request<any>(gql`
3880
+ mutation createTestClass2 {
3881
+ createTestClass2(input: { fields: { name: "name" } }) {
3882
+ testClass2 {
3883
+ id
3884
+ name
3885
+ }
3886
+ }
3887
+ }
3888
+ `)
3889
+
3890
+ await client.request<any>(gql`
3891
+ mutation updateTestClass2s {
3892
+ updateTestClass2s(
3893
+ input: {
3894
+ where: {id: {equalTo: "${resAfterAdd.createTestClass2.testClass2.id}"}}
3895
+ fields: {
3896
+ field2: { add: ["${res.createTestClass.testClass.id}"] }
3897
+ }
3898
+ }
3899
+ ) {
3900
+ edges {
3901
+ node {
3902
+ id
3903
+ name
3904
+ field2 {
3905
+ edges {
3906
+ node {
3907
+ field1
3908
+ }
3909
+ }
3910
+ }
3911
+ }
3912
+ }
3913
+ }
3914
+ }
3915
+ `)
3916
+
3917
+ const resAfterUpdate = await client.request<any>(gql`
3918
+ mutation updateTestClass2s {
3919
+ updateTestClass2s(
3920
+ input: {
3921
+ where: {id: {equalTo: "${resAfterAdd.createTestClass2.testClass2.id}"}}
3922
+ fields: {
3923
+ field2: { add: ["${res2.createTestClass.testClass.id}"] }
3924
+ }
3925
+ }
3926
+ ) {
3927
+ edges {
3928
+ node {
3929
+ id
3930
+ name
3931
+ field2 {
3932
+ edges {
3933
+ node {
3934
+ field1
3935
+ }
3936
+ }
3937
+ }
3938
+ }
3939
+ }
3940
+ }
3941
+ }
3942
+ `)
3943
+
3944
+ const field2AfterUpdate2 = (await wabe.controllers.database.getObjects({
3945
+ // @ts-expect-error
3946
+ className: 'TestClass2',
3947
+ context: {
3948
+ wabe,
3949
+ isRoot: true,
3950
+ },
3951
+ // @ts-expect-error
3952
+ select: { field2: true },
3953
+ })) as any
3954
+
3955
+ expect(field2AfterUpdate2[0]?.field2.length).toBe(2)
3956
+ expect(field2AfterUpdate2[0]?.field2).toEqual([
3957
+ { id: res.createTestClass.testClass.id },
3958
+ { id: res2.createTestClass.testClass.id },
3959
+ ])
3960
+
3961
+ expect(resAfterUpdate.updateTestClass2s.edges[0].node.name).toBe('name')
3962
+ expect(resAfterUpdate.updateTestClass2s.edges[0].node.field2.edges[0].node.field1).toBe(
3963
+ 'field1',
3964
+ )
3965
+
3966
+ await wabe.close()
3967
+ })
3968
+
3969
+ it('should remove an object on a relation field (on updateMany)', async () => {
3970
+ const { client, wabe } = await createWabe({
3971
+ classes: [
3972
+ {
3973
+ name: 'TestClass',
3974
+ fields: {
3975
+ field1: {
3976
+ type: 'String',
3977
+ },
3978
+ },
3979
+ },
3980
+ {
3981
+ name: 'TestClass2',
3982
+ fields: {
3983
+ name: {
3984
+ type: 'String',
3985
+ },
3986
+ field2: {
3987
+ type: 'Relation',
3988
+ // @ts-expect-error
3989
+ class: 'TestClass',
3990
+ },
3991
+ },
3992
+ },
3993
+ ],
3994
+ })
3995
+
3996
+ const resAfterAdd = await client.request<any>(gql`
3997
+ mutation createTestClass2 {
3998
+ createTestClass2(
3999
+ input: { fields: { name: "name", field2: { createAndAdd: [{ field1: "field1" }] } } }
4000
+ ) {
4001
+ testClass2 {
4002
+ id
4003
+ name
4004
+ field2 {
4005
+ edges {
4006
+ node {
4007
+ id
4008
+ field1
4009
+ }
4010
+ }
4011
+ }
4012
+ }
4013
+ }
4014
+ }
4015
+ `)
4016
+
4017
+ const field2BeforeUpdate2 = (await wabe.controllers.database.getObjects({
4018
+ // @ts-expect-error
4019
+ className: 'TestClass2',
4020
+ context: {
4021
+ wabe,
4022
+ isRoot: true,
4023
+ },
4024
+ // @ts-expect-error
4025
+ select: { field2: true },
4026
+ })) as any
4027
+
4028
+ expect(field2BeforeUpdate2[0]?.field2).toEqual([
4029
+ { id: resAfterAdd.createTestClass2.testClass2.field2.edges[0].node.id },
4030
+ ])
4031
+
4032
+ const resAfterUpdate = await client.request<any>(gql`
4033
+ mutation updateTestClass2s {
4034
+ updateTestClass2s(
4035
+ input: {
4036
+ where: {id: {equalTo: "${resAfterAdd.createTestClass2.testClass2.id}"}}
4037
+ fields: {
4038
+ field2: { remove: ["${resAfterAdd.createTestClass2.testClass2.field2.edges[0].node.id}"] }
4039
+ }
4040
+ }
4041
+ ) {
4042
+ edges {
4043
+ node {
4044
+ id
4045
+ name
4046
+ field2 {
4047
+ edges {
4048
+ node {
4049
+ field1
4050
+ }
4051
+ }
4052
+ }
4053
+ }
4054
+ }
4055
+ }
4056
+ }
4057
+ `)
4058
+
4059
+ const field2AfterUpdate2 = (await wabe.controllers.database.getObjects({
4060
+ // @ts-expect-error
4061
+ className: 'TestClass2',
4062
+ context: {
4063
+ wabe,
4064
+ isRoot: true,
4065
+ },
4066
+ })) as any
4067
+
4068
+ expect(field2AfterUpdate2[0]?.field2).toEqual([])
4069
+
4070
+ expect(resAfterUpdate.updateTestClass2s.edges[0].node.name).toBe('name')
4071
+ expect(resAfterUpdate.updateTestClass2s.edges[0].node.field2.edges.length).toBe(0)
4072
+
4073
+ const resAfterRemove = await client.request<any>(gql`
4074
+ query testClasses {
4075
+ testClasses {
4076
+ edges {
4077
+ node {
4078
+ field1
4079
+ }
4080
+ }
4081
+ }
4082
+ }
4083
+ `)
4084
+
4085
+ expect(resAfterRemove.testClasses.edges.length).toBe(0)
4086
+
4087
+ await wabe.close()
4088
+ })
4089
+
4090
+ it('should remove an object on a relation field (on update)', async () => {
4091
+ const { client, wabe } = await createWabe({
4092
+ classes: [
4093
+ {
4094
+ name: 'TestClass',
4095
+ fields: {
4096
+ field1: {
4097
+ type: 'String',
4098
+ },
4099
+ },
4100
+ },
4101
+ {
4102
+ name: 'TestClass2',
4103
+ fields: {
4104
+ name: {
4105
+ type: 'String',
4106
+ },
4107
+ field2: {
4108
+ type: 'Relation',
4109
+ // @ts-expect-error
4110
+ class: 'TestClass',
4111
+ },
4112
+ },
4113
+ },
4114
+ ],
4115
+ })
4116
+
4117
+ const resAfterAdd = await client.request<any>(gql`
4118
+ mutation createTestClass2 {
4119
+ createTestClass2(
4120
+ input: { fields: { name: "name", field2: { createAndAdd: [{ field1: "field1" }] } } }
4121
+ ) {
4122
+ testClass2 {
4123
+ id
4124
+ name
4125
+ field2 {
4126
+ edges {
4127
+ node {
4128
+ id
4129
+ field1
4130
+ }
4131
+ }
4132
+ }
4133
+ }
4134
+ }
4135
+ }
4136
+ `)
4137
+
4138
+ const resAfterUpdate = await client.request<any>(gql`
4139
+ mutation updateTestClass2 {
4140
+ updateTestClass2(
4141
+ input: {
4142
+ id: "${resAfterAdd.createTestClass2.testClass2.id}"
4143
+ fields: {
4144
+ field2: { remove: ["${resAfterAdd.createTestClass2.testClass2.field2.edges[0].node.id}"] }
4145
+ }
4146
+ }
4147
+ ) {
4148
+ testClass2{
4149
+ id
4150
+ name
4151
+ field2 {
4152
+ edges {
4153
+ node {
4154
+ field1
4155
+ }
4156
+ }
4157
+ }
4158
+ }
4159
+ }
4160
+ }
4161
+ `)
4162
+
4163
+ const field2AfterUpdate2 = (await wabe.controllers.database.getObjects({
4164
+ // @ts-expect-error
4165
+ className: 'TestClass2',
4166
+ context: {
4167
+ wabe,
4168
+ isRoot: true,
4169
+ },
4170
+ })) as any
4171
+
4172
+ expect(field2AfterUpdate2[0]?.field2.length).toBe(0)
4173
+
4174
+ expect(resAfterUpdate.updateTestClass2.testClass2.name).toBe('name')
4175
+ expect(resAfterUpdate.updateTestClass2.testClass2.field2.edges.length).toBe(0)
4176
+
4177
+ const resAfterRemove = await client.request<any>(gql`
4178
+ query testClasses {
4179
+ testClasses {
4180
+ edges {
4181
+ node {
4182
+ field1
4183
+ }
4184
+ }
4185
+ }
4186
+ }
4187
+ `)
4188
+
4189
+ expect(resAfterRemove.testClasses.edges.length).toBe(0)
4190
+
4191
+ await wabe.close()
4192
+ })
4193
+
4194
+ it('should filter objects where field exists (exists: true)', async () => {
4195
+ const { client, wabe } = await createWabe({
4196
+ classes: [
4197
+ {
4198
+ name: 'TestClass',
4199
+ fields: {
4200
+ name: {
4201
+ type: 'String',
4202
+ },
4203
+ age: {
4204
+ type: 'Int',
4205
+ },
4206
+ active: {
4207
+ type: 'Boolean',
4208
+ },
4209
+ },
4210
+ },
4211
+ ],
4212
+ })
4213
+
4214
+ // Create objects with and without the name field
4215
+ await client.request<any>(gql`
4216
+ mutation createObjects {
4217
+ createTestClasses(
4218
+ input: {
4219
+ fields: [{ name: "Object with name" }, { name: "Another object with name" }, { age: 25 }]
4220
+ }
4221
+ ) {
4222
+ ok
4223
+ }
4224
+ }
4225
+ `)
4226
+
4227
+ // Test exists: true - should return only objects with name field
4228
+ const res = await client.request<any>(gql`
4229
+ query testExistsTrue {
4230
+ testClasses(where: { name: { exists: true } }) {
4231
+ totalCount
4232
+ edges {
4233
+ node {
4234
+ id
4235
+ name
4236
+ }
4237
+ }
4238
+ }
4239
+ }
4240
+ `)
4241
+
4242
+ expect(res.testClasses.totalCount).toBe(2)
4243
+ expect(res.testClasses.edges.length).toBe(2)
4244
+ expect(res.testClasses.edges.every((edge: any) => edge.node.name)).toBe(true)
4245
+
4246
+ await wabe.close()
4247
+ })
4248
+
4249
+ it('should filter objects where field does not exist (exists: false)', async () => {
4250
+ const { client, wabe } = await createWabe({
4251
+ classes: [
4252
+ {
4253
+ name: 'TestClass',
4254
+ fields: {
4255
+ name: {
4256
+ type: 'String',
4257
+ },
4258
+ age: {
4259
+ type: 'Int',
4260
+ },
4261
+ active: {
4262
+ type: 'Boolean',
4263
+ },
4264
+ },
4265
+ },
4266
+ ],
4267
+ })
4268
+
4269
+ // Create objects with and without the name field
4270
+ await client.request<any>(gql`
4271
+ mutation createObjects {
4272
+ createTestClasses(
4273
+ input: { fields: [{ name: "Object with name" }, { age: 25 }, { active: true }] }
4274
+ ) {
4275
+ ok
4276
+ }
4277
+ }
4278
+ `)
4279
+
4280
+ // Test exists: false - should return only objects without name field
4281
+ const res = await client.request<any>(gql`
4282
+ query testExistsFalse {
4283
+ testClasses(where: { name: { exists: false } }) {
4284
+ totalCount
4285
+ edges {
4286
+ node {
4287
+ id
4288
+ name
4289
+ }
4290
+ }
4291
+ }
4292
+ }
4293
+ `)
4294
+
4295
+ expect(res.testClasses.totalCount).toBe(2)
4296
+ expect(res.testClasses.edges.length).toBe(2)
4297
+ expect(res.testClasses.edges.every((edge: any) => !edge.node.name)).toBe(true)
4298
+
4299
+ await wabe.close()
4300
+ })
4301
+
4302
+ it('should work with AND conditions', async () => {
4303
+ const { client, wabe } = await createWabe({
4304
+ classes: [
4305
+ {
4306
+ name: 'TestClass',
4307
+ fields: {
4308
+ name: {
4309
+ type: 'String',
4310
+ },
4311
+ age: {
4312
+ type: 'Int',
4313
+ },
4314
+ active: {
4315
+ type: 'Boolean',
4316
+ },
4317
+ },
4318
+ },
4319
+ ],
4320
+ })
4321
+ // Create objects with different field combinations
4322
+ await client.request<any>(gql`
4323
+ mutation createObjects {
4324
+ createTestClasses(
4325
+ input: {
4326
+ fields: [{ name: "John", age: 25 }, { name: "Jane", age: 30 }, { age: 35 }, { name: "Bob" }]
4327
+ }
4328
+ ) {
4329
+ ok
4330
+ }
4331
+ }
4332
+ `)
4333
+
4334
+ // Test with AND condition
4335
+ const res = await client.request<any>(gql`
4336
+ query testExistsWithAnd {
4337
+ testClasses(where: { AND: [{ name: { exists: true } }, { age: { exists: true } }] }) {
4338
+ totalCount
4339
+ edges {
4340
+ node {
4341
+ id
4342
+ name
4343
+ age
4344
+ }
4345
+ }
4346
+ }
4347
+ }
4348
+ `)
4349
+
4350
+ expect(res.testClasses.totalCount).toBe(2)
4351
+ expect(res.testClasses.edges.length).toBe(2)
4352
+ expect(res.testClasses.edges.every((edge: any) => edge.node.name && edge.node.age)).toBe(true)
4353
+
4354
+ await wabe.close()
4355
+ })
4356
+
4357
+ it('should work with OR conditions', async () => {
4358
+ const { client, wabe } = await createWabe({
4359
+ classes: [
4360
+ {
4361
+ name: 'TestClass',
4362
+ fields: {
4363
+ name: {
4364
+ type: 'String',
4365
+ },
4366
+ age: {
4367
+ type: 'Int',
4368
+ },
4369
+ active: {
4370
+ type: 'Boolean',
4371
+ },
4372
+ },
4373
+ },
4374
+ ],
4375
+ })
4376
+
4377
+ // Create objects with different field combinations
4378
+ await client.request<any>(gql`
4379
+ mutation createObjects {
4380
+ createTestClasses(input: { fields: [{ name: "John", age: 25 }, { name: "Jane" }, { age: 30 }] }) {
4381
+ ok
4382
+ }
4383
+ }
4384
+ `)
4385
+
4386
+ // Test with OR condition - objects that have either name OR age
4387
+ const res = await client.request<any>(gql`
4388
+ query testExistsWithOr {
4389
+ testClasses(where: { OR: [{ name: { exists: true } }, { age: { exists: true } }] }) {
4390
+ totalCount
4391
+ edges {
4392
+ node {
4393
+ id
4394
+ name
4395
+ age
4396
+ }
4397
+ }
4398
+ }
4399
+ }
4400
+ `)
4401
+
4402
+ // All 3 objects should match (2 have name, 2 have age, 1 has both)
4403
+ expect(res.testClasses.totalCount).toBe(3)
4404
+ expect(res.testClasses.edges.length).toBe(3)
4405
+
4406
+ await wabe.close()
4407
+ })
4408
+ })