wabe 0.6.9 → 0.6.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +156 -50
- package/bucket/b.txt +1 -0
- package/dev/index.ts +215 -0
- package/dist/authentication/Session.d.ts +4 -1
- package/dist/authentication/interface.d.ts +16 -0
- package/dist/cron/index.d.ts +0 -1
- package/dist/database/DatabaseController.d.ts +41 -13
- package/dist/database/interface.d.ts +1 -0
- package/dist/email/DevAdapter.d.ts +0 -1
- package/dist/email/interface.d.ts +1 -1
- package/dist/graphql/resolvers.d.ts +4 -2
- package/dist/hooks/index.d.ts +8 -2
- package/dist/index.d.ts +0 -1
- package/dist/index.js +32144 -32058
- package/dist/schema/Schema.d.ts +2 -1
- package/dist/server/index.d.ts +4 -2
- package/dist/utils/crypto.d.ts +7 -0
- package/dist/utils/helper.d.ts +5 -1
- package/generated/schema.graphql +22 -14
- package/generated/wabe.ts +4 -4
- package/package.json +23 -23
- package/src/authentication/OTP.test.ts +69 -0
- package/src/authentication/OTP.ts +64 -0
- package/src/authentication/Session.test.ts +629 -0
- package/src/authentication/Session.ts +493 -0
- package/src/authentication/defaultAuthentication.ts +209 -0
- package/src/authentication/index.ts +3 -0
- package/src/authentication/interface.ts +155 -0
- package/src/authentication/oauth/GitHub.test.ts +91 -0
- package/src/authentication/oauth/GitHub.ts +121 -0
- package/src/authentication/oauth/Google.test.ts +91 -0
- package/src/authentication/oauth/Google.ts +101 -0
- package/src/authentication/oauth/Oauth2Client.test.ts +219 -0
- package/src/authentication/oauth/Oauth2Client.ts +135 -0
- package/src/authentication/oauth/index.ts +2 -0
- package/src/authentication/oauth/utils.test.ts +33 -0
- package/src/authentication/oauth/utils.ts +27 -0
- package/src/authentication/providers/EmailOTP.test.ts +127 -0
- package/src/authentication/providers/EmailOTP.ts +84 -0
- package/src/authentication/providers/EmailPassword.test.ts +176 -0
- package/src/authentication/providers/EmailPassword.ts +116 -0
- package/src/authentication/providers/EmailPasswordSRP.test.ts +208 -0
- package/src/authentication/providers/EmailPasswordSRP.ts +179 -0
- package/src/authentication/providers/GitHub.ts +24 -0
- package/src/authentication/providers/Google.ts +24 -0
- package/src/authentication/providers/OAuth.test.ts +185 -0
- package/src/authentication/providers/OAuth.ts +106 -0
- package/src/authentication/providers/PhonePassword.test.ts +176 -0
- package/src/authentication/providers/PhonePassword.ts +115 -0
- package/src/authentication/providers/QRCodeOTP.test.ts +77 -0
- package/src/authentication/providers/QRCodeOTP.ts +58 -0
- package/src/authentication/providers/index.ts +6 -0
- package/src/authentication/resolvers/refreshResolver.test.ts +30 -0
- package/src/authentication/resolvers/refreshResolver.ts +19 -0
- package/src/authentication/resolvers/signInWithResolver.inte.test.ts +59 -0
- package/src/authentication/resolvers/signInWithResolver.test.ts +293 -0
- package/src/authentication/resolvers/signInWithResolver.ts +92 -0
- package/src/authentication/resolvers/signOutResolver.test.ts +38 -0
- package/src/authentication/resolvers/signOutResolver.ts +18 -0
- package/src/authentication/resolvers/signUpWithResolver.test.ts +180 -0
- package/src/authentication/resolvers/signUpWithResolver.ts +65 -0
- package/src/authentication/resolvers/verifyChallenge.test.ts +133 -0
- package/src/authentication/resolvers/verifyChallenge.ts +62 -0
- package/src/authentication/roles.test.ts +49 -0
- package/src/authentication/roles.ts +40 -0
- package/src/authentication/utils.test.ts +97 -0
- package/src/authentication/utils.ts +39 -0
- package/src/cache/InMemoryCache.test.ts +62 -0
- package/src/cache/InMemoryCache.ts +45 -0
- package/src/cron/index.test.ts +17 -0
- package/src/cron/index.ts +43 -0
- package/src/database/DatabaseController.test.ts +613 -0
- package/src/database/DatabaseController.ts +1007 -0
- package/src/database/index.test.ts +1372 -0
- package/src/database/index.ts +9 -0
- package/src/database/interface.ts +302 -0
- package/src/email/DevAdapter.ts +7 -0
- package/src/email/EmailController.test.ts +29 -0
- package/src/email/EmailController.ts +13 -0
- package/src/email/index.ts +2 -0
- package/src/email/interface.ts +36 -0
- package/src/email/templates/sendOtpCode.ts +120 -0
- package/src/file/FileController.ts +28 -0
- package/src/file/FileDevAdapter.ts +51 -0
- package/src/file/hookDeleteFile.ts +25 -0
- package/src/file/hookReadFile.ts +66 -0
- package/src/file/hookUploadFile.ts +50 -0
- package/src/file/index.test.ts +932 -0
- package/src/file/index.ts +2 -0
- package/src/file/interface.ts +39 -0
- package/src/graphql/GraphQLSchema.test.ts +4408 -0
- package/src/graphql/GraphQLSchema.ts +880 -0
- package/src/graphql/index.ts +2 -0
- package/src/graphql/parseGraphqlSchema.ts +85 -0
- package/src/graphql/parser.test.ts +203 -0
- package/src/graphql/parser.ts +542 -0
- package/src/graphql/pointerAndRelationFunction.ts +191 -0
- package/src/graphql/resolvers.ts +442 -0
- package/src/graphql/tests/aggregation.test.ts +1115 -0
- package/src/graphql/tests/e2e.test.ts +590 -0
- package/src/graphql/tests/scalars.test.ts +250 -0
- package/src/graphql/types.ts +227 -0
- package/src/hooks/HookObject.test.ts +122 -0
- package/src/hooks/HookObject.ts +165 -0
- package/src/hooks/authentication.ts +67 -0
- package/src/hooks/createUser.test.ts +77 -0
- package/src/hooks/createUser.ts +10 -0
- package/src/hooks/defaultFields.test.ts +176 -0
- package/src/hooks/defaultFields.ts +32 -0
- package/src/hooks/deleteSession.test.ts +181 -0
- package/src/hooks/deleteSession.ts +20 -0
- package/src/hooks/hashFieldHook.test.ts +152 -0
- package/src/hooks/hashFieldHook.ts +89 -0
- package/src/hooks/index.test.ts +258 -0
- package/src/hooks/index.ts +414 -0
- package/src/hooks/permissions.test.ts +412 -0
- package/src/hooks/permissions.ts +93 -0
- package/src/hooks/protected.test.ts +551 -0
- package/src/hooks/protected.ts +60 -0
- package/src/hooks/searchableFields.test.ts +147 -0
- package/src/hooks/searchableFields.ts +86 -0
- package/src/hooks/session.test.ts +134 -0
- package/src/hooks/session.ts +76 -0
- package/src/hooks/setEmail.test.ts +216 -0
- package/src/hooks/setEmail.ts +33 -0
- package/src/hooks/setupAcl.test.ts +618 -0
- package/src/hooks/setupAcl.ts +25 -0
- package/src/index.ts +9 -0
- package/src/schema/Schema.test.ts +482 -0
- package/src/schema/Schema.ts +757 -0
- package/src/schema/defaultResolvers.ts +93 -0
- package/src/schema/index.ts +1 -0
- package/src/schema/resolvers/meResolver.test.ts +62 -0
- package/src/schema/resolvers/meResolver.ts +10 -0
- package/src/schema/resolvers/resetPassword.test.ts +341 -0
- package/src/schema/resolvers/resetPassword.ts +63 -0
- package/src/schema/resolvers/sendEmail.test.ts +118 -0
- package/src/schema/resolvers/sendEmail.ts +21 -0
- package/src/schema/resolvers/sendOtpCode.test.ts +141 -0
- package/src/schema/resolvers/sendOtpCode.ts +52 -0
- package/src/security.test.ts +3434 -0
- package/src/server/defaultSessionHandler.test.ts +62 -0
- package/src/server/defaultSessionHandler.ts +105 -0
- package/src/server/generateCodegen.ts +433 -0
- package/src/server/index.test.ts +532 -0
- package/src/server/index.ts +334 -0
- package/src/server/interface.ts +11 -0
- package/src/server/routes/authHandler.ts +169 -0
- package/src/server/routes/index.ts +39 -0
- package/src/utils/crypto.test.ts +41 -0
- package/src/utils/crypto.ts +105 -0
- package/src/utils/export.ts +11 -0
- package/src/utils/helper.ts +204 -0
- package/src/utils/index.test.ts +11 -0
- package/src/utils/index.ts +189 -0
- package/src/utils/preload.ts +8 -0
- package/src/utils/testHelper.ts +116 -0
- package/tsconfig.json +32 -0
- package/bunfig.toml +0 -4
- package/dist/ai/index.d.ts +0 -1
- package/dist/ai/interface.d.ts +0 -9
- /package/dist/server/{defaultHandlers.d.ts → defaultSessionHandler.d.ts} +0 -0
|
@@ -0,0 +1,1007 @@
|
|
|
1
|
+
import { selectFieldsWithoutPrivateFields } from 'src/utils/helper'
|
|
2
|
+
import type { WabeTypes } from '../..'
|
|
3
|
+
import { initializeHook, OperationType } from '../hooks'
|
|
4
|
+
import type { SchemaInterface } from '../schema'
|
|
5
|
+
import type { WabeContext } from '../server/interface'
|
|
6
|
+
import { contextWithRoot, notEmpty } from '../utils/export'
|
|
7
|
+
import type { DevWabeTypes } from '../utils/helper'
|
|
8
|
+
import type {
|
|
9
|
+
CountOptions,
|
|
10
|
+
CreateObjectOptions,
|
|
11
|
+
CreateObjectsOptions,
|
|
12
|
+
DatabaseAdapter,
|
|
13
|
+
DeleteObjectOptions,
|
|
14
|
+
DeleteObjectsOptions,
|
|
15
|
+
GetObjectOptions,
|
|
16
|
+
GetObjectsOptions,
|
|
17
|
+
OutputType,
|
|
18
|
+
UpdateObjectOptions,
|
|
19
|
+
UpdateObjectsOptions,
|
|
20
|
+
WhereType,
|
|
21
|
+
} from './interface'
|
|
22
|
+
|
|
23
|
+
export type Select = Record<string, boolean>
|
|
24
|
+
type SelectWithObject = Record<string, object | boolean>
|
|
25
|
+
|
|
26
|
+
export class DatabaseController<T extends WabeTypes> {
|
|
27
|
+
public adapter: DatabaseAdapter<T>
|
|
28
|
+
|
|
29
|
+
constructor(adapter: DatabaseAdapter<T>) {
|
|
30
|
+
this.adapter = adapter
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Get a class definition from the schema by name (case-insensitive)
|
|
35
|
+
*/
|
|
36
|
+
_getClass(className: string | keyof T['types'], context: WabeContext<T>) {
|
|
37
|
+
return context.wabe.config.schema?.classes?.find(
|
|
38
|
+
(c) => c.name.toLowerCase() === String(className).toLowerCase(),
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Get field type and target class information
|
|
44
|
+
*/
|
|
45
|
+
_getFieldType(originClassName: string, fieldName: string, context: WabeContext<T>) {
|
|
46
|
+
const realClass = this._getClass(originClassName, context)
|
|
47
|
+
return realClass?.fields[fieldName] as { type: string; class?: string } | undefined
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
_getSelectMinusPointersAndRelations({
|
|
51
|
+
className,
|
|
52
|
+
context,
|
|
53
|
+
select,
|
|
54
|
+
}: {
|
|
55
|
+
className: keyof T['types']
|
|
56
|
+
context: WabeContext<T>
|
|
57
|
+
select?: SelectWithObject
|
|
58
|
+
}): {
|
|
59
|
+
pointers: Record<string, { className: string; select: Select }>
|
|
60
|
+
selectWithoutPointers: Select
|
|
61
|
+
} {
|
|
62
|
+
const realClass = this._getClass(className, context)
|
|
63
|
+
|
|
64
|
+
if (!realClass) throw new Error('Class not found in schema')
|
|
65
|
+
|
|
66
|
+
if (!select) return { pointers: {}, selectWithoutPointers: {} }
|
|
67
|
+
|
|
68
|
+
const pointers: Record<string, { className: string; select: Select }> = {}
|
|
69
|
+
const selectWithoutPointers: Select = {}
|
|
70
|
+
|
|
71
|
+
const selectEntries = Object.entries(
|
|
72
|
+
context.isRoot ? select : selectFieldsWithoutPrivateFields(select),
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
for (const [fieldName, value] of selectEntries) {
|
|
76
|
+
const field = realClass.fields[fieldName]
|
|
77
|
+
const isPointerOrRelation = field?.type === 'Pointer' || field?.type === 'Relation'
|
|
78
|
+
|
|
79
|
+
if (!isPointerOrRelation) {
|
|
80
|
+
selectWithoutPointers[fieldName] = true
|
|
81
|
+
} else {
|
|
82
|
+
pointers[fieldName] = {
|
|
83
|
+
className: (field as { class: string }).class,
|
|
84
|
+
select: (value === true ? undefined : value) as Select,
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return { pointers, selectWithoutPointers }
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
_isFieldOfType(
|
|
93
|
+
originClassName: string,
|
|
94
|
+
pointerField: string,
|
|
95
|
+
expectedType: 'Pointer' | 'Relation',
|
|
96
|
+
context: WabeContext<T>,
|
|
97
|
+
currentClassName?: string,
|
|
98
|
+
): boolean {
|
|
99
|
+
if (!currentClassName) return false
|
|
100
|
+
|
|
101
|
+
const field = this._getFieldType(originClassName, pointerField, context)
|
|
102
|
+
return (
|
|
103
|
+
field?.type === expectedType && field.class?.toLowerCase() === currentClassName.toLowerCase()
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async _getWhereObjectWithPointerOrRelation<U extends keyof T['types']>(
|
|
108
|
+
className: U,
|
|
109
|
+
where: WhereType<T, U>,
|
|
110
|
+
context: WabeContext<T>,
|
|
111
|
+
) {
|
|
112
|
+
const whereKeys = Object.keys(where) as Array<keyof WhereType<T, U>>
|
|
113
|
+
|
|
114
|
+
const realClass = this._getClass(className, context)
|
|
115
|
+
|
|
116
|
+
const newWhereObject = await whereKeys.reduce(async (acc, whereKey) => {
|
|
117
|
+
const currentAcc = await acc
|
|
118
|
+
|
|
119
|
+
const typedWhereKey = whereKey as string
|
|
120
|
+
|
|
121
|
+
const field = realClass?.fields[typedWhereKey]
|
|
122
|
+
|
|
123
|
+
if (typedWhereKey === 'AND' || typedWhereKey === 'OR') {
|
|
124
|
+
const newWhere = await Promise.all(
|
|
125
|
+
(where[typedWhereKey] as any).map((whereObject: any) =>
|
|
126
|
+
this._getWhereObjectWithPointerOrRelation(className, whereObject, context),
|
|
127
|
+
),
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
...currentAcc,
|
|
132
|
+
[typedWhereKey]: newWhere,
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (field?.type !== 'Pointer' && field?.type !== 'Relation') return acc
|
|
137
|
+
|
|
138
|
+
// @ts-expect-error
|
|
139
|
+
const fieldTargetClass = field.class
|
|
140
|
+
|
|
141
|
+
const defaultWhere = where[typedWhereKey]
|
|
142
|
+
|
|
143
|
+
const objects = await this.getObjects({
|
|
144
|
+
className: fieldTargetClass,
|
|
145
|
+
// @ts-expect-error
|
|
146
|
+
select: { id: true },
|
|
147
|
+
// @ts-expect-error
|
|
148
|
+
where: defaultWhere,
|
|
149
|
+
context,
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
...acc,
|
|
154
|
+
// If we don't found any object we just execute the query with the default where
|
|
155
|
+
// Without any transformation for pointer or relation
|
|
156
|
+
// Ensure the 'in' condition is not empty to avoid unauthorized access
|
|
157
|
+
...(objects.length > 0
|
|
158
|
+
? {
|
|
159
|
+
[typedWhereKey]: {
|
|
160
|
+
in: objects.map((object) => object?.id).filter(notEmpty),
|
|
161
|
+
},
|
|
162
|
+
}
|
|
163
|
+
: {}),
|
|
164
|
+
}
|
|
165
|
+
}, Promise.resolve({}))
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
...where,
|
|
169
|
+
...newWhereObject,
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
_buildWhereWithACL<K extends keyof T['types']>(
|
|
174
|
+
where: WhereType<T, K>,
|
|
175
|
+
context: WabeContext<T>,
|
|
176
|
+
operation: 'write' | 'read',
|
|
177
|
+
): WhereType<T, K> {
|
|
178
|
+
if (context.isRoot) return where
|
|
179
|
+
|
|
180
|
+
const roleId = context.user?.role?.id
|
|
181
|
+
const userId = context.user?.id
|
|
182
|
+
|
|
183
|
+
// If we have an user we good right we return
|
|
184
|
+
// If we don't have user we check role
|
|
185
|
+
// If the role is good we return
|
|
186
|
+
|
|
187
|
+
// @ts-expect-error
|
|
188
|
+
return {
|
|
189
|
+
AND: [
|
|
190
|
+
{ ...where },
|
|
191
|
+
// If the user is not connected we need to have a null acl
|
|
192
|
+
!userId
|
|
193
|
+
? {
|
|
194
|
+
acl: { equalTo: null },
|
|
195
|
+
}
|
|
196
|
+
: undefined,
|
|
197
|
+
// If we have user or role we need to check the acl
|
|
198
|
+
userId || roleId
|
|
199
|
+
? {
|
|
200
|
+
OR: [
|
|
201
|
+
{
|
|
202
|
+
acl: { equalTo: null },
|
|
203
|
+
},
|
|
204
|
+
userId
|
|
205
|
+
? {
|
|
206
|
+
acl: {
|
|
207
|
+
users: {
|
|
208
|
+
contains: {
|
|
209
|
+
userId,
|
|
210
|
+
[operation]: true,
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
}
|
|
215
|
+
: undefined,
|
|
216
|
+
roleId
|
|
217
|
+
? {
|
|
218
|
+
AND: [
|
|
219
|
+
{
|
|
220
|
+
acl: {
|
|
221
|
+
users: {
|
|
222
|
+
notContains: {
|
|
223
|
+
userId,
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
acl: {
|
|
230
|
+
roles: {
|
|
231
|
+
contains: {
|
|
232
|
+
roleId,
|
|
233
|
+
[operation]: true,
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
],
|
|
239
|
+
}
|
|
240
|
+
: undefined,
|
|
241
|
+
].filter(notEmpty),
|
|
242
|
+
}
|
|
243
|
+
: undefined,
|
|
244
|
+
].filter(notEmpty),
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Private helper to load a single object for hooks (skips hooks to avoid recursion)
|
|
250
|
+
*/
|
|
251
|
+
_loadObjectForHooks(className: keyof T['types'], context: WabeContext<T>) {
|
|
252
|
+
return (id: string) =>
|
|
253
|
+
this.getObject({
|
|
254
|
+
className,
|
|
255
|
+
context: contextWithRoot(context),
|
|
256
|
+
id,
|
|
257
|
+
_skipHooks: true,
|
|
258
|
+
})
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Private helper to load multiple objects for hooks (skips hooks to avoid recursion)
|
|
263
|
+
*/
|
|
264
|
+
_loadObjectsForHooks(className: keyof T['types'], context: WabeContext<T>) {
|
|
265
|
+
return ({ where, ids }: { where?: WhereType<DevWabeTypes, any>; ids: string[] }) =>
|
|
266
|
+
this.getObjects({
|
|
267
|
+
className,
|
|
268
|
+
context: contextWithRoot(context),
|
|
269
|
+
// @ts-expect-error
|
|
270
|
+
where: where ? where : { id: { in: ids } },
|
|
271
|
+
_skipHooks: true,
|
|
272
|
+
})
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Generic executor for single object operations (create, update, delete, read)
|
|
277
|
+
* Encapsulates the hook lifecycle: Init -> Before -> Adapter -> After
|
|
278
|
+
*/
|
|
279
|
+
async _executeSingleOperationWithHooks<
|
|
280
|
+
K extends keyof T['types'],
|
|
281
|
+
U extends keyof T['types'][K],
|
|
282
|
+
>({
|
|
283
|
+
operationTypeBefore,
|
|
284
|
+
operationTypeAfter,
|
|
285
|
+
className,
|
|
286
|
+
context,
|
|
287
|
+
data,
|
|
288
|
+
select,
|
|
289
|
+
id,
|
|
290
|
+
adapterCallback,
|
|
291
|
+
inputObject,
|
|
292
|
+
}: {
|
|
293
|
+
operationTypeBefore: OperationType
|
|
294
|
+
operationTypeAfter: OperationType
|
|
295
|
+
className: K
|
|
296
|
+
context: WabeContext<T>
|
|
297
|
+
data?: any
|
|
298
|
+
select?: Select
|
|
299
|
+
id?: string
|
|
300
|
+
inputObject?: OutputType<T, K, U>
|
|
301
|
+
adapterCallback: (newData: any) => Promise<OutputType<T, K, U> | { id: string } | null>
|
|
302
|
+
}) {
|
|
303
|
+
const hook = initializeHook({
|
|
304
|
+
className,
|
|
305
|
+
context,
|
|
306
|
+
newData: data,
|
|
307
|
+
// @ts-expect-error
|
|
308
|
+
select,
|
|
309
|
+
objectLoader: this._loadObjectForHooks(className, context),
|
|
310
|
+
objectsLoader: this._loadObjectsForHooks(className, context),
|
|
311
|
+
})
|
|
312
|
+
|
|
313
|
+
const { newData, object: objectFromHook } = await hook.runOnSingleObject({
|
|
314
|
+
operationType: operationTypeBefore,
|
|
315
|
+
id,
|
|
316
|
+
|
|
317
|
+
object: inputObject,
|
|
318
|
+
})
|
|
319
|
+
|
|
320
|
+
const res = await adapterCallback(newData || data)
|
|
321
|
+
|
|
322
|
+
if (!res) return null
|
|
323
|
+
|
|
324
|
+
// If the operation is delete, we use the objectFromHook (snapshot before delete)
|
|
325
|
+
// Otherwise we use the result from adapter (which might be just { id })
|
|
326
|
+
// loading the full object if needed happens inside runOnSingleObject if 'id' is passed
|
|
327
|
+
await hook.runOnSingleObject({
|
|
328
|
+
operationType: operationTypeAfter,
|
|
329
|
+
id: res.id,
|
|
330
|
+
|
|
331
|
+
originalObject: objectFromHook,
|
|
332
|
+
})
|
|
333
|
+
|
|
334
|
+
return res
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
_getFinalObjectWithPointerAndRelation({
|
|
338
|
+
pointers,
|
|
339
|
+
context,
|
|
340
|
+
originClassName,
|
|
341
|
+
object,
|
|
342
|
+
_skipHooks,
|
|
343
|
+
}: {
|
|
344
|
+
originClassName: string
|
|
345
|
+
pointers: Record<string, { className: string; select: Select }>
|
|
346
|
+
context: WabeContext<any>
|
|
347
|
+
object: Record<string, any>
|
|
348
|
+
_skipHooks?: boolean
|
|
349
|
+
}) {
|
|
350
|
+
return Object.entries(pointers).reduce(
|
|
351
|
+
async (acc, [pointerField, { className: currentClassName, select: currentSelect }]) => {
|
|
352
|
+
const accObject = await acc
|
|
353
|
+
|
|
354
|
+
const isPointer = this._isFieldOfType(
|
|
355
|
+
originClassName,
|
|
356
|
+
pointerField,
|
|
357
|
+
'Pointer',
|
|
358
|
+
context,
|
|
359
|
+
currentClassName,
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
if (isPointer) {
|
|
363
|
+
if (!object[pointerField])
|
|
364
|
+
return {
|
|
365
|
+
...accObject,
|
|
366
|
+
[pointerField]: null,
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const objectOfPointerClass = await this.getObject({
|
|
370
|
+
className: currentClassName,
|
|
371
|
+
id: object[pointerField],
|
|
372
|
+
context,
|
|
373
|
+
// @ts-expect-error
|
|
374
|
+
select: currentSelect,
|
|
375
|
+
_skipHooks,
|
|
376
|
+
})
|
|
377
|
+
|
|
378
|
+
return {
|
|
379
|
+
...accObject,
|
|
380
|
+
[pointerField]: objectOfPointerClass,
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const isRelation = this._isFieldOfType(
|
|
385
|
+
originClassName,
|
|
386
|
+
pointerField,
|
|
387
|
+
'Relation',
|
|
388
|
+
context,
|
|
389
|
+
currentClassName,
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
if (isRelation && object[pointerField]) {
|
|
393
|
+
const selectWithoutTotalCount = Object.entries(currentSelect || {}).reduce(
|
|
394
|
+
(acc2, [key, value]) => {
|
|
395
|
+
if (key === 'totalCount') return acc2
|
|
396
|
+
|
|
397
|
+
return {
|
|
398
|
+
...acc2,
|
|
399
|
+
[key]: value,
|
|
400
|
+
}
|
|
401
|
+
},
|
|
402
|
+
{} as Record<string, any>,
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
const relationObjects = await this.getObjects({
|
|
406
|
+
className: currentClassName,
|
|
407
|
+
select: selectWithoutTotalCount,
|
|
408
|
+
// @ts-expect-error
|
|
409
|
+
where: { id: { in: object[pointerField] } },
|
|
410
|
+
context,
|
|
411
|
+
_skipHooks,
|
|
412
|
+
})
|
|
413
|
+
|
|
414
|
+
return {
|
|
415
|
+
...accObject,
|
|
416
|
+
[pointerField]: context.isGraphQLCall
|
|
417
|
+
? {
|
|
418
|
+
totalCount: relationObjects.length,
|
|
419
|
+
edges: relationObjects.map((object: any) => ({
|
|
420
|
+
node: object,
|
|
421
|
+
})),
|
|
422
|
+
}
|
|
423
|
+
: relationObjects,
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
return accObject
|
|
428
|
+
},
|
|
429
|
+
Promise.resolve({} as Record<string, any>),
|
|
430
|
+
)
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
async close() {
|
|
434
|
+
await this.adapter.close()
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
createClassIfNotExist(className: string, schema: SchemaInterface<T>): Promise<any> {
|
|
438
|
+
return this.adapter.createClassIfNotExist(className, schema)
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
initializeDatabase(schema: SchemaInterface<T>): Promise<void> {
|
|
442
|
+
return this.adapter.initializeDatabase(schema)
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
async count<K extends keyof T['types']>({
|
|
446
|
+
className,
|
|
447
|
+
context,
|
|
448
|
+
where,
|
|
449
|
+
}: CountOptions<T, K>): Promise<number> {
|
|
450
|
+
const whereWithACLCondition = this._buildWhereWithACL(where || {}, context, 'read')
|
|
451
|
+
|
|
452
|
+
const hook = initializeHook({
|
|
453
|
+
className,
|
|
454
|
+
context,
|
|
455
|
+
select: {},
|
|
456
|
+
})
|
|
457
|
+
|
|
458
|
+
await hook?.runOnSingleObject({
|
|
459
|
+
operationType: OperationType.BeforeRead,
|
|
460
|
+
})
|
|
461
|
+
|
|
462
|
+
const count = await this.adapter.count({
|
|
463
|
+
className,
|
|
464
|
+
context,
|
|
465
|
+
where: whereWithACLCondition,
|
|
466
|
+
})
|
|
467
|
+
|
|
468
|
+
await hook?.runOnSingleObject({
|
|
469
|
+
operationType: OperationType.AfterRead,
|
|
470
|
+
})
|
|
471
|
+
|
|
472
|
+
return count
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
async clearDatabase(): Promise<void> {
|
|
476
|
+
await this.adapter.clearDatabase()
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
async getObject<K extends keyof T['types'], U extends keyof T['types'][K]>({
|
|
480
|
+
select,
|
|
481
|
+
className,
|
|
482
|
+
context,
|
|
483
|
+
_skipHooks,
|
|
484
|
+
id,
|
|
485
|
+
where,
|
|
486
|
+
}: GetObjectOptions<T, K, U>): Promise<OutputType<T, K, U>> {
|
|
487
|
+
const { pointers, selectWithoutPointers } = this._getSelectMinusPointersAndRelations({
|
|
488
|
+
className,
|
|
489
|
+
context,
|
|
490
|
+
select: select as SelectWithObject,
|
|
491
|
+
})
|
|
492
|
+
|
|
493
|
+
const hook = !_skipHooks
|
|
494
|
+
? initializeHook({
|
|
495
|
+
className,
|
|
496
|
+
context,
|
|
497
|
+
select: selectWithoutPointers,
|
|
498
|
+
objectLoader: this._loadObjectForHooks(className, context),
|
|
499
|
+
objectsLoader: this._loadObjectsForHooks(className, context),
|
|
500
|
+
})
|
|
501
|
+
: undefined
|
|
502
|
+
|
|
503
|
+
await hook?.runOnSingleObject({
|
|
504
|
+
operationType: OperationType.BeforeRead,
|
|
505
|
+
id,
|
|
506
|
+
})
|
|
507
|
+
|
|
508
|
+
const whereWithACLCondition = this._buildWhereWithACL(where || {}, context, 'read')
|
|
509
|
+
|
|
510
|
+
const selectWithPointersAndRelationsToGetId = Object.keys(pointers).reduce((acc, fieldName) => {
|
|
511
|
+
acc[fieldName] = true
|
|
512
|
+
|
|
513
|
+
return acc
|
|
514
|
+
}, selectWithoutPointers)
|
|
515
|
+
|
|
516
|
+
const objectToReturn = await this.adapter.getObject({
|
|
517
|
+
className,
|
|
518
|
+
id,
|
|
519
|
+
context: contextWithRoot(context),
|
|
520
|
+
// @ts-expect-error
|
|
521
|
+
select: !select ? undefined : selectWithPointersAndRelationsToGetId,
|
|
522
|
+
where: whereWithACLCondition,
|
|
523
|
+
})
|
|
524
|
+
|
|
525
|
+
const finalObject = {
|
|
526
|
+
...objectToReturn,
|
|
527
|
+
...(await this._getFinalObjectWithPointerAndRelation({
|
|
528
|
+
context,
|
|
529
|
+
// @ts-expect-error
|
|
530
|
+
originClassName: className,
|
|
531
|
+
pointers,
|
|
532
|
+
// @ts-expect-error
|
|
533
|
+
object: objectToReturn,
|
|
534
|
+
_skipHooks,
|
|
535
|
+
})),
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
const afterReadResult = await hook?.runOnSingleObject({
|
|
539
|
+
operationType: OperationType.AfterRead,
|
|
540
|
+
id,
|
|
541
|
+
// @ts-expect-error
|
|
542
|
+
object: finalObject,
|
|
543
|
+
})
|
|
544
|
+
|
|
545
|
+
return afterReadResult?.object || finalObject
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
async getObjects<
|
|
549
|
+
K extends keyof T['types'],
|
|
550
|
+
U extends keyof T['types'][K],
|
|
551
|
+
W extends keyof T['types'][K],
|
|
552
|
+
>({
|
|
553
|
+
className,
|
|
554
|
+
select,
|
|
555
|
+
context,
|
|
556
|
+
where,
|
|
557
|
+
_skipHooks,
|
|
558
|
+
first,
|
|
559
|
+
offset,
|
|
560
|
+
order,
|
|
561
|
+
}: GetObjectsOptions<T, K, U, W>): Promise<OutputType<T, K, W>[]> {
|
|
562
|
+
const { pointers, selectWithoutPointers } = this._getSelectMinusPointersAndRelations({
|
|
563
|
+
className,
|
|
564
|
+
context,
|
|
565
|
+
select: select as SelectWithObject,
|
|
566
|
+
})
|
|
567
|
+
|
|
568
|
+
const whereWithPointer = await this._getWhereObjectWithPointerOrRelation(
|
|
569
|
+
className,
|
|
570
|
+
where || {},
|
|
571
|
+
context,
|
|
572
|
+
)
|
|
573
|
+
|
|
574
|
+
const whereWithACLCondition = this._buildWhereWithACL(whereWithPointer || {}, context, 'read')
|
|
575
|
+
|
|
576
|
+
const selectWithPointersAndRelationsToGetId = Object.keys(pointers).reduce((acc, fieldName) => {
|
|
577
|
+
acc[fieldName] = true
|
|
578
|
+
|
|
579
|
+
return acc
|
|
580
|
+
}, selectWithoutPointers)
|
|
581
|
+
|
|
582
|
+
const hook = !_skipHooks
|
|
583
|
+
? initializeHook({
|
|
584
|
+
className,
|
|
585
|
+
select: selectWithoutPointers,
|
|
586
|
+
context,
|
|
587
|
+
objectLoader: this._loadObjectForHooks(className, context),
|
|
588
|
+
objectsLoader: this._loadObjectsForHooks(className, context),
|
|
589
|
+
})
|
|
590
|
+
: undefined
|
|
591
|
+
|
|
592
|
+
await hook?.runOnMultipleObjects({
|
|
593
|
+
operationType: OperationType.BeforeRead,
|
|
594
|
+
where: whereWithACLCondition,
|
|
595
|
+
})
|
|
596
|
+
|
|
597
|
+
const objectsToReturn = await this.adapter.getObjects({
|
|
598
|
+
className,
|
|
599
|
+
context: contextWithRoot(context),
|
|
600
|
+
first,
|
|
601
|
+
offset,
|
|
602
|
+
where: whereWithACLCondition,
|
|
603
|
+
// @ts-expect-error
|
|
604
|
+
select: !select ? undefined : selectWithPointersAndRelationsToGetId,
|
|
605
|
+
order,
|
|
606
|
+
})
|
|
607
|
+
|
|
608
|
+
const objectsWithPointers = await Promise.all(
|
|
609
|
+
objectsToReturn.map(async (object) => {
|
|
610
|
+
return {
|
|
611
|
+
...object,
|
|
612
|
+
...(await this._getFinalObjectWithPointerAndRelation({
|
|
613
|
+
// @ts-expect-error
|
|
614
|
+
object,
|
|
615
|
+
context,
|
|
616
|
+
// @ts-expect-error
|
|
617
|
+
originClassName: className,
|
|
618
|
+
pointers,
|
|
619
|
+
_skipHooks,
|
|
620
|
+
})),
|
|
621
|
+
}
|
|
622
|
+
}),
|
|
623
|
+
)
|
|
624
|
+
|
|
625
|
+
const afterReadResults = await hook?.runOnMultipleObjects({
|
|
626
|
+
operationType: OperationType.AfterRead,
|
|
627
|
+
// @ts-expect-error
|
|
628
|
+
objects: objectsWithPointers,
|
|
629
|
+
})
|
|
630
|
+
|
|
631
|
+
return (afterReadResults?.objects || objectsWithPointers) as unknown as Promise<
|
|
632
|
+
OutputType<T, K, W>[]
|
|
633
|
+
>
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
async createObject<
|
|
637
|
+
K extends keyof T['types'],
|
|
638
|
+
U extends keyof T['types'][K],
|
|
639
|
+
W extends keyof T['types'][K],
|
|
640
|
+
>({
|
|
641
|
+
className,
|
|
642
|
+
context,
|
|
643
|
+
data,
|
|
644
|
+
select,
|
|
645
|
+
}: CreateObjectOptions<T, K, U, W>): Promise<OutputType<T, K, W>> {
|
|
646
|
+
// Here data.file is null but should not be
|
|
647
|
+
|
|
648
|
+
const result = await this._executeSingleOperationWithHooks<K, W>({
|
|
649
|
+
operationTypeBefore: OperationType.BeforeCreate,
|
|
650
|
+
operationTypeAfter: OperationType.AfterCreate,
|
|
651
|
+
className,
|
|
652
|
+
context,
|
|
653
|
+
data,
|
|
654
|
+
select: select as Select,
|
|
655
|
+
adapterCallback: async (newData) => {
|
|
656
|
+
const res = await this.adapter.createObject({
|
|
657
|
+
className,
|
|
658
|
+
context,
|
|
659
|
+
select,
|
|
660
|
+
data: newData || data,
|
|
661
|
+
})
|
|
662
|
+
|
|
663
|
+
return { id: res.id }
|
|
664
|
+
},
|
|
665
|
+
})
|
|
666
|
+
|
|
667
|
+
const res = result as { id: string }
|
|
668
|
+
|
|
669
|
+
if (select && Object.keys(select).length === 0) return null
|
|
670
|
+
|
|
671
|
+
return this.getObject({
|
|
672
|
+
className,
|
|
673
|
+
context: contextWithRoot(context),
|
|
674
|
+
select: selectFieldsWithoutPrivateFields(select),
|
|
675
|
+
id: res.id,
|
|
676
|
+
})
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
async createObjects<
|
|
680
|
+
K extends keyof T['types'],
|
|
681
|
+
U extends keyof T['types'][K],
|
|
682
|
+
W extends keyof T['types'][K],
|
|
683
|
+
X extends keyof T['types'][K],
|
|
684
|
+
>({
|
|
685
|
+
data,
|
|
686
|
+
select,
|
|
687
|
+
className,
|
|
688
|
+
context,
|
|
689
|
+
first,
|
|
690
|
+
offset,
|
|
691
|
+
order,
|
|
692
|
+
}: CreateObjectsOptions<T, K, U, W, X>): Promise<OutputType<T, K, W>[]> {
|
|
693
|
+
if (data.length === 0) return []
|
|
694
|
+
|
|
695
|
+
const hooks = await Promise.all(
|
|
696
|
+
data.map((newData) =>
|
|
697
|
+
initializeHook({
|
|
698
|
+
className,
|
|
699
|
+
context,
|
|
700
|
+
newData,
|
|
701
|
+
// @ts-expect-error
|
|
702
|
+
select,
|
|
703
|
+
objectLoader: this._loadObjectForHooks(className, context),
|
|
704
|
+
objectsLoader: this._loadObjectsForHooks(className, context),
|
|
705
|
+
}),
|
|
706
|
+
),
|
|
707
|
+
)
|
|
708
|
+
|
|
709
|
+
const arrayOfComputedData = (
|
|
710
|
+
await Promise.all(
|
|
711
|
+
hooks.map(
|
|
712
|
+
async (hook) =>
|
|
713
|
+
(
|
|
714
|
+
await hook.runOnMultipleObjects({
|
|
715
|
+
operationType: OperationType.BeforeCreate,
|
|
716
|
+
})
|
|
717
|
+
)?.newData[0],
|
|
718
|
+
),
|
|
719
|
+
)
|
|
720
|
+
).filter(notEmpty)
|
|
721
|
+
|
|
722
|
+
const listOfIds = await this.adapter.createObjects({
|
|
723
|
+
className,
|
|
724
|
+
select,
|
|
725
|
+
context,
|
|
726
|
+
data: arrayOfComputedData,
|
|
727
|
+
first,
|
|
728
|
+
offset,
|
|
729
|
+
order,
|
|
730
|
+
})
|
|
731
|
+
|
|
732
|
+
const ids = listOfIds.map(({ id }) => id)
|
|
733
|
+
|
|
734
|
+
await Promise.all(
|
|
735
|
+
hooks.map((hook) =>
|
|
736
|
+
hook.runOnMultipleObjects({
|
|
737
|
+
operationType: OperationType.AfterCreate,
|
|
738
|
+
ids,
|
|
739
|
+
}),
|
|
740
|
+
),
|
|
741
|
+
)
|
|
742
|
+
|
|
743
|
+
if (select && Object.keys(select).length === 0) return []
|
|
744
|
+
|
|
745
|
+
return this.getObjects({
|
|
746
|
+
className,
|
|
747
|
+
context: contextWithRoot(context),
|
|
748
|
+
select,
|
|
749
|
+
// @ts-expect-error
|
|
750
|
+
where: { id: { in: ids } },
|
|
751
|
+
first,
|
|
752
|
+
offset,
|
|
753
|
+
order,
|
|
754
|
+
})
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
async updateObject<
|
|
758
|
+
K extends keyof T['types'],
|
|
759
|
+
U extends keyof T['types'][K],
|
|
760
|
+
W extends keyof T['types'][K],
|
|
761
|
+
>({
|
|
762
|
+
id,
|
|
763
|
+
className,
|
|
764
|
+
context,
|
|
765
|
+
data,
|
|
766
|
+
select,
|
|
767
|
+
_skipHooks,
|
|
768
|
+
}: UpdateObjectOptions<T, K, U, W>): Promise<OutputType<T, K, W>> {
|
|
769
|
+
if (_skipHooks) {
|
|
770
|
+
const whereWithACLCondition = this._buildWhereWithACL({}, context, 'write')
|
|
771
|
+
|
|
772
|
+
return this.adapter.updateObject({
|
|
773
|
+
className,
|
|
774
|
+
select,
|
|
775
|
+
id,
|
|
776
|
+
context,
|
|
777
|
+
data,
|
|
778
|
+
where: whereWithACLCondition,
|
|
779
|
+
}) as Promise<OutputType<T, K, W>>
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
await this._executeSingleOperationWithHooks<K, W>({
|
|
783
|
+
operationTypeBefore: OperationType.BeforeUpdate,
|
|
784
|
+
operationTypeAfter: OperationType.AfterUpdate,
|
|
785
|
+
className,
|
|
786
|
+
context,
|
|
787
|
+
data,
|
|
788
|
+
id,
|
|
789
|
+
select: select as Select,
|
|
790
|
+
adapterCallback: async (newData) => {
|
|
791
|
+
const whereWithACLCondition = this._buildWhereWithACL({}, context, 'write')
|
|
792
|
+
|
|
793
|
+
await this.adapter.updateObject({
|
|
794
|
+
className,
|
|
795
|
+
select,
|
|
796
|
+
id,
|
|
797
|
+
context,
|
|
798
|
+
data: newData || data,
|
|
799
|
+
where: whereWithACLCondition,
|
|
800
|
+
})
|
|
801
|
+
|
|
802
|
+
return { id }
|
|
803
|
+
},
|
|
804
|
+
})
|
|
805
|
+
|
|
806
|
+
if (select && Object.keys(select).length === 0) return null
|
|
807
|
+
|
|
808
|
+
return this.getObject({
|
|
809
|
+
className,
|
|
810
|
+
context,
|
|
811
|
+
select,
|
|
812
|
+
id,
|
|
813
|
+
})
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
async updateObjects<
|
|
817
|
+
K extends keyof T['types'],
|
|
818
|
+
U extends keyof T['types'][K],
|
|
819
|
+
W extends keyof T['types'][K],
|
|
820
|
+
X extends keyof T['types'][K],
|
|
821
|
+
>({
|
|
822
|
+
className,
|
|
823
|
+
where,
|
|
824
|
+
context,
|
|
825
|
+
select,
|
|
826
|
+
data,
|
|
827
|
+
first,
|
|
828
|
+
offset,
|
|
829
|
+
order,
|
|
830
|
+
_skipHooks,
|
|
831
|
+
}: UpdateObjectsOptions<T, K, U, W, X>): Promise<OutputType<T, K, W>[]> {
|
|
832
|
+
const whereObject = await this._getWhereObjectWithPointerOrRelation(
|
|
833
|
+
className,
|
|
834
|
+
where || {},
|
|
835
|
+
context,
|
|
836
|
+
)
|
|
837
|
+
|
|
838
|
+
const hook = !_skipHooks
|
|
839
|
+
? initializeHook({
|
|
840
|
+
className,
|
|
841
|
+
context,
|
|
842
|
+
newData: data,
|
|
843
|
+
// @ts-expect-error
|
|
844
|
+
select,
|
|
845
|
+
objectLoader: this._loadObjectForHooks(className, context),
|
|
846
|
+
objectsLoader: this._loadObjectsForHooks(className, context),
|
|
847
|
+
})
|
|
848
|
+
: undefined
|
|
849
|
+
|
|
850
|
+
const whereWithACLCondition = this._buildWhereWithACL(whereObject, context, 'write')
|
|
851
|
+
|
|
852
|
+
const resultsAfterBeforeUpdate = await hook?.runOnMultipleObjects({
|
|
853
|
+
operationType: OperationType.BeforeUpdate,
|
|
854
|
+
where: whereWithACLCondition,
|
|
855
|
+
})
|
|
856
|
+
|
|
857
|
+
const objects = await this.adapter.updateObjects({
|
|
858
|
+
className,
|
|
859
|
+
context,
|
|
860
|
+
select,
|
|
861
|
+
data: resultsAfterBeforeUpdate?.newData[0] || data,
|
|
862
|
+
where: whereWithACLCondition,
|
|
863
|
+
first,
|
|
864
|
+
offset,
|
|
865
|
+
order,
|
|
866
|
+
})
|
|
867
|
+
|
|
868
|
+
const objectsId = objects.map((object) => object?.id).filter(notEmpty)
|
|
869
|
+
|
|
870
|
+
await hook?.runOnMultipleObjects({
|
|
871
|
+
operationType: OperationType.AfterUpdate,
|
|
872
|
+
ids: objectsId,
|
|
873
|
+
originalObjects: resultsAfterBeforeUpdate?.objects || [],
|
|
874
|
+
})
|
|
875
|
+
|
|
876
|
+
if (select && Object.keys(select).length === 0) return []
|
|
877
|
+
|
|
878
|
+
return this.getObjects({
|
|
879
|
+
className,
|
|
880
|
+
context,
|
|
881
|
+
select,
|
|
882
|
+
// @ts-expect-error
|
|
883
|
+
where: { id: { in: objectsId } },
|
|
884
|
+
first,
|
|
885
|
+
offset,
|
|
886
|
+
order,
|
|
887
|
+
})
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
async deleteObject<K extends keyof T['types'], U extends keyof T['types'][K]>({
|
|
891
|
+
context,
|
|
892
|
+
className,
|
|
893
|
+
id,
|
|
894
|
+
select,
|
|
895
|
+
}: DeleteObjectOptions<T, K, U>): Promise<OutputType<T, K, U>> {
|
|
896
|
+
const result = (await this._executeSingleOperationWithHooks<K, U>({
|
|
897
|
+
operationTypeBefore: OperationType.BeforeDelete,
|
|
898
|
+
operationTypeAfter: OperationType.AfterDelete,
|
|
899
|
+
className,
|
|
900
|
+
context,
|
|
901
|
+
id,
|
|
902
|
+
select: select as Select,
|
|
903
|
+
adapterCallback: async (_newData) => {
|
|
904
|
+
const whereWithACLCondition = this._buildWhereWithACL({}, context, 'write')
|
|
905
|
+
|
|
906
|
+
// We need to fetch the object before deleting it if we want to return it
|
|
907
|
+
// But executeSingleOperationWithHooks already fetched it in runOnSingleObject if an id is present
|
|
908
|
+
// Wait, runOnSingleObject fetches it for the hook context 'object', but does not return it unless we use it.
|
|
909
|
+
// However, if we utilize _executeSingleOperationWithHooks, the 'object' from before-hook is kept.
|
|
910
|
+
// But _executeSingleOperationWithHooks logic for delete is slightly different regarding return value:
|
|
911
|
+
// Delete usually returns the deleted object.
|
|
912
|
+
// Update: My abstraction returns 'res' from adapterCallback.
|
|
913
|
+
// So I need to return the object here.
|
|
914
|
+
|
|
915
|
+
let objectBeforeDelete = null
|
|
916
|
+
|
|
917
|
+
if (select && Object.keys(select).length > 0)
|
|
918
|
+
objectBeforeDelete = await this.getObject({
|
|
919
|
+
className,
|
|
920
|
+
context,
|
|
921
|
+
select,
|
|
922
|
+
id,
|
|
923
|
+
})
|
|
924
|
+
|
|
925
|
+
await this.adapter.deleteObject({
|
|
926
|
+
className,
|
|
927
|
+
context,
|
|
928
|
+
id,
|
|
929
|
+
|
|
930
|
+
where: whereWithACLCondition,
|
|
931
|
+
})
|
|
932
|
+
|
|
933
|
+
return objectBeforeDelete || { id }
|
|
934
|
+
},
|
|
935
|
+
})) as unknown as OutputType<T, K, U>
|
|
936
|
+
|
|
937
|
+
if (select && Object.keys(select).length === 0) return null as any
|
|
938
|
+
|
|
939
|
+
return result
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
async deleteObjects<
|
|
943
|
+
K extends keyof T['types'],
|
|
944
|
+
U extends keyof T['types'][K],
|
|
945
|
+
W extends keyof T['types'][K],
|
|
946
|
+
>({
|
|
947
|
+
className,
|
|
948
|
+
context,
|
|
949
|
+
select,
|
|
950
|
+
where,
|
|
951
|
+
first,
|
|
952
|
+
offset,
|
|
953
|
+
order,
|
|
954
|
+
}: DeleteObjectsOptions<T, K, U, W>): Promise<OutputType<T, K, W>[]> {
|
|
955
|
+
const whereObject = await this._getWhereObjectWithPointerOrRelation(
|
|
956
|
+
className,
|
|
957
|
+
where || {},
|
|
958
|
+
context,
|
|
959
|
+
)
|
|
960
|
+
|
|
961
|
+
const hook = initializeHook({
|
|
962
|
+
className,
|
|
963
|
+
context,
|
|
964
|
+
// @ts-expect-error
|
|
965
|
+
select,
|
|
966
|
+
objectLoader: this._loadObjectForHooks(className, context),
|
|
967
|
+
objectsLoader: this._loadObjectsForHooks(className, context),
|
|
968
|
+
})
|
|
969
|
+
|
|
970
|
+
const whereWithACLCondition = this._buildWhereWithACL(whereObject, context, 'write')
|
|
971
|
+
|
|
972
|
+
let objectsBeforeDelete: OutputType<T, K, W>[] = []
|
|
973
|
+
|
|
974
|
+
if (select && Object.keys(select).length > 0)
|
|
975
|
+
objectsBeforeDelete = await this.getObjects({
|
|
976
|
+
className,
|
|
977
|
+
where,
|
|
978
|
+
select,
|
|
979
|
+
context,
|
|
980
|
+
first,
|
|
981
|
+
offset,
|
|
982
|
+
order,
|
|
983
|
+
})
|
|
984
|
+
|
|
985
|
+
const resultOfBeforeDelete = await hook.runOnMultipleObjects({
|
|
986
|
+
operationType: OperationType.BeforeDelete,
|
|
987
|
+
where: whereWithACLCondition,
|
|
988
|
+
})
|
|
989
|
+
|
|
990
|
+
await this.adapter.deleteObjects({
|
|
991
|
+
className,
|
|
992
|
+
context,
|
|
993
|
+
select,
|
|
994
|
+
first,
|
|
995
|
+
offset,
|
|
996
|
+
where: whereWithACLCondition,
|
|
997
|
+
order,
|
|
998
|
+
})
|
|
999
|
+
|
|
1000
|
+
await hook.runOnMultipleObjects({
|
|
1001
|
+
operationType: OperationType.AfterDelete,
|
|
1002
|
+
originalObjects: resultOfBeforeDelete.objects,
|
|
1003
|
+
})
|
|
1004
|
+
|
|
1005
|
+
return objectsBeforeDelete
|
|
1006
|
+
}
|
|
1007
|
+
}
|