@smartive/graphql-magic 19.1.3-next.2 → 19.2.0-next.2
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/CHANGELOG.md +3 -3
- package/dist/bin/gqm.cjs +1 -0
- package/dist/cjs/index.cjs +155 -91
- package/dist/esm/models/mutation-hook.d.ts +4 -2
- package/dist/esm/models/utils.js +1 -0
- package/dist/esm/models/utils.js.map +1 -1
- package/dist/esm/resolvers/mutations.d.ts +8 -2
- package/dist/esm/resolvers/mutations.js +144 -93
- package/dist/esm/resolvers/mutations.js.map +1 -1
- package/package.json +1 -1
- package/src/models/mutation-hook.ts +9 -2
- package/src/models/utils.ts +1 -0
- package/src/resolvers/mutations.ts +190 -101
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { GraphQLResolveInfo } from 'graphql';
|
|
2
2
|
import { v4 as uuid } from 'uuid';
|
|
3
|
-
import { Context
|
|
3
|
+
import { Context } from '../context';
|
|
4
4
|
import { ForbiddenError, GraphQLError } from '../errors';
|
|
5
5
|
import { EntityField, EntityModel } from '../models/models';
|
|
6
|
-
import { Entity } from '../models/mutation-hook';
|
|
6
|
+
import { Entity, MutationContext, Trigger } from '../models/mutation-hook';
|
|
7
7
|
import { get, isPrimitive, it, typeToField } from '../models/utils';
|
|
8
8
|
import { applyPermissions, checkCanWrite, getEntityToMutate } from '../permissions/check';
|
|
9
9
|
import { anyDateToLuxon } from '../utils';
|
|
@@ -16,23 +16,53 @@ export const mutationResolver = async (_parent: any, args: any, partialCtx: Cont
|
|
|
16
16
|
const ctx = { ...partialCtx, knex, info, aliases: new AliasGenerator() };
|
|
17
17
|
const model = ctx.models.getModel(modelName, 'entity');
|
|
18
18
|
switch (mutation) {
|
|
19
|
-
case 'create':
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
return await
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
19
|
+
case 'create': {
|
|
20
|
+
const id = await createEntity(model, args.data, ctx, 'mutation');
|
|
21
|
+
|
|
22
|
+
return await resolve(ctx, id);
|
|
23
|
+
}
|
|
24
|
+
case 'update': {
|
|
25
|
+
const id = args.where.id;
|
|
26
|
+
|
|
27
|
+
await updateEntity(model, id, args.data, ctx);
|
|
28
|
+
|
|
29
|
+
return await resolve(ctx, id);
|
|
30
|
+
}
|
|
31
|
+
case 'delete': {
|
|
32
|
+
const id = args.where.id;
|
|
33
|
+
|
|
34
|
+
await deleteEntity(model, id, args.dryRun, model.rootModel.name, id, ctx, 'mutation');
|
|
35
|
+
|
|
36
|
+
return id;
|
|
37
|
+
}
|
|
38
|
+
case 'restore': {
|
|
39
|
+
const id = args.where.id;
|
|
40
|
+
|
|
41
|
+
await restoreEntity(model, id, ctx, 'mutation');
|
|
42
|
+
|
|
43
|
+
return id;
|
|
44
|
+
}
|
|
27
45
|
}
|
|
28
46
|
});
|
|
29
47
|
};
|
|
30
48
|
|
|
31
|
-
const
|
|
49
|
+
export const createEntity = async (
|
|
50
|
+
model: EntityModel,
|
|
51
|
+
input: Entity,
|
|
52
|
+
ctx: MutationContext,
|
|
53
|
+
trigger: Trigger = 'direct-call',
|
|
54
|
+
) => {
|
|
32
55
|
const normalizedInput = { ...input };
|
|
33
|
-
normalizedInput.id
|
|
34
|
-
|
|
35
|
-
|
|
56
|
+
if (!normalizedInput.id) {
|
|
57
|
+
normalizedInput.id = uuid();
|
|
58
|
+
}
|
|
59
|
+
const id = normalizedInput.id as string;
|
|
60
|
+
if (!normalizedInput.createdAt) {
|
|
61
|
+
normalizedInput.createdAt = ctx.now;
|
|
62
|
+
}
|
|
63
|
+
if (!normalizedInput.createdById) {
|
|
64
|
+
normalizedInput.createdById = ctx.user?.id;
|
|
65
|
+
}
|
|
36
66
|
if (model.parent) {
|
|
37
67
|
normalizedInput.type = model.name;
|
|
38
68
|
}
|
|
@@ -42,10 +72,11 @@ const create = async (model: EntityModel, { data: input }: { data: any }, ctx: F
|
|
|
42
72
|
await ctx.handleUploads?.(normalizedInput);
|
|
43
73
|
|
|
44
74
|
const data = { prev: {}, input, normalizedInput, next: normalizedInput };
|
|
45
|
-
await ctx.mutationHook?.({ model, action: 'create', trigger
|
|
75
|
+
await ctx.mutationHook?.({ model, action: 'create', trigger, when: 'before', data, ctx });
|
|
76
|
+
|
|
46
77
|
if (model.parent) {
|
|
47
78
|
const rootInput = {};
|
|
48
|
-
const childInput = { id
|
|
79
|
+
const childInput = { id };
|
|
49
80
|
for (const field of model.fields) {
|
|
50
81
|
const columnName = field.kind === 'relation' ? `${field.name}Id` : field.name;
|
|
51
82
|
if (columnName in normalizedInput) {
|
|
@@ -62,25 +93,39 @@ const create = async (model: EntityModel, { data: input }: { data: any }, ctx: F
|
|
|
62
93
|
await ctx.knex(model.name).insert(normalizedInput);
|
|
63
94
|
}
|
|
64
95
|
await createRevision(model, normalizedInput, ctx);
|
|
65
|
-
await ctx.mutationHook?.({ model, action: 'create', trigger
|
|
96
|
+
await ctx.mutationHook?.({ model, action: 'create', trigger, when: 'after', data, ctx });
|
|
66
97
|
|
|
67
|
-
return
|
|
98
|
+
return normalizedInput.id as string;
|
|
68
99
|
};
|
|
69
100
|
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
101
|
+
export const updateEntities = async (
|
|
102
|
+
model: EntityModel,
|
|
103
|
+
where: Record<string, unknown>,
|
|
104
|
+
updateFields: Entity,
|
|
105
|
+
ctx: MutationContext,
|
|
106
|
+
) => {
|
|
107
|
+
const entities = await ctx.knex(model.name).where(where).select('id');
|
|
108
|
+
for (const entity of entities) {
|
|
109
|
+
await updateEntity(model, entity.id, updateFields, ctx);
|
|
73
110
|
}
|
|
111
|
+
};
|
|
74
112
|
|
|
113
|
+
export const updateEntity = async (
|
|
114
|
+
model: EntityModel,
|
|
115
|
+
id: string,
|
|
116
|
+
input: Entity,
|
|
117
|
+
ctx: MutationContext,
|
|
118
|
+
trigger: Trigger = 'direct-call',
|
|
119
|
+
) => {
|
|
75
120
|
const normalizedInput = { ...input };
|
|
76
121
|
|
|
77
122
|
sanitize(ctx, model, normalizedInput);
|
|
78
123
|
|
|
79
|
-
const
|
|
124
|
+
const currentEntity = await getEntityToMutate(ctx, model, { id }, 'UPDATE');
|
|
80
125
|
|
|
81
126
|
// Remove data that wouldn't mutate given that it's irrelevant for permissions
|
|
82
127
|
for (const key of Object.keys(normalizedInput)) {
|
|
83
|
-
if (normalizedInput[key] ===
|
|
128
|
+
if (normalizedInput[key] === currentEntity[key]) {
|
|
84
129
|
delete normalizedInput[key];
|
|
85
130
|
}
|
|
86
131
|
}
|
|
@@ -89,49 +134,52 @@ const update = async (model: EntityModel, { where, data: input }: { where: any;
|
|
|
89
134
|
await checkCanWrite(ctx, model, normalizedInput, 'UPDATE');
|
|
90
135
|
await ctx.handleUploads?.(normalizedInput);
|
|
91
136
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if (Object.keys(rootInput).length) {
|
|
110
|
-
await ctx.knex(model.parent).where({ id: prev.id }).update(rootInput);
|
|
111
|
-
}
|
|
112
|
-
if (Object.keys(childInput).length) {
|
|
113
|
-
await ctx.knex(model.name).where({ id: prev.id }).update(childInput);
|
|
114
|
-
}
|
|
115
|
-
} else {
|
|
116
|
-
await ctx.knex(model.name).where({ id: prev.id }).update(normalizedInput);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
await createRevision(model, next, ctx);
|
|
120
|
-
await ctx.mutationHook?.({ model, action: 'update', trigger: 'mutation', when: 'after', data, ctx });
|
|
137
|
+
await ctx.mutationHook?.({
|
|
138
|
+
model,
|
|
139
|
+
action: 'update',
|
|
140
|
+
trigger,
|
|
141
|
+
when: 'before',
|
|
142
|
+
data: { prev: currentEntity, input, normalizedInput, next: { ...currentEntity, ...normalizedInput } },
|
|
143
|
+
ctx,
|
|
144
|
+
});
|
|
145
|
+
await doUpdate(model, currentEntity, normalizedInput, ctx);
|
|
146
|
+
await ctx.mutationHook?.({
|
|
147
|
+
model,
|
|
148
|
+
action: 'update',
|
|
149
|
+
trigger,
|
|
150
|
+
when: 'after',
|
|
151
|
+
data: { prev: currentEntity, input, normalizedInput, next: { ...currentEntity, ...normalizedInput } },
|
|
152
|
+
ctx,
|
|
153
|
+
});
|
|
121
154
|
}
|
|
122
|
-
|
|
123
|
-
return await resolve(ctx);
|
|
124
155
|
};
|
|
125
156
|
|
|
126
157
|
type Callbacks = (() => Promise<void>)[];
|
|
127
158
|
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
159
|
+
export const deleteEntities = async (
|
|
160
|
+
model: EntityModel,
|
|
161
|
+
where: Record<string, unknown>,
|
|
162
|
+
deleteRootType: string | undefined,
|
|
163
|
+
deleteRootId: string | undefined,
|
|
164
|
+
ctx: MutationContext,
|
|
165
|
+
) => {
|
|
166
|
+
const entities = await ctx.knex(model.name).where(where).select('id');
|
|
167
|
+
for (const entity of entities) {
|
|
168
|
+
await deleteEntity(model, entity.id, false, deleteRootType, deleteRootId, ctx);
|
|
131
169
|
}
|
|
170
|
+
};
|
|
132
171
|
|
|
172
|
+
export const deleteEntity = async (
|
|
173
|
+
model: EntityModel,
|
|
174
|
+
id: string,
|
|
175
|
+
dryRun: boolean,
|
|
176
|
+
deleteRootType: string | undefined = model.rootModel.name,
|
|
177
|
+
deleteRootId: string | undefined = id,
|
|
178
|
+
ctx: MutationContext,
|
|
179
|
+
trigger: Trigger = 'direct-call',
|
|
180
|
+
) => {
|
|
133
181
|
const rootModel = model.rootModel;
|
|
134
|
-
const entity = await getEntityToMutate(ctx, rootModel,
|
|
182
|
+
const entity = await getEntityToMutate(ctx, rootModel, { id }, 'DELETE');
|
|
135
183
|
|
|
136
184
|
if (entity.deleted) {
|
|
137
185
|
throw new ForbiddenError(`${getTechnicalDisplay(model, entity)} is already deleted.`);
|
|
@@ -164,7 +212,7 @@ const del = async (model: EntityModel, { where, dryRun }: { where: any; dryRun:
|
|
|
164
212
|
const afterHooks: Callbacks = [];
|
|
165
213
|
|
|
166
214
|
const mutationHook = ctx.mutationHook;
|
|
167
|
-
const deleteCascade = async (currentModel: EntityModel, currentEntity: Entity) => {
|
|
215
|
+
const deleteCascade = async (currentModel: EntityModel, currentEntity: Entity, currentTrigger: Trigger) => {
|
|
168
216
|
if (!(currentModel.name in toDelete)) {
|
|
169
217
|
toDelete[currentModel.name] = {};
|
|
170
218
|
}
|
|
@@ -172,42 +220,38 @@ const del = async (model: EntityModel, { where, dryRun }: { where: any; dryRun:
|
|
|
172
220
|
return;
|
|
173
221
|
}
|
|
174
222
|
toDelete[currentModel.name][currentEntity.id as string] = await fetchDisplay(ctx.knex, currentModel, currentEntity);
|
|
175
|
-
const trigger = currentModel.name === rootModel.name && currentEntity.id === entity.id ? 'mutation' : 'cascade';
|
|
176
223
|
|
|
177
224
|
if (!dryRun) {
|
|
178
225
|
const normalizedInput = {
|
|
179
226
|
deleted: true,
|
|
180
227
|
deletedAt: ctx.now,
|
|
181
228
|
deletedById: ctx.user?.id,
|
|
182
|
-
deleteRootType
|
|
183
|
-
deleteRootId
|
|
229
|
+
deleteRootType,
|
|
230
|
+
deleteRootId,
|
|
184
231
|
};
|
|
185
|
-
const next = { ...currentEntity, ...normalizedInput };
|
|
186
|
-
const data = { prev: currentEntity, input: {}, normalizedInput, next };
|
|
187
232
|
if (mutationHook) {
|
|
188
233
|
beforeHooks.push(async () => {
|
|
189
234
|
await mutationHook({
|
|
190
235
|
model: currentModel,
|
|
191
236
|
action: 'delete',
|
|
192
|
-
trigger,
|
|
237
|
+
trigger: currentTrigger,
|
|
193
238
|
when: 'before',
|
|
194
|
-
data,
|
|
239
|
+
data: { prev: currentEntity, input: {}, normalizedInput, next: { ...currentEntity, ...normalizedInput } },
|
|
195
240
|
ctx,
|
|
196
241
|
});
|
|
197
242
|
});
|
|
198
243
|
}
|
|
199
244
|
mutations.push(async () => {
|
|
200
|
-
await
|
|
201
|
-
await createRevision(currentModel, next, ctx);
|
|
245
|
+
await doUpdate(currentModel, currentEntity, normalizedInput, ctx);
|
|
202
246
|
});
|
|
203
247
|
if (mutationHook) {
|
|
204
248
|
afterHooks.push(async () => {
|
|
205
249
|
await mutationHook({
|
|
206
250
|
model: currentModel,
|
|
207
251
|
action: 'delete',
|
|
208
|
-
trigger,
|
|
252
|
+
trigger: currentTrigger,
|
|
209
253
|
when: 'after',
|
|
210
|
-
data,
|
|
254
|
+
data: { prev: currentEntity, input: {}, normalizedInput, next: { ...currentEntity, ...normalizedInput } },
|
|
211
255
|
ctx,
|
|
212
256
|
});
|
|
213
257
|
});
|
|
@@ -237,8 +281,6 @@ const del = async (model: EntityModel, { where, dryRun }: { where: any; dryRun:
|
|
|
237
281
|
toUnlink[descendantModel.name][descendant.id].fields.push(name);
|
|
238
282
|
} else {
|
|
239
283
|
const normalizedInput = { [`${name}Id`]: null };
|
|
240
|
-
const next = { ...descendant, ...normalizedInput };
|
|
241
|
-
const data = { prev: descendant, input: {}, normalizedInput, next };
|
|
242
284
|
if (mutationHook) {
|
|
243
285
|
beforeHooks.push(async () => {
|
|
244
286
|
await mutationHook({
|
|
@@ -246,14 +288,13 @@ const del = async (model: EntityModel, { where, dryRun }: { where: any; dryRun:
|
|
|
246
288
|
action: 'update',
|
|
247
289
|
trigger: 'set-null',
|
|
248
290
|
when: 'before',
|
|
249
|
-
data,
|
|
291
|
+
data: { prev: descendant, input: {}, normalizedInput, next: { ...descendant, ...normalizedInput } },
|
|
250
292
|
ctx,
|
|
251
293
|
});
|
|
252
294
|
});
|
|
253
295
|
}
|
|
254
296
|
mutations.push(async () => {
|
|
255
|
-
await
|
|
256
|
-
await createRevision(descendantModel, next, ctx);
|
|
297
|
+
await doUpdate(descendantModel, descendant, normalizedInput, ctx);
|
|
257
298
|
});
|
|
258
299
|
if (mutationHook) {
|
|
259
300
|
afterHooks.push(async () => {
|
|
@@ -262,7 +303,7 @@ const del = async (model: EntityModel, { where, dryRun }: { where: any; dryRun:
|
|
|
262
303
|
action: 'update',
|
|
263
304
|
trigger: 'set-null',
|
|
264
305
|
when: 'after',
|
|
265
|
-
data,
|
|
306
|
+
data: { prev: descendant, input: {}, normalizedInput, next: { ...descendant, ...normalizedInput } },
|
|
266
307
|
ctx,
|
|
267
308
|
});
|
|
268
309
|
});
|
|
@@ -308,7 +349,7 @@ const del = async (model: EntityModel, { where, dryRun }: { where: any; dryRun:
|
|
|
308
349
|
);
|
|
309
350
|
}
|
|
310
351
|
for (const descendant of descendants) {
|
|
311
|
-
await deleteCascade(descendantModel, descendant);
|
|
352
|
+
await deleteCascade(descendantModel, descendant, 'cascade');
|
|
312
353
|
}
|
|
313
354
|
break;
|
|
314
355
|
}
|
|
@@ -317,7 +358,7 @@ const del = async (model: EntityModel, { where, dryRun }: { where: any; dryRun:
|
|
|
317
358
|
}
|
|
318
359
|
};
|
|
319
360
|
|
|
320
|
-
await deleteCascade(rootModel, entity);
|
|
361
|
+
await deleteCascade(rootModel, entity, trigger);
|
|
321
362
|
|
|
322
363
|
for (const callback of [...beforeHooks, ...mutations, ...afterHooks]) {
|
|
323
364
|
await callback();
|
|
@@ -331,18 +372,17 @@ const del = async (model: EntityModel, { where, dryRun }: { where: any; dryRun:
|
|
|
331
372
|
restricted,
|
|
332
373
|
});
|
|
333
374
|
}
|
|
334
|
-
|
|
335
|
-
return entity.id;
|
|
336
375
|
};
|
|
337
376
|
|
|
338
|
-
const
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
377
|
+
export const restoreEntity = async (
|
|
378
|
+
model: EntityModel,
|
|
379
|
+
id: string,
|
|
380
|
+
ctx: MutationContext,
|
|
381
|
+
trigger: Trigger = 'direct-call',
|
|
382
|
+
) => {
|
|
343
383
|
const rootModel = model.rootModel;
|
|
344
384
|
|
|
345
|
-
const entity = await getEntityToMutate(ctx, rootModel,
|
|
385
|
+
const entity = await getEntityToMutate(ctx, rootModel, { id }, 'RESTORE');
|
|
346
386
|
|
|
347
387
|
if (!entity.deleted) {
|
|
348
388
|
throw new ForbiddenError(`${getTechnicalDisplay(model, entity)} is not deleted.`);
|
|
@@ -362,9 +402,9 @@ const restore = async (model: EntityModel, { where }: { where: any }, ctx: FullC
|
|
|
362
402
|
const mutations: Callbacks = [];
|
|
363
403
|
const afterHooks: Callbacks = [];
|
|
364
404
|
|
|
365
|
-
const restoreCascade = async (currentModel: EntityModel, currentEntity: Entity) => {
|
|
405
|
+
const restoreCascade = async (currentModel: EntityModel, currentEntity: Entity, currentTrigger: Trigger) => {
|
|
366
406
|
if (entity.deleteRootId) {
|
|
367
|
-
if (!(currentEntity.deleteRootType ===
|
|
407
|
+
if (!(currentEntity.deleteRootType === model.name && currentEntity.deleteRootId === entity.id)) {
|
|
368
408
|
return;
|
|
369
409
|
}
|
|
370
410
|
|
|
@@ -381,7 +421,7 @@ const restore = async (model: EntityModel, { where }: { where: any }, ctx: FullC
|
|
|
381
421
|
toRestore[currentModel.name].add(currentEntity.id as string);
|
|
382
422
|
|
|
383
423
|
for (const relation of currentModel.relations) {
|
|
384
|
-
const parentId =
|
|
424
|
+
const parentId = currentEntity[relation.field.foreignKey] as string | undefined;
|
|
385
425
|
if (!parentId) {
|
|
386
426
|
continue;
|
|
387
427
|
}
|
|
@@ -406,16 +446,29 @@ const restore = async (model: EntityModel, { where }: { where: any }, ctx: FullC
|
|
|
406
446
|
const data = { prev: currentEntity, input: {}, normalizedInput, next: { ...currentEntity, ...normalizedInput } };
|
|
407
447
|
if (ctx.mutationHook) {
|
|
408
448
|
beforeHooks.push(async () => {
|
|
409
|
-
await ctx.mutationHook!({
|
|
449
|
+
await ctx.mutationHook!({
|
|
450
|
+
model: currentModel,
|
|
451
|
+
action: 'restore',
|
|
452
|
+
trigger: currentTrigger,
|
|
453
|
+
when: 'before',
|
|
454
|
+
data,
|
|
455
|
+
ctx,
|
|
456
|
+
});
|
|
410
457
|
});
|
|
411
458
|
}
|
|
412
459
|
mutations.push(async () => {
|
|
413
|
-
await
|
|
414
|
-
await createRevision(currentModel, { ...currentEntity, deleted: false }, ctx);
|
|
460
|
+
await doUpdate(currentModel, currentEntity, normalizedInput, ctx);
|
|
415
461
|
});
|
|
416
462
|
if (ctx.mutationHook) {
|
|
417
463
|
afterHooks.push(async () => {
|
|
418
|
-
await ctx.mutationHook!({
|
|
464
|
+
await ctx.mutationHook!({
|
|
465
|
+
model: currentModel,
|
|
466
|
+
action: 'restore',
|
|
467
|
+
trigger: currentTrigger,
|
|
468
|
+
when: 'after',
|
|
469
|
+
data,
|
|
470
|
+
ctx,
|
|
471
|
+
});
|
|
419
472
|
});
|
|
420
473
|
}
|
|
421
474
|
|
|
@@ -438,21 +491,19 @@ const restore = async (model: EntityModel, { where }: { where: any }, ctx: FullC
|
|
|
438
491
|
);
|
|
439
492
|
}
|
|
440
493
|
for (const descendant of deletedDescendants) {
|
|
441
|
-
await restoreCascade(descendantModel, descendant);
|
|
494
|
+
await restoreCascade(descendantModel, descendant, 'cascade');
|
|
442
495
|
}
|
|
443
496
|
}
|
|
444
497
|
};
|
|
445
498
|
|
|
446
|
-
await restoreCascade(rootModel, entity);
|
|
499
|
+
await restoreCascade(rootModel, entity, trigger);
|
|
447
500
|
|
|
448
501
|
for (const callback of [...beforeHooks, ...mutations, ...afterHooks]) {
|
|
449
502
|
await callback();
|
|
450
503
|
}
|
|
451
|
-
|
|
452
|
-
return entity.id;
|
|
453
504
|
};
|
|
454
505
|
|
|
455
|
-
export const createRevision = async (model: EntityModel, data: Entity, ctx:
|
|
506
|
+
export const createRevision = async (model: EntityModel, data: Entity, ctx: MutationContext) => {
|
|
456
507
|
if (model.updatable) {
|
|
457
508
|
const revisionId = uuid();
|
|
458
509
|
const rootRevisionData: Entity = {
|
|
@@ -491,10 +542,14 @@ export const createRevision = async (model: EntityModel, data: Entity, ctx: Pick
|
|
|
491
542
|
}
|
|
492
543
|
};
|
|
493
544
|
|
|
494
|
-
const sanitize = (ctx:
|
|
545
|
+
const sanitize = (ctx: MutationContext, model: EntityModel, data: Entity) => {
|
|
495
546
|
if (model.updatable) {
|
|
496
|
-
data.updatedAt
|
|
497
|
-
|
|
547
|
+
if (!data.updatedAt) {
|
|
548
|
+
data.updatedAt = ctx.now;
|
|
549
|
+
}
|
|
550
|
+
if (!data.updatedById) {
|
|
551
|
+
data.updatedById = ctx.user?.id;
|
|
552
|
+
}
|
|
498
553
|
}
|
|
499
554
|
|
|
500
555
|
for (const key of Object.keys(data)) {
|
|
@@ -526,3 +581,37 @@ const isEndOfDay = (field: EntityField) =>
|
|
|
526
581
|
|
|
527
582
|
const isEndOfMonth = (field: EntityField) =>
|
|
528
583
|
isPrimitive(field) && field.type === 'DateTime' && field?.endOfMonth === true && field?.dateTimeType === 'year_and_month';
|
|
584
|
+
|
|
585
|
+
const doUpdate = async (model: EntityModel, currentEntity: Entity, update: Entity, ctx: MutationContext) => {
|
|
586
|
+
if (model.updatable) {
|
|
587
|
+
if (!update.updatedAt) {
|
|
588
|
+
update.updatedAt = ctx.now;
|
|
589
|
+
}
|
|
590
|
+
if (!update.updatedById) {
|
|
591
|
+
update.updatedById = ctx.user?.id;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
if (model.parent) {
|
|
595
|
+
const rootInput = {};
|
|
596
|
+
const childInput = {};
|
|
597
|
+
for (const field of model.fields) {
|
|
598
|
+
const columnName = field.kind === 'relation' ? `${field.name}Id` : field.name;
|
|
599
|
+
if (columnName in update) {
|
|
600
|
+
if (field.inherited) {
|
|
601
|
+
rootInput[columnName] = update[columnName];
|
|
602
|
+
} else {
|
|
603
|
+
childInput[columnName] = update[columnName];
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
if (Object.keys(rootInput).length) {
|
|
608
|
+
await ctx.knex(model.parent).where({ id: currentEntity.id }).update(rootInput);
|
|
609
|
+
}
|
|
610
|
+
if (Object.keys(childInput).length) {
|
|
611
|
+
await ctx.knex(model.name).where({ id: currentEntity.id }).update(childInput);
|
|
612
|
+
}
|
|
613
|
+
} else {
|
|
614
|
+
await ctx.knex(model.name).where({ id: currentEntity.id }).update(update);
|
|
615
|
+
}
|
|
616
|
+
await createRevision(model, { ...currentEntity, ...update }, ctx);
|
|
617
|
+
};
|