@stamhoofd/backend 2.57.1 → 2.58.0
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/package.json +11 -11
- package/src/endpoints/global/payments/StripeWebhookEndpoint.ts +12 -1
- package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +7 -14
- package/src/endpoints/global/registration-periods/PatchRegistrationPeriodsEndpoint.ts +20 -1
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +13 -12
- package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +5 -0
- package/src/endpoints/organization/dashboard/stripe/ConnectStripeEndpoint.ts +8 -1
- package/src/endpoints/organization/dashboard/stripe/DeleteStripeAccountEndpoint.ts +8 -1
- package/src/endpoints/organization/dashboard/stripe/UpdateStripeAccountEndpoint.ts +14 -2
- package/src/helpers/PeriodHelper.ts +30 -25
- package/src/services/AuditLogService.ts +105 -20
- package/src/services/explainPatch.ts +452 -309
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { ArrayDecoder, AutoEncoder,
|
|
2
|
-
import {
|
|
1
|
+
import { ArrayDecoder, AutoEncoder, BooleanDecoder, DateDecoder, EnumDecoder, Field, getOptionalId, IntegerDecoder, isPatchableArray, isPatchMap, MapDecoder, StringDecoder, SymbolDecoder } from '@simonbackx/simple-encoding';
|
|
2
|
+
import { AuditLogPatchItem, AuditLogPatchItemType, AuditLogReplacement, AuditLogReplacementType, BooleanStatus, Image, isEmptyFilter, isUuid, PropertyFilter, RichText, Version } from '@stamhoofd/structures';
|
|
3
3
|
import { Formatter } from '@stamhoofd/utility';
|
|
4
|
+
import { get } from 'http';
|
|
4
5
|
|
|
5
6
|
export type PatchExplainer = {
|
|
6
7
|
key: string;
|
|
@@ -20,14 +21,34 @@ function createStringChangeHandler(key: string) {
|
|
|
20
21
|
|
|
21
22
|
return [
|
|
22
23
|
AuditLogPatchItem.create({
|
|
23
|
-
key:
|
|
24
|
-
oldValue:
|
|
25
|
-
value:
|
|
24
|
+
key: getAutoEncoderKey(key),
|
|
25
|
+
oldValue: getAutoEncoderValue(oldValue) || getAutoEncoderName(oldValue) || undefined,
|
|
26
|
+
value: getAutoEncoderValue(value) || getAutoEncoderName(value) || undefined,
|
|
26
27
|
}).autoType(),
|
|
27
28
|
];
|
|
28
29
|
};
|
|
29
30
|
}
|
|
30
31
|
|
|
32
|
+
function createEnumChangeHandler(key: string) {
|
|
33
|
+
return (oldValue: unknown, value: unknown) => {
|
|
34
|
+
if (oldValue === value) {
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (value === undefined) {
|
|
39
|
+
// Not altered
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return [
|
|
44
|
+
AuditLogPatchItem.create({
|
|
45
|
+
key: getAutoEncoderKey(key),
|
|
46
|
+
oldValue: typeof oldValue === 'string' ? AuditLogReplacement.key(oldValue) : undefined,
|
|
47
|
+
value: typeof value === 'string' ? AuditLogReplacement.key(value) : undefined,
|
|
48
|
+
}).autoType(),
|
|
49
|
+
];
|
|
50
|
+
};
|
|
51
|
+
}
|
|
31
52
|
function createIntegerChangeHandler(key: string) {
|
|
32
53
|
return (oldValue: unknown, value: unknown) => {
|
|
33
54
|
if ((typeof oldValue !== 'number' && oldValue !== null) || (typeof value !== 'number' && value !== null)) {
|
|
@@ -40,7 +61,7 @@ function createIntegerChangeHandler(key: string) {
|
|
|
40
61
|
const formatter: (typeof Formatter.price | typeof Formatter.integer) = key.toLowerCase().includes('price') ? Formatter.price.bind(Formatter) : Formatter.integer.bind(Formatter);
|
|
41
62
|
return [
|
|
42
63
|
AuditLogPatchItem.create({
|
|
43
|
-
key:
|
|
64
|
+
key: getAutoEncoderKey(key),
|
|
44
65
|
oldValue: oldValue !== null ? AuditLogReplacement.string(formatter(oldValue)) : undefined,
|
|
45
66
|
value: value !== null ? AuditLogReplacement.string(formatter(value)) : undefined,
|
|
46
67
|
}).autoType(),
|
|
@@ -50,7 +71,11 @@ function createIntegerChangeHandler(key: string) {
|
|
|
50
71
|
|
|
51
72
|
function createDateChangeHandler(key: string) {
|
|
52
73
|
return (oldValue: unknown, value: unknown) => {
|
|
53
|
-
if (
|
|
74
|
+
if (!(oldValue instanceof Date) && oldValue !== null) {
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if ((!(value instanceof Date)) && value !== null) {
|
|
54
79
|
return [];
|
|
55
80
|
}
|
|
56
81
|
|
|
@@ -68,7 +93,7 @@ function createDateChangeHandler(key: string) {
|
|
|
68
93
|
|
|
69
94
|
return [
|
|
70
95
|
AuditLogPatchItem.create({
|
|
71
|
-
key:
|
|
96
|
+
key: getAutoEncoderKey(key),
|
|
72
97
|
oldValue: dno ? AuditLogReplacement.string(dno) : undefined,
|
|
73
98
|
value: dn ? AuditLogReplacement.string(dn) : undefined,
|
|
74
99
|
}).autoType(),
|
|
@@ -92,48 +117,91 @@ function createBooleanChangeHandler(key: string) {
|
|
|
92
117
|
|
|
93
118
|
return [
|
|
94
119
|
AuditLogPatchItem.create({
|
|
95
|
-
key:
|
|
96
|
-
oldValue: oldValue === true ? AuditLogReplacement.
|
|
97
|
-
value: value === true ? AuditLogReplacement.
|
|
120
|
+
key: getAutoEncoderKey(key),
|
|
121
|
+
oldValue: oldValue === true ? AuditLogReplacement.key('on') : (oldValue === false ? AuditLogReplacement.key('off') : undefined),
|
|
122
|
+
value: value === true ? AuditLogReplacement.key('on') : (value === false ? AuditLogReplacement.key('off') : undefined),
|
|
98
123
|
}).autoType(),
|
|
99
124
|
];
|
|
100
125
|
};
|
|
101
126
|
}
|
|
102
127
|
|
|
103
|
-
function
|
|
128
|
+
function getAutoEncoderKey(autoEncoder: string): AuditLogReplacement;
|
|
129
|
+
function getAutoEncoderKey(autoEncoder: unknown): AuditLogReplacement | null;
|
|
130
|
+
function getAutoEncoderKey(autoEncoder: unknown): AuditLogReplacement | null {
|
|
104
131
|
if (typeof autoEncoder === 'string') {
|
|
105
|
-
|
|
132
|
+
if (isUuid(autoEncoder)) {
|
|
133
|
+
return AuditLogReplacement.uuid(autoEncoder);
|
|
134
|
+
}
|
|
135
|
+
return AuditLogReplacement.key(autoEncoder);
|
|
106
136
|
}
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
107
139
|
|
|
108
|
-
|
|
109
|
-
|
|
140
|
+
function getAutoEncoderName(autoEncoder: unknown): AuditLogReplacement | null {
|
|
141
|
+
if (typeof autoEncoder === 'string') {
|
|
142
|
+
if (isUuid(autoEncoder)) {
|
|
143
|
+
return AuditLogReplacement.uuid(autoEncoder);
|
|
144
|
+
}
|
|
145
|
+
return AuditLogReplacement.string(autoEncoder);
|
|
110
146
|
}
|
|
111
147
|
|
|
112
|
-
if (autoEncoder
|
|
113
|
-
|
|
148
|
+
if (typeof autoEncoder === 'object' && autoEncoder !== null && 'getPatchName' in autoEncoder && typeof autoEncoder.getPatchName === 'function') {
|
|
149
|
+
const name = autoEncoder.getPatchName();
|
|
150
|
+
if (typeof name === 'string') {
|
|
151
|
+
return name ? AuditLogReplacement.string(name) : AuditLogReplacement.key('untitled');
|
|
152
|
+
}
|
|
114
153
|
}
|
|
115
154
|
|
|
116
155
|
if (typeof autoEncoder === 'object' && autoEncoder !== null && 'name' in autoEncoder && typeof autoEncoder.name === 'string') {
|
|
117
|
-
return autoEncoder.name;
|
|
156
|
+
return autoEncoder.name ? AuditLogReplacement.string(autoEncoder.name) : AuditLogReplacement.key('untitled');
|
|
118
157
|
}
|
|
119
158
|
return null;
|
|
120
159
|
}
|
|
160
|
+
function getAutoEncoderPutValue(autoEncoder: unknown): AuditLogReplacement | null {
|
|
161
|
+
if (typeof autoEncoder === 'object' && autoEncoder !== null && 'getPutValue' in autoEncoder && typeof autoEncoder.getPutValue === 'function') {
|
|
162
|
+
const name = autoEncoder.getPutValue();
|
|
163
|
+
if (typeof name === 'string') {
|
|
164
|
+
return AuditLogReplacement.string(name);
|
|
165
|
+
}
|
|
166
|
+
if (name instanceof AuditLogReplacement) {
|
|
167
|
+
return name;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return getAutoEncoderValue(autoEncoder);
|
|
171
|
+
}
|
|
121
172
|
|
|
122
173
|
function getAutoEncoderValue(autoEncoder: unknown): AuditLogReplacement | null {
|
|
123
174
|
if (typeof autoEncoder === 'string') {
|
|
175
|
+
if (isUuid(autoEncoder)) {
|
|
176
|
+
return AuditLogReplacement.uuid(autoEncoder);
|
|
177
|
+
}
|
|
124
178
|
return AuditLogReplacement.string(autoEncoder);
|
|
125
179
|
}
|
|
126
180
|
|
|
127
|
-
if (autoEncoder
|
|
128
|
-
|
|
181
|
+
if (typeof autoEncoder === 'symbol') {
|
|
182
|
+
const name = Symbol.keyFor(autoEncoder);
|
|
183
|
+
if (name) {
|
|
184
|
+
return AuditLogReplacement.key(name);
|
|
185
|
+
}
|
|
186
|
+
return AuditLogReplacement.key('unknown');
|
|
129
187
|
}
|
|
130
188
|
|
|
131
|
-
if (autoEncoder
|
|
132
|
-
return AuditLogReplacement.string(
|
|
189
|
+
if (typeof autoEncoder === 'number') {
|
|
190
|
+
return AuditLogReplacement.string(Formatter.integer(autoEncoder));
|
|
133
191
|
}
|
|
134
192
|
|
|
135
|
-
if (
|
|
136
|
-
return AuditLogReplacement.string(autoEncoder
|
|
193
|
+
if (autoEncoder instanceof Date) {
|
|
194
|
+
return AuditLogReplacement.string(Formatter.dateTime(autoEncoder, true, true));
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (typeof autoEncoder === 'object' && autoEncoder !== null && 'getPatchValue' in autoEncoder && typeof autoEncoder.getPatchValue === 'function') {
|
|
198
|
+
const name = autoEncoder.getPatchValue();
|
|
199
|
+
if (typeof name === 'string') {
|
|
200
|
+
return AuditLogReplacement.string(name);
|
|
201
|
+
}
|
|
202
|
+
if (name instanceof AuditLogReplacement) {
|
|
203
|
+
return name;
|
|
204
|
+
}
|
|
137
205
|
}
|
|
138
206
|
|
|
139
207
|
if (autoEncoder instanceof Image) {
|
|
@@ -147,144 +215,239 @@ function getAutoEncoderValue(autoEncoder: unknown): AuditLogReplacement | null {
|
|
|
147
215
|
if (autoEncoder instanceof RichText) {
|
|
148
216
|
return AuditLogReplacement.string(autoEncoder.text);
|
|
149
217
|
}
|
|
218
|
+
|
|
219
|
+
if (autoEncoder instanceof PropertyFilter) {
|
|
220
|
+
if (autoEncoder.isAlwaysEnabledAndRequired) {
|
|
221
|
+
return AuditLogReplacement.key('alwaysEnabledAndRequired');
|
|
222
|
+
}
|
|
223
|
+
if (autoEncoder.enabledWhen === null && autoEncoder.requiredWhen === null) {
|
|
224
|
+
return AuditLogReplacement.key('alwaysEnabledAndOptional');
|
|
225
|
+
}
|
|
226
|
+
if (autoEncoder.enabledWhen !== null && autoEncoder.requiredWhen === null) {
|
|
227
|
+
return AuditLogReplacement.key('sometimesEnabledAndOptional');
|
|
228
|
+
}
|
|
229
|
+
if (autoEncoder.enabledWhen === null && autoEncoder.requiredWhen !== null) {
|
|
230
|
+
return AuditLogReplacement.key('alwaysEnabledAndSometimesRequired');
|
|
231
|
+
}
|
|
232
|
+
if (autoEncoder.enabledWhen !== null && isEmptyFilter(autoEncoder.requiredWhen)) {
|
|
233
|
+
return AuditLogReplacement.key('sometimesEnabledAndRequired');
|
|
234
|
+
}
|
|
235
|
+
return AuditLogReplacement.key('sometimesEnabledAndSometimesRequired');
|
|
236
|
+
}
|
|
237
|
+
|
|
150
238
|
return null;
|
|
151
239
|
}
|
|
152
240
|
|
|
153
|
-
function
|
|
154
|
-
return (
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
241
|
+
function getKeySingular(key: string) {
|
|
242
|
+
return key.replace(/ies$/, 'y').replace(/s$/, '');
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function findOriginalById(id: unknown, oldArray: unknown[]): unknown | null {
|
|
246
|
+
return id ? oldArray.find(v => getId(v) === id) : null;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function getId(object: unknown): string | number | null {
|
|
250
|
+
const id = getOptionalId(object);
|
|
251
|
+
if (typeof id !== 'string' && typeof id !== 'number') {
|
|
252
|
+
if (object instanceof AutoEncoder) {
|
|
253
|
+
const encoded = object.encode({ version: Version });
|
|
254
|
+
return JSON.stringify(encoded);
|
|
158
255
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
256
|
+
return JSON.stringify(object);
|
|
257
|
+
}
|
|
258
|
+
return id;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function findOriginal(put: unknown, oldArray: unknown[]): unknown | null {
|
|
262
|
+
return findOriginalById(getId(put), oldArray);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function processPut(key: string, put: unknown, original: unknown | null, createdIdSet?: Set<string>): AuditLogPatchItem[] {
|
|
266
|
+
const items: AuditLogPatchItem[] = [];
|
|
267
|
+
const keySingular = getKeySingular(key);
|
|
268
|
+
const v = getAutoEncoderPutValue(put);
|
|
269
|
+
|
|
270
|
+
// Added a new parent
|
|
271
|
+
if (!original) {
|
|
272
|
+
items.push(
|
|
273
|
+
AuditLogPatchItem.create({
|
|
274
|
+
key: AuditLogReplacement.key(keySingular).append(getAutoEncoderName(put)),
|
|
275
|
+
value: v || undefined,
|
|
276
|
+
type: AuditLogPatchItemType.Added,
|
|
277
|
+
}),
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Little hack: detect PUT/DELETE behaviour:
|
|
282
|
+
if (createdIdSet) {
|
|
283
|
+
const id = getId(put);
|
|
284
|
+
if (id && typeof id === 'string') {
|
|
285
|
+
createdIdSet.add(id);
|
|
162
286
|
}
|
|
287
|
+
}
|
|
163
288
|
|
|
164
|
-
|
|
165
|
-
|
|
289
|
+
if (!original && (v || getAutoEncoderName(put))) {
|
|
290
|
+
// Simplify addition: don't show all added properties
|
|
291
|
+
return items;
|
|
292
|
+
}
|
|
166
293
|
|
|
167
|
-
|
|
294
|
+
items.push(
|
|
295
|
+
...explainPatch(
|
|
296
|
+
original ?? null,
|
|
297
|
+
put,
|
|
298
|
+
).map((i) => {
|
|
299
|
+
i.key = i.key.prepend(getAutoEncoderName(original) || getAutoEncoderName(put) || AuditLogReplacement.key('item'));
|
|
300
|
+
i.key = i.key.prepend(getAutoEncoderKey(key));
|
|
301
|
+
return i;
|
|
302
|
+
}),
|
|
303
|
+
);
|
|
304
|
+
return items;
|
|
305
|
+
}
|
|
168
306
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
307
|
+
function processPatch(key: string, patch: unknown, original: unknown | null): AuditLogPatchItem[] {
|
|
308
|
+
if (!original) {
|
|
309
|
+
// Not supported
|
|
310
|
+
return [];
|
|
311
|
+
}
|
|
174
312
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
// Not supported
|
|
179
|
-
original = null;
|
|
180
|
-
}
|
|
313
|
+
if (patch === original) {
|
|
314
|
+
return [];
|
|
315
|
+
}
|
|
181
316
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
317
|
+
const items: AuditLogPatchItem[] = [];
|
|
318
|
+
const keySingular = getKeySingular(key);
|
|
319
|
+
|
|
320
|
+
const l = explainPatch(
|
|
321
|
+
original,
|
|
322
|
+
patch,
|
|
323
|
+
).map((i) => {
|
|
324
|
+
i.key = i.key.prepend(getAutoEncoderName(original) || getAutoEncoderName(patch) || AuditLogReplacement.key('item'));
|
|
325
|
+
i.key = i.key.prepend(getAutoEncoderKey(key));
|
|
326
|
+
return i;
|
|
327
|
+
});
|
|
328
|
+
let ov = getAutoEncoderValue(original);
|
|
329
|
+
let v = getAutoEncoderValue(patch);
|
|
330
|
+
|
|
331
|
+
if (l.length === 0 && patch instanceof AutoEncoder && patch.isPatch()) {
|
|
332
|
+
items.push(
|
|
333
|
+
AuditLogPatchItem.create({
|
|
334
|
+
key: getAutoEncoderKey(keySingular).append(getAutoEncoderName(original) || getAutoEncoderName(patch) || AuditLogReplacement.key('item')),
|
|
335
|
+
oldValue: ov || undefined,
|
|
336
|
+
value: v || undefined,
|
|
337
|
+
type: AuditLogPatchItemType.Changed,
|
|
338
|
+
}),
|
|
339
|
+
);
|
|
340
|
+
return items;
|
|
341
|
+
}
|
|
192
342
|
|
|
193
|
-
|
|
194
|
-
|
|
343
|
+
if (ov && v) {
|
|
344
|
+
if (ov.toString() === v.toString()) {
|
|
345
|
+
ov = null;
|
|
346
|
+
v = null;
|
|
347
|
+
|
|
348
|
+
if (l.length === 0) {
|
|
349
|
+
// Probably no change
|
|
350
|
+
return [];
|
|
195
351
|
}
|
|
352
|
+
}
|
|
196
353
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
354
|
+
// Simplify changes by providing one change instead of for all keys
|
|
355
|
+
items.push(
|
|
356
|
+
AuditLogPatchItem.create({
|
|
357
|
+
key: getAutoEncoderKey(keySingular).append(getAutoEncoderName(original) || getAutoEncoderName(v) || AuditLogReplacement.key('item')),
|
|
358
|
+
oldValue: ov || undefined,
|
|
359
|
+
value: v || undefined,
|
|
360
|
+
type: AuditLogPatchItemType.Changed,
|
|
361
|
+
}),
|
|
362
|
+
);
|
|
363
|
+
return items;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
items.push(
|
|
367
|
+
...l,
|
|
368
|
+
);
|
|
369
|
+
|
|
370
|
+
return items;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function processDelete(key: string, deletedItem: unknown, createdIdSet?: Set<string>): AuditLogPatchItem[] {
|
|
374
|
+
if (createdIdSet) {
|
|
375
|
+
const id = getId(deletedItem);
|
|
376
|
+
if (id && typeof id === 'string' && createdIdSet.has(id)) {
|
|
377
|
+
// DELETE + PUT happened
|
|
378
|
+
return [];
|
|
210
379
|
}
|
|
380
|
+
}
|
|
211
381
|
|
|
212
|
-
|
|
213
|
-
const original = oldValue.find(v => v.id === patch.id);
|
|
214
|
-
if (!original) {
|
|
215
|
-
// Not supported
|
|
216
|
-
continue;
|
|
217
|
-
}
|
|
218
|
-
if (!(original instanceof AutoEncoder)) {
|
|
219
|
-
// Not supported
|
|
220
|
-
continue;
|
|
221
|
-
}
|
|
382
|
+
const v = getAutoEncoderPutValue(deletedItem);
|
|
222
383
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
patch,
|
|
226
|
-
).map((i) => {
|
|
227
|
-
const name = getAutoEncoderName(original);
|
|
228
|
-
if (name) {
|
|
229
|
-
i.key = i.key.prepend(AuditLogReplacement.string(name));
|
|
230
|
-
}
|
|
231
|
-
i.key = i.key.prepend(AuditLogReplacement.key(key));
|
|
232
|
-
return i;
|
|
233
|
-
});
|
|
384
|
+
const keySingular = getKeySingular(key);
|
|
385
|
+
const k = AuditLogReplacement.key(keySingular).append(getAutoEncoderName(deletedItem));
|
|
234
386
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
}
|
|
387
|
+
return [
|
|
388
|
+
AuditLogPatchItem.create({
|
|
389
|
+
key: k,
|
|
390
|
+
type: AuditLogPatchItemType.Removed,
|
|
391
|
+
oldValue: v ?? undefined,
|
|
392
|
+
}),
|
|
393
|
+
];
|
|
394
|
+
}
|
|
244
395
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
396
|
+
function createArrayChangeHandler(key: string) {
|
|
397
|
+
return (oldValue: unknown, value: unknown) => {
|
|
398
|
+
if (!Array.isArray(oldValue)) {
|
|
399
|
+
// Not supported
|
|
400
|
+
return [];
|
|
248
401
|
}
|
|
402
|
+
const items: AuditLogPatchItem[] = [];
|
|
249
403
|
|
|
250
|
-
|
|
251
|
-
if (
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
404
|
+
if (!isPatchableArray(value)) {
|
|
405
|
+
if (Array.isArray(value)) {
|
|
406
|
+
// Search for puts
|
|
407
|
+
for (const newItem of value) {
|
|
408
|
+
const original = findOriginal(newItem, oldValue);
|
|
409
|
+
|
|
410
|
+
if (!original) {
|
|
411
|
+
// Has been added
|
|
412
|
+
items.push(...processPut(key, newItem, original));
|
|
413
|
+
}
|
|
414
|
+
else {
|
|
415
|
+
// Has been overwritten
|
|
416
|
+
items.push(...processPatch(key, newItem, original));
|
|
417
|
+
}
|
|
418
|
+
}
|
|
263
419
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
420
|
+
// Search for deletes
|
|
421
|
+
for (const original of oldValue) {
|
|
422
|
+
const newItem = findOriginal(original, value);
|
|
423
|
+
if (!newItem) {
|
|
424
|
+
// Has been deleted
|
|
425
|
+
items.push(...processDelete(key, original));
|
|
426
|
+
}
|
|
427
|
+
}
|
|
267
428
|
}
|
|
429
|
+
// Not supported
|
|
430
|
+
return items;
|
|
431
|
+
}
|
|
268
432
|
|
|
269
|
-
|
|
433
|
+
const createdIdSet = new Set<string>();
|
|
270
434
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
}
|
|
435
|
+
for (const { put } of value.getPuts()) {
|
|
436
|
+
items.push(...processPut(key, put, findOriginal(put, oldValue), createdIdSet));
|
|
437
|
+
}
|
|
275
438
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
);
|
|
439
|
+
for (const patch of value.getPatches()) {
|
|
440
|
+
items.push(...processPatch(key, patch, findOriginal(patch, oldValue)));
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
for (const id of value.getDeletes()) {
|
|
444
|
+
items.push(...processDelete(key, findOriginalById(id, oldValue), createdIdSet));
|
|
282
445
|
}
|
|
283
446
|
|
|
284
447
|
if (value.getMoves().length > 0) {
|
|
285
448
|
items.push(
|
|
286
449
|
AuditLogPatchItem.create({
|
|
287
|
-
key:
|
|
450
|
+
key: getAutoEncoderKey(key),
|
|
288
451
|
type: AuditLogPatchItemType.Reordered,
|
|
289
452
|
}),
|
|
290
453
|
);
|
|
@@ -293,9 +456,9 @@ function createArrayChangeHandler(key: string) {
|
|
|
293
456
|
};
|
|
294
457
|
}
|
|
295
458
|
|
|
296
|
-
function createMapChangeHandler(key
|
|
459
|
+
function createMapChangeHandler(key?: string) {
|
|
297
460
|
return (oldValue: unknown, value: unknown) => {
|
|
298
|
-
if (!
|
|
461
|
+
if (!(value instanceof Map)) {
|
|
299
462
|
// Not supported
|
|
300
463
|
return [];
|
|
301
464
|
}
|
|
@@ -305,22 +468,27 @@ function createMapChangeHandler(key: string) {
|
|
|
305
468
|
}
|
|
306
469
|
|
|
307
470
|
const items: AuditLogPatchItem[] = [];
|
|
308
|
-
const keySingular = key.replace(/ies$/, 'y').replace(/s$/, '');
|
|
471
|
+
const keySingular = key ? key.replace(/ies$/, 'y').replace(/s$/, '') : key;
|
|
472
|
+
const isPatch = isPatchMap(value);
|
|
309
473
|
|
|
310
474
|
for (const [k, v] of value.entries()) {
|
|
311
475
|
if (typeof k !== 'string') {
|
|
312
476
|
// Not supported
|
|
313
477
|
continue;
|
|
314
478
|
}
|
|
315
|
-
|
|
479
|
+
let original = oldValue.get(k);
|
|
480
|
+
|
|
481
|
+
if (v instanceof Map && !original) {
|
|
482
|
+
original = new Map();
|
|
483
|
+
}
|
|
316
484
|
|
|
317
|
-
if (v === null) {
|
|
485
|
+
if (v === null && isPatch) {
|
|
318
486
|
// Delete
|
|
319
487
|
if (original) {
|
|
320
488
|
items.push(
|
|
321
489
|
AuditLogPatchItem.create({
|
|
322
|
-
key: AuditLogReplacement.key(keySingular),
|
|
323
|
-
oldValue:
|
|
490
|
+
key: AuditLogReplacement.key(keySingular).append(getAutoEncoderKey(k)).append(getAutoEncoderName(original)),
|
|
491
|
+
oldValue: getAutoEncoderPutValue(original) || undefined,
|
|
324
492
|
type: AuditLogPatchItemType.Removed,
|
|
325
493
|
}),
|
|
326
494
|
);
|
|
@@ -332,30 +500,55 @@ function createMapChangeHandler(key: string) {
|
|
|
332
500
|
// added
|
|
333
501
|
items.push(
|
|
334
502
|
AuditLogPatchItem.create({
|
|
335
|
-
key: AuditLogReplacement.key(keySingular),
|
|
336
|
-
value:
|
|
503
|
+
key: AuditLogReplacement.key(keySingular).append(getAutoEncoderKey(k)).append(getAutoEncoderName(v)),
|
|
504
|
+
value: getAutoEncoderPutValue(v) || undefined,
|
|
337
505
|
type: AuditLogPatchItemType.Added,
|
|
338
506
|
}),
|
|
339
507
|
);
|
|
340
508
|
}
|
|
341
509
|
else {
|
|
510
|
+
let ov = getAutoEncoderValue(original);
|
|
511
|
+
let nv = getAutoEncoderValue(v);
|
|
512
|
+
|
|
342
513
|
const c = explainPatch(
|
|
343
514
|
original,
|
|
344
|
-
v
|
|
515
|
+
v,
|
|
345
516
|
).map((i) => {
|
|
346
|
-
|
|
347
|
-
if (name) {
|
|
348
|
-
i.key = i.key.prepend(name);
|
|
349
|
-
}
|
|
517
|
+
i.key = i.key.prepend(getAutoEncoderName(original) || getAutoEncoderName(v) || getAutoEncoderKey(k));
|
|
350
518
|
i.key = i.key.prepend(AuditLogReplacement.key(keySingular));
|
|
351
519
|
return i;
|
|
352
520
|
});
|
|
353
521
|
|
|
354
|
-
if (
|
|
522
|
+
if (ov && nv) {
|
|
523
|
+
if (ov.toString() === nv.toString()) {
|
|
524
|
+
ov = null;
|
|
525
|
+
nv = null;
|
|
526
|
+
|
|
527
|
+
if (c.length === 0) {
|
|
528
|
+
// Probably no change
|
|
529
|
+
continue;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// Simplify change
|
|
534
|
+
items.push(
|
|
535
|
+
AuditLogPatchItem.create({
|
|
536
|
+
key: AuditLogReplacement.key(keySingular).append(getAutoEncoderKey(k)).append(getAutoEncoderName(original) || getAutoEncoderName(v)),
|
|
537
|
+
oldValue: ov || undefined,
|
|
538
|
+
value: nv || undefined,
|
|
539
|
+
type: AuditLogPatchItemType.Changed,
|
|
540
|
+
}),
|
|
541
|
+
);
|
|
542
|
+
continue;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
if (c.length === 0 && v instanceof AutoEncoder && v.isPatch()) {
|
|
355
546
|
// Manual log
|
|
356
547
|
items.push(
|
|
357
548
|
AuditLogPatchItem.create({
|
|
358
|
-
key: AuditLogReplacement.key(keySingular).append(
|
|
549
|
+
key: AuditLogReplacement.key(keySingular).append(getAutoEncoderKey(k)).append(getAutoEncoderName(original) || getAutoEncoderName(v)),
|
|
550
|
+
oldValue: getAutoEncoderValue(original) || undefined,
|
|
551
|
+
value: getAutoEncoderValue(v) || undefined,
|
|
359
552
|
type: AuditLogPatchItemType.Changed,
|
|
360
553
|
}),
|
|
361
554
|
);
|
|
@@ -368,121 +561,124 @@ function createMapChangeHandler(key: string) {
|
|
|
368
561
|
}
|
|
369
562
|
}
|
|
370
563
|
|
|
564
|
+
if (!isPatch) {
|
|
565
|
+
// Loop old values
|
|
566
|
+
for (const [k, v] of oldValue.entries()) {
|
|
567
|
+
if (typeof k !== 'string') {
|
|
568
|
+
// Not supported
|
|
569
|
+
continue;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
if (value.has(k)) {
|
|
573
|
+
continue;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
items.push(
|
|
577
|
+
AuditLogPatchItem.create({
|
|
578
|
+
key: AuditLogReplacement.key(keySingular).append(getAutoEncoderKey(k)).append(getAutoEncoderName(v)),
|
|
579
|
+
oldValue: getAutoEncoderPutValue(v) || undefined,
|
|
580
|
+
type: AuditLogPatchItemType.Removed,
|
|
581
|
+
}),
|
|
582
|
+
);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
371
586
|
return items;
|
|
372
587
|
};
|
|
373
588
|
}
|
|
374
589
|
|
|
375
|
-
function
|
|
590
|
+
function createUnknownChangeHandler(key: string) {
|
|
376
591
|
return (oldValue: unknown, value: unknown) => {
|
|
377
|
-
if (
|
|
378
|
-
// Not supported
|
|
592
|
+
if (typeof value !== 'object' && value !== null) {
|
|
379
593
|
return [];
|
|
380
594
|
}
|
|
381
|
-
const keySingular = key.replace(/ies$/, 'y').replace(/s$/, '');
|
|
382
|
-
|
|
383
|
-
if (Array.isArray(value)) {
|
|
384
|
-
if (!value.every(v => typeof v === 'string')) {
|
|
385
|
-
// Not supported
|
|
386
|
-
return [];
|
|
387
|
-
}
|
|
388
|
-
if (!oldValue.every(v => typeof v === 'string')) {
|
|
389
|
-
// Not supported
|
|
390
|
-
return [];
|
|
391
|
-
}
|
|
392
595
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
if (valueStr === oldValueStr) {
|
|
398
|
-
return [];
|
|
399
|
-
}
|
|
596
|
+
if (oldValue === value) {
|
|
597
|
+
return [];
|
|
598
|
+
}
|
|
400
599
|
|
|
600
|
+
if (!oldValue && getAutoEncoderValue(value)) {
|
|
601
|
+
// Simplify addition
|
|
401
602
|
return [
|
|
402
603
|
AuditLogPatchItem.create({
|
|
403
|
-
key:
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
})
|
|
604
|
+
key: getAutoEncoderKey(key),
|
|
605
|
+
value: getAutoEncoderPutValue(value) || undefined,
|
|
606
|
+
type: AuditLogPatchItemType.Added,
|
|
607
|
+
}),
|
|
407
608
|
];
|
|
408
609
|
}
|
|
409
610
|
|
|
410
|
-
if (
|
|
411
|
-
|
|
412
|
-
|
|
611
|
+
if (oldValue && value === null) {
|
|
612
|
+
return [
|
|
613
|
+
AuditLogPatchItem.create({
|
|
614
|
+
key: getAutoEncoderKey(key),
|
|
615
|
+
oldValue: getAutoEncoderPutValue(oldValue) || undefined,
|
|
616
|
+
type: AuditLogPatchItemType.Removed,
|
|
617
|
+
}),
|
|
618
|
+
];
|
|
413
619
|
}
|
|
414
620
|
|
|
415
|
-
const items
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
if (typeof put !== 'string') {
|
|
420
|
-
// Not supported
|
|
421
|
-
continue;
|
|
422
|
-
}
|
|
621
|
+
const items = explainPatch(oldValue, value).map((i) => {
|
|
622
|
+
i.key = i.key.prepend(getAutoEncoderKey(key));
|
|
623
|
+
return i;
|
|
624
|
+
});
|
|
423
625
|
|
|
424
|
-
|
|
425
|
-
|
|
626
|
+
let v = getAutoEncoderValue(value);
|
|
627
|
+
let ov = getAutoEncoderValue(oldValue);
|
|
426
628
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
}
|
|
437
|
-
createdIdSet.add(put);
|
|
629
|
+
if (oldValue && value && getAutoEncoderValue(value) && items.length === 0 && value instanceof AutoEncoder && value.isPatch()) {
|
|
630
|
+
return [
|
|
631
|
+
AuditLogPatchItem.create({
|
|
632
|
+
key: getAutoEncoderKey(key),
|
|
633
|
+
value: v || undefined,
|
|
634
|
+
oldValue: ov || undefined,
|
|
635
|
+
type: AuditLogPatchItemType.Changed,
|
|
636
|
+
}),
|
|
637
|
+
];
|
|
438
638
|
}
|
|
439
639
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
if (createdIdSet.has(id)) {
|
|
446
|
-
// DELETE + PUT happened
|
|
447
|
-
continue;
|
|
448
|
-
}
|
|
640
|
+
if (v && ov) {
|
|
641
|
+
// Simplify change
|
|
642
|
+
if (v.toString() === ov.toString()) {
|
|
643
|
+
v = null;
|
|
644
|
+
ov = null;
|
|
449
645
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
646
|
+
if (items.length === 0) {
|
|
647
|
+
// Probably no change
|
|
648
|
+
return [];
|
|
649
|
+
}
|
|
454
650
|
}
|
|
455
651
|
|
|
456
|
-
|
|
457
|
-
AuditLogPatchItem.create({
|
|
458
|
-
key: AuditLogReplacement.key(keySingular),
|
|
459
|
-
oldValue: AuditLogReplacement.string(original),
|
|
460
|
-
type: AuditLogPatchItemType.Removed,
|
|
461
|
-
}),
|
|
462
|
-
);
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
if (value.getMoves().length > 0) {
|
|
466
|
-
items.push(
|
|
652
|
+
return [
|
|
467
653
|
AuditLogPatchItem.create({
|
|
468
|
-
key:
|
|
469
|
-
|
|
654
|
+
key: getAutoEncoderKey(key),
|
|
655
|
+
value: v || undefined,
|
|
656
|
+
oldValue: ov || undefined,
|
|
657
|
+
type: AuditLogPatchItemType.Changed,
|
|
470
658
|
}),
|
|
471
|
-
|
|
659
|
+
];
|
|
472
660
|
}
|
|
473
661
|
return items;
|
|
474
662
|
};
|
|
475
663
|
}
|
|
476
664
|
|
|
477
665
|
function getExplainerForField(field: Field<any>) {
|
|
478
|
-
if (field.decoder === StringDecoder
|
|
666
|
+
if (field.decoder === StringDecoder) {
|
|
479
667
|
return createStringChangeHandler(field.property);
|
|
480
668
|
}
|
|
481
669
|
|
|
670
|
+
if (field.decoder instanceof EnumDecoder) {
|
|
671
|
+
return createEnumChangeHandler(field.property);
|
|
672
|
+
}
|
|
673
|
+
|
|
482
674
|
if (field.decoder instanceof SymbolDecoder) {
|
|
483
|
-
if (field.decoder.decoder === StringDecoder
|
|
675
|
+
if (field.decoder.decoder === StringDecoder) {
|
|
484
676
|
return createStringChangeHandler(field.property);
|
|
485
677
|
}
|
|
678
|
+
|
|
679
|
+
if (field.decoder.decoder instanceof EnumDecoder) {
|
|
680
|
+
return createEnumChangeHandler(field.property);
|
|
681
|
+
}
|
|
486
682
|
}
|
|
487
683
|
|
|
488
684
|
if (field.decoder === DateDecoder) {
|
|
@@ -497,10 +693,6 @@ function getExplainerForField(field: Field<any>) {
|
|
|
497
693
|
return createIntegerChangeHandler(field.property);
|
|
498
694
|
}
|
|
499
695
|
|
|
500
|
-
if (field.decoder instanceof ArrayDecoder && field.decoder.decoder === StringDecoder) {
|
|
501
|
-
return createSimpleArrayChangeHandler(field.property);
|
|
502
|
-
}
|
|
503
|
-
|
|
504
696
|
if (field.decoder instanceof ArrayDecoder) {
|
|
505
697
|
return createArrayChangeHandler(field.property);
|
|
506
698
|
}
|
|
@@ -524,106 +716,57 @@ function getExplainerForField(field: Field<any>) {
|
|
|
524
716
|
|
|
525
717
|
return [
|
|
526
718
|
AuditLogPatchItem.create({
|
|
527
|
-
key:
|
|
528
|
-
oldValue: wasTrueOld === true ? AuditLogReplacement.
|
|
529
|
-
value: isTrue === true ? AuditLogReplacement.
|
|
530
|
-
}),
|
|
719
|
+
key: getAutoEncoderKey(field.property),
|
|
720
|
+
oldValue: wasTrueOld === true ? AuditLogReplacement.key('checked') : (wasTrueOld === false ? AuditLogReplacement.key('unchecked') : undefined),
|
|
721
|
+
value: isTrue === true ? AuditLogReplacement.key('checked') : (isTrue === false ? AuditLogReplacement.key('unchecked') : undefined),
|
|
722
|
+
}).autoType(),
|
|
531
723
|
];
|
|
532
724
|
};
|
|
533
725
|
}
|
|
534
726
|
|
|
535
|
-
|
|
536
|
-
return (oldValue: unknown, value: unknown) => {
|
|
537
|
-
if (!(value instanceof AutoEncoder) && value !== null) {
|
|
538
|
-
return [];
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
if (oldValue === value) {
|
|
542
|
-
return [];
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
if (oldValue && value && getAutoEncoderValue(value as AutoEncoder)) {
|
|
546
|
-
// Simplify addition
|
|
547
|
-
return [
|
|
548
|
-
AuditLogPatchItem.create({
|
|
549
|
-
key: AuditLogReplacement.key(field.property),
|
|
550
|
-
value: getAutoEncoderValue(value as AutoEncoder) || AuditLogReplacement.key(field.property),
|
|
551
|
-
oldValue: getAutoEncoderValue(oldValue as AutoEncoder) || AuditLogReplacement.key(field.property),
|
|
552
|
-
type: AuditLogPatchItemType.Changed,
|
|
553
|
-
}),
|
|
554
|
-
];
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
if (!oldValue && getAutoEncoderValue(value as AutoEncoder)) {
|
|
558
|
-
// Simplify addition
|
|
559
|
-
return [
|
|
560
|
-
AuditLogPatchItem.create({
|
|
561
|
-
key: AuditLogReplacement.key(field.property),
|
|
562
|
-
value: getAutoEncoderValue(value as AutoEncoder) || AuditLogReplacement.key(field.property),
|
|
563
|
-
type: AuditLogPatchItemType.Added,
|
|
564
|
-
}),
|
|
565
|
-
];
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
if (value === null) {
|
|
569
|
-
return [
|
|
570
|
-
AuditLogPatchItem.create({
|
|
571
|
-
key: AuditLogReplacement.key(field.property),
|
|
572
|
-
oldValue: getAutoEncoderValue(oldValue as AutoEncoder) || AuditLogReplacement.key(field.property),
|
|
573
|
-
type: AuditLogPatchItemType.Removed,
|
|
574
|
-
}),
|
|
575
|
-
];
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
return explainPatch(oldValue as AutoEncoder | null, value).map((i) => {
|
|
579
|
-
i.key = i.key.prepend(AuditLogReplacement.key(field.property));
|
|
580
|
-
return i;
|
|
581
|
-
});
|
|
582
|
-
};
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
// Simple addition/delete/change detection
|
|
586
|
-
return (oldValue: unknown, value: unknown) => {
|
|
587
|
-
if (value === undefined) {
|
|
588
|
-
return [];
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
if (oldValue === value) {
|
|
592
|
-
return [];
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
return [
|
|
596
|
-
AuditLogPatchItem.create({
|
|
597
|
-
key: AuditLogReplacement.key(field.property),
|
|
598
|
-
type: AuditLogPatchItemType.Changed,
|
|
599
|
-
}),
|
|
600
|
-
];
|
|
601
|
-
};
|
|
727
|
+
return createUnknownChangeHandler(field.property);
|
|
602
728
|
}
|
|
603
729
|
|
|
604
|
-
export function explainPatch
|
|
730
|
+
export function explainPatch(original: unknown | null, patch: unknown): AuditLogPatchItem[] {
|
|
605
731
|
if (isPatchableArray(patch)) {
|
|
606
732
|
const b = createArrayChangeHandler('items');
|
|
607
733
|
return b(original, patch);
|
|
608
734
|
}
|
|
609
|
-
|
|
735
|
+
|
|
736
|
+
if (original instanceof Map) {
|
|
737
|
+
const b = createMapChangeHandler();
|
|
738
|
+
return b(original, patch);
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
if (typeof patch !== 'object' || patch === null) {
|
|
742
|
+
if (patch === null) {
|
|
743
|
+
// todo
|
|
744
|
+
}
|
|
610
745
|
return [];
|
|
611
746
|
}
|
|
612
|
-
if (original &&
|
|
747
|
+
if (original && typeof original !== 'object') {
|
|
613
748
|
return [];
|
|
614
749
|
}
|
|
615
750
|
|
|
616
751
|
const items: AuditLogPatchItem[] = [];
|
|
617
752
|
|
|
618
753
|
for (const key in patch) {
|
|
619
|
-
const field = original
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
754
|
+
const field = original instanceof AutoEncoder
|
|
755
|
+
? original.static.latestFields.find(f => f.property === key)
|
|
756
|
+
: (
|
|
757
|
+
patch instanceof AutoEncoder
|
|
758
|
+
? patch.static.latestFields.find(f => f.property === key)
|
|
759
|
+
: null
|
|
760
|
+
);
|
|
624
761
|
const oldValue = original?.[key] ?? null;
|
|
625
762
|
const value = patch[key];
|
|
626
763
|
|
|
764
|
+
if (!(patch instanceof AutoEncoder) || !field) {
|
|
765
|
+
// try manual without type information
|
|
766
|
+
items.push(...createUnknownChangeHandler(key)(oldValue, value));
|
|
767
|
+
continue;
|
|
768
|
+
}
|
|
769
|
+
|
|
627
770
|
if (patch.isPut() && key === 'id') {
|
|
628
771
|
continue;
|
|
629
772
|
}
|