wabe 0.6.9 → 0.6.10

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