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,168 @@
1
+ import type { OperationType } from '.'
2
+ import type { RoleEnum, ACLObject } from '../../generated/wabe'
3
+ import type { MutationData, OutputType, Select } from '../database'
4
+ import type { WabeTypes } from '../server'
5
+ import type { WabeContext } from '../server/interface'
6
+ import { contextWithRoot } from '../utils/export'
7
+
8
+ type AddACLOpptions = {
9
+ userId?: string
10
+ role?: RoleEnum
11
+ read: boolean
12
+ write: boolean
13
+ } | null
14
+
15
+ export class HookObject<
16
+ T extends WabeTypes,
17
+ K extends keyof WabeTypes['types'],
18
+ > {
19
+ public className: K
20
+ private newData: MutationData<T, K, keyof T['types'][K]> | undefined
21
+ public operationType: OperationType
22
+ public context: WabeContext<T>
23
+ public object: OutputType<T, K, keyof T['types'][K]>
24
+ // Object before any mutation, for example before delete
25
+ public originalObject: OutputType<T, K, keyof T['types'][K]> | undefined
26
+ public select: Select
27
+
28
+ constructor({
29
+ newData,
30
+ className,
31
+ operationType,
32
+ context,
33
+ object,
34
+ originalObject,
35
+ select,
36
+ }: {
37
+ className: K
38
+ newData?: MutationData<T, K, keyof T['types'][K]>
39
+ operationType: OperationType
40
+ context: WabeContext<T>
41
+ object: OutputType<T, K, keyof T['types'][K]>
42
+ originalObject?: OutputType<T, K, keyof T['types'][K]>
43
+ select: Select
44
+ }) {
45
+ this.newData = newData
46
+ this.className = className
47
+ this.operationType = operationType
48
+ this.context = context
49
+ this.object = object
50
+ this.originalObject = originalObject
51
+ this.select = select || {}
52
+ }
53
+
54
+ getUser() {
55
+ return this.context.user
56
+ }
57
+
58
+ isFieldUpdated(field: keyof T['types'][K]) {
59
+ return !!(this.newData && !!this.newData[field])
60
+ }
61
+
62
+ upsertNewData(field: keyof T['types'][K], value: any) {
63
+ if (!this.newData) return
64
+
65
+ if (!['beforeCreate', 'beforeUpdate'].includes(this.operationType))
66
+ throw new Error('Cannot set data in a hook that is not a before hook')
67
+
68
+ this.newData[field] = value
69
+ }
70
+
71
+ getNewData(): MutationData<T, K, keyof T['types'][K]> {
72
+ return this.newData || ({} as any)
73
+ }
74
+
75
+ fetch(): Promise<OutputType<T, K, keyof T['types'][K]>> {
76
+ const databaseController = this.context.wabe.controllers.database
77
+
78
+ if (!this.object?.id) return Promise.resolve(null)
79
+
80
+ return databaseController.getObject({
81
+ className: this.className,
82
+ id: this.object.id,
83
+ context: contextWithRoot(this.context),
84
+ })
85
+ }
86
+
87
+ async addACL(type: 'users' | 'roles', options: AddACLOpptions) {
88
+ const updateACL = async (newACLObject: any) => {
89
+ if (this.className === 'User') {
90
+ const currentUserId = this.object?.id
91
+
92
+ if (currentUserId)
93
+ await this.context.wabe.controllers.database.updateObject({
94
+ className: this.className,
95
+ context: contextWithRoot(this.context),
96
+ id: currentUserId,
97
+ data: {
98
+ // @ts-expect-error
99
+ acl: newACLObject,
100
+ },
101
+ select: {},
102
+ })
103
+
104
+ return
105
+ }
106
+
107
+ // @ts-expect-error
108
+ this.upsertNewData('acl', newACLObject)
109
+ }
110
+
111
+ const result =
112
+ this.className === 'User'
113
+ ? await this.context.wabe.controllers.database.getObject({
114
+ className: 'User',
115
+ // @ts-expect-error
116
+ select: { acl: true },
117
+ // @ts-expect-error
118
+ id: this.object?.id,
119
+ context: contextWithRoot(this.context),
120
+ })
121
+ : // @ts-expect-error
122
+ { acl: this.getNewData().acl }
123
+
124
+ const currentACL: ACLObject = result?.acl || {}
125
+
126
+ if (options === null) {
127
+ await updateACL({
128
+ ...currentACL,
129
+ [type]: [],
130
+ })
131
+ return
132
+ }
133
+
134
+ const { userId, role, read, write } = options
135
+
136
+ if (userId && role) throw new Error('Cannot specify both userId and role')
137
+
138
+ if (role) {
139
+ const result = await this.context.wabe.controllers.database.getObjects({
140
+ className: 'Role',
141
+ // @ts-expect-error
142
+ select: { id: true },
143
+ // @ts-expect-error
144
+ where: {
145
+ name: {
146
+ equalTo: role,
147
+ },
148
+ },
149
+ context: contextWithRoot(this.context),
150
+ })
151
+
152
+ const roleId = result[0]?.id
153
+
154
+ await updateACL({
155
+ ...currentACL,
156
+ [type]: [...(currentACL?.[type] || []), { roleId, read, write }],
157
+ })
158
+
159
+ return
160
+ }
161
+
162
+ // User ACL
163
+ await updateACL({
164
+ ...currentACL,
165
+ [type]: [...(currentACL?.[type] || []), { userId, read, write }],
166
+ })
167
+ }
168
+ }
@@ -0,0 +1,76 @@
1
+ import type { ProviderInterface } from '../authentication'
2
+ import { getAuthenticationMethod } from '../authentication/utils'
3
+ import type { DevWabeTypes } from '../utils/helper'
4
+ import type { HookObject } from './HookObject'
5
+
6
+ export const defaultCallAuthenticationProviderOnBeforeCreateUser = async (
7
+ hookObject: HookObject<any, any>,
8
+ ) => {
9
+ if (
10
+ !hookObject.isFieldUpdated('authentication') ||
11
+ hookObject.getNewData().isOauth
12
+ )
13
+ return
14
+
15
+ const context = hookObject.context
16
+
17
+ const authentication = hookObject.getNewData().authentication
18
+
19
+ // Exception for SRP
20
+ if (authentication.emailPasswordSRP) return
21
+
22
+ const { provider, name } = getAuthenticationMethod<
23
+ DevWabeTypes,
24
+ ProviderInterface<DevWabeTypes>
25
+ >(Object.keys(authentication), context)
26
+
27
+ const inputOfTheGoodAuthenticationMethod = authentication[name]
28
+
29
+ const { authenticationDataToSave } = await provider.onSignUp({
30
+ input: inputOfTheGoodAuthenticationMethod,
31
+ context,
32
+ })
33
+
34
+ hookObject.upsertNewData('authentication', {
35
+ [name]: authenticationDataToSave,
36
+ })
37
+ }
38
+
39
+ export const defaultCallAuthenticationProviderOnBeforeUpdateUser = async (
40
+ hookObject: HookObject<any, any>,
41
+ ) => {
42
+ if (
43
+ !hookObject.isFieldUpdated('authentication') ||
44
+ hookObject.getNewData().isOauth
45
+ )
46
+ return
47
+
48
+ const context = hookObject.context
49
+
50
+ const authentication = hookObject.getNewData().authentication
51
+
52
+ // Exception for SRP
53
+ if (authentication.emailPasswordSRP) return
54
+
55
+ const { provider, name } = getAuthenticationMethod<
56
+ DevWabeTypes,
57
+ ProviderInterface<DevWabeTypes>
58
+ >(Object.keys(authentication), context)
59
+
60
+ if (!provider.onUpdateAuthenticationData) return
61
+
62
+ const inputOfTheGoodAuthenticationMethod = authentication[name]
63
+
64
+ if (!hookObject.object?.id) return
65
+
66
+ const { authenticationDataToSave } =
67
+ await provider.onUpdateAuthenticationData({
68
+ context,
69
+ input: inputOfTheGoodAuthenticationMethod,
70
+ userId: hookObject.object.id,
71
+ })
72
+
73
+ hookObject.upsertNewData('authentication', {
74
+ [name]: authenticationDataToSave,
75
+ })
76
+ }
@@ -0,0 +1,77 @@
1
+ import { describe, beforeAll, afterAll, afterEach, it, expect } from 'bun:test'
2
+ import type { Wabe } from 'src'
3
+ import type { DevWabeTypes } from 'src/utils/helper'
4
+ import { setupTests, closeTests } from 'src/utils/testHelper'
5
+
6
+ describe('createUser hook', () => {
7
+ let wabe: Wabe<DevWabeTypes>
8
+
9
+ beforeAll(async () => {
10
+ const setup = await setupTests()
11
+ wabe = setup.wabe
12
+ })
13
+
14
+ afterAll(async () => {
15
+ await closeTests(wabe)
16
+ })
17
+
18
+ afterEach(async () => {
19
+ await wabe.controllers.database.clearDatabase()
20
+ })
21
+
22
+ it('should throw an error if disableSignUp is true and user is anonymous', () => {
23
+ if (wabe.config) {
24
+ wabe.config.authentication = {
25
+ disableSignUp: true,
26
+ }
27
+ }
28
+
29
+ expect(
30
+ wabe.controllers.database.createObject({
31
+ className: 'User',
32
+ context: {
33
+ wabe,
34
+ isRoot: false,
35
+ },
36
+ data: {
37
+ email: 'email@test.fr',
38
+ },
39
+ select: {},
40
+ }),
41
+ ).rejects.toThrow('Sign up is disabled')
42
+
43
+ if (wabe.config) {
44
+ wabe.config.authentication = {
45
+ disableSignUp: false,
46
+ }
47
+ }
48
+ })
49
+
50
+ it('should not throw an error if disableSignUp is true and user is root', async () => {
51
+ if (wabe.config) {
52
+ wabe.config.authentication = {
53
+ disableSignUp: true,
54
+ }
55
+ }
56
+
57
+ const res = await wabe.controllers.database.createObject({
58
+ className: 'User',
59
+ context: {
60
+ wabe,
61
+ isRoot: true,
62
+ },
63
+ data: {
64
+ email: 'email@test.fr',
65
+ },
66
+ select: { id: true },
67
+ })
68
+
69
+ expect(res?.id).toBeDefined()
70
+
71
+ if (wabe.config) {
72
+ wabe.config.authentication = {
73
+ disableSignUp: false,
74
+ }
75
+ }
76
+ })
77
+ })
@@ -0,0 +1,10 @@
1
+ import type { HookObject } from './HookObject'
2
+
3
+ export const defaultBeforeCreateUser = (object: HookObject<any, any>) => {
4
+ const context = object.context
5
+
6
+ if (context.isRoot) return
7
+
8
+ if (context.wabe.config.authentication?.disableSignUp && !context.user)
9
+ throw new Error('Sign up is disabled')
10
+ }
@@ -0,0 +1,187 @@
1
+ import { describe, expect, it, spyOn } from 'bun:test'
2
+ import {
3
+ defaultBeforeCreateForCreatedAt,
4
+ defaultBeforeCreateForDefaultValue,
5
+ defaultBeforeUpdateForUpdatedAt,
6
+ } from './defaultFields'
7
+ import { HookObject } from './HookObject'
8
+ import { OperationType } from '.'
9
+ import type { DevWabeTypes } from '../utils/helper'
10
+
11
+ describe('Default fields', () => {
12
+ const now = new Date()
13
+
14
+ describe('CreatedAt and UpdatedAt', () => {
15
+ it('should add createdAt and updatedAt value on insert operation type', () => {
16
+ const hookObject = new HookObject<DevWabeTypes, 'User'>({
17
+ className: 'User',
18
+ operationType: OperationType.BeforeCreate,
19
+ newData: {
20
+ email: 'email@test.fr',
21
+ } as any,
22
+ context: {} as any,
23
+ object: {} as any,
24
+ select: {},
25
+ })
26
+
27
+ const spyHookObjectUpsertNewData = spyOn(hookObject, 'upsertNewData')
28
+
29
+ defaultBeforeCreateForCreatedAt(hookObject)
30
+
31
+ expect(spyHookObjectUpsertNewData).toHaveBeenCalledTimes(2)
32
+ expect(spyHookObjectUpsertNewData).toHaveBeenNthCalledWith(
33
+ 1,
34
+ 'createdAt',
35
+ expect.any(Date),
36
+ )
37
+ expect(spyHookObjectUpsertNewData).toHaveBeenNthCalledWith(
38
+ 2,
39
+ 'updatedAt',
40
+ expect.any(Date),
41
+ )
42
+
43
+ const createdAt = spyHookObjectUpsertNewData.mock.calls[0]?.[1]
44
+
45
+ expect(createdAt.getDay()).toEqual(now.getDay())
46
+ expect(createdAt.getMonth()).toEqual(now.getMonth())
47
+ expect(createdAt.getFullYear()).toEqual(now.getFullYear())
48
+
49
+ const updatedAt = spyHookObjectUpsertNewData.mock.calls[1]?.[1]
50
+ expect(updatedAt.getDay()).toEqual(now.getDay())
51
+ expect(updatedAt.getMonth()).toEqual(now.getMonth())
52
+ expect(updatedAt.getFullYear()).toEqual(now.getFullYear())
53
+ })
54
+
55
+ it('should add updatedAt value on update operation type', () => {
56
+ const hookObject = new HookObject<DevWabeTypes, 'User'>({
57
+ className: 'User',
58
+ operationType: OperationType.BeforeUpdate,
59
+ newData: {
60
+ email: 'email@test.fr',
61
+ } as any,
62
+ context: {} as any,
63
+ object: {} as any,
64
+ select: {},
65
+ })
66
+
67
+ const spyHookObjectUpsertNewData = spyOn(hookObject, 'upsertNewData')
68
+
69
+ defaultBeforeUpdateForUpdatedAt(hookObject)
70
+ expect(spyHookObjectUpsertNewData).toHaveBeenCalledTimes(1)
71
+ expect(spyHookObjectUpsertNewData).toHaveBeenCalledWith(
72
+ 'updatedAt',
73
+ expect.any(Date),
74
+ )
75
+
76
+ const updatedAt = spyHookObjectUpsertNewData.mock.calls[0]?.[1]
77
+
78
+ // Don't test hours to avoid flaky
79
+ expect(updatedAt.getDay()).toEqual(now.getDay())
80
+ expect(updatedAt.getMonth()).toEqual(now.getMonth())
81
+ expect(updatedAt.getFullYear()).toEqual(now.getFullYear())
82
+ })
83
+
84
+ it('should not overwrite if the createdAt field is already set', () => {
85
+ const hookObject = new HookObject<DevWabeTypes, 'User'>({
86
+ className: 'User',
87
+ operationType: OperationType.BeforeCreate,
88
+ newData: {
89
+ email: 'email@test.fr',
90
+ createdAt: now,
91
+ } as any,
92
+ context: {} as any,
93
+ object: {} as any,
94
+ select: {},
95
+ })
96
+
97
+ const spyHookObjectUpsertNewData = spyOn(hookObject, 'upsertNewData')
98
+
99
+ defaultBeforeCreateForCreatedAt(hookObject)
100
+
101
+ expect(spyHookObjectUpsertNewData).toHaveBeenCalledTimes(1)
102
+ })
103
+
104
+ it('should not overwrite if the updatedAt field is already set', () => {
105
+ const hookObject = new HookObject<DevWabeTypes, 'User'>({
106
+ className: 'User',
107
+ operationType: OperationType.BeforeCreate,
108
+ newData: {
109
+ email: 'email@test.fr',
110
+ updatedAt: now,
111
+ } as any,
112
+ context: {} as any,
113
+ object: {} as any,
114
+ select: {},
115
+ })
116
+
117
+ const spyHookObjectUpsertNewData = spyOn(hookObject, 'upsertNewData')
118
+
119
+ defaultBeforeCreateForCreatedAt(hookObject)
120
+
121
+ expect(spyHookObjectUpsertNewData).toHaveBeenCalledTimes(1)
122
+ })
123
+ })
124
+
125
+ describe('Default value', () => {
126
+ const config = {
127
+ schema: {
128
+ classes: [
129
+ {
130
+ name: 'User',
131
+ fields: {
132
+ name: { type: 'String' },
133
+ age: { type: 'Int' },
134
+ isAdmin: {
135
+ type: 'Boolean',
136
+ defaultValue: false,
137
+ },
138
+ },
139
+ },
140
+ ],
141
+ },
142
+ } as any
143
+
144
+ const context = { wabe: { config } } as any
145
+
146
+ it('should add the value if a default value is defined in schema but not specified', async () => {
147
+ const hookObject = new HookObject<DevWabeTypes, 'User'>({
148
+ className: 'User',
149
+ operationType: OperationType.BeforeCreate,
150
+ newData: {
151
+ id: 'id',
152
+ email: 'email@test.fr',
153
+ } as any,
154
+ context,
155
+ object: {} as any,
156
+ select: {},
157
+ })
158
+
159
+ const spyHookObjectUpsertNewData = spyOn(hookObject, 'upsertNewData')
160
+
161
+ await defaultBeforeCreateForDefaultValue(hookObject)
162
+
163
+ expect(spyHookObjectUpsertNewData).toHaveBeenCalledTimes(1)
164
+ })
165
+
166
+ it('should not add a default value if a value is specified', async () => {
167
+ const hookObject = new HookObject<DevWabeTypes, 'User'>({
168
+ className: 'User',
169
+ operationType: OperationType.BeforeCreate,
170
+ newData: {
171
+ id: 'id',
172
+ email: 'email@test.fr',
173
+ isAdmin: true,
174
+ } as any,
175
+ context,
176
+ object: {} as any,
177
+ select: {},
178
+ })
179
+
180
+ const spyHookObjectUpsertNewData = spyOn(hookObject, 'upsertNewData')
181
+
182
+ await defaultBeforeCreateForDefaultValue(hookObject)
183
+
184
+ expect(spyHookObjectUpsertNewData).toHaveBeenCalledTimes(0)
185
+ })
186
+ })
187
+ })
@@ -0,0 +1,40 @@
1
+ import { getClassFromClassName } from '../utils'
2
+ import type { DevWabeTypes } from '../utils/helper'
3
+ import type { HookObject } from './HookObject'
4
+
5
+ export const defaultBeforeCreateForCreatedAt = (
6
+ object: HookObject<any, any>,
7
+ ) => {
8
+ if (!object.isFieldUpdated('createdAt'))
9
+ object.upsertNewData('createdAt', new Date())
10
+
11
+ if (!object.isFieldUpdated('updatedAt'))
12
+ object.upsertNewData('updatedAt', new Date())
13
+ }
14
+
15
+ export const defaultBeforeUpdateForUpdatedAt = (
16
+ object: HookObject<any, any>,
17
+ ) => {
18
+ object.upsertNewData('updatedAt', new Date())
19
+ }
20
+
21
+ export const defaultBeforeCreateForDefaultValue = (
22
+ object: HookObject<any, any>,
23
+ ) => {
24
+ const schemaClass = getClassFromClassName<DevWabeTypes>(
25
+ object.className,
26
+ object.context.wabe.config,
27
+ )
28
+ const allFields = Object.keys(schemaClass.fields)
29
+ allFields.forEach((field) => {
30
+ const currentSchemaField = schemaClass.fields[field]
31
+ if (
32
+ !object.isFieldUpdated(field) &&
33
+ currentSchemaField?.type !== 'Pointer' &&
34
+ currentSchemaField?.type !== 'Relation' &&
35
+ currentSchemaField?.type !== 'File' &&
36
+ currentSchemaField?.defaultValue !== undefined
37
+ )
38
+ object.upsertNewData(field, currentSchemaField.defaultValue)
39
+ })
40
+ }