@stamhoofd/backend 2.58.0 → 2.60.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/index.ts +10 -1
- package/package.json +12 -12
- package/src/audit-logs/DocumentTemplateLogger.ts +22 -0
- package/src/audit-logs/EventLogger.ts +30 -0
- package/src/audit-logs/GroupLogger.ts +95 -0
- package/src/audit-logs/MemberLogger.ts +24 -0
- package/src/audit-logs/MemberPlatformMembershipLogger.ts +60 -0
- package/src/audit-logs/MemberResponsibilityRecordLogger.ts +69 -0
- package/src/audit-logs/ModelLogger.ts +219 -0
- package/src/audit-logs/OrderLogger.ts +57 -0
- package/src/audit-logs/OrganizationLogger.ts +16 -0
- package/src/audit-logs/OrganizationRegistrationPeriodLogger.ts +77 -0
- package/src/audit-logs/PaymentLogger.ts +43 -0
- package/src/audit-logs/PlatformLogger.ts +13 -0
- package/src/audit-logs/RegistrationLogger.ts +53 -0
- package/src/audit-logs/RegistrationPeriodLogger.ts +21 -0
- package/src/audit-logs/StripeAccountLogger.ts +47 -0
- package/src/audit-logs/WebshopLogger.ts +35 -0
- package/src/crons/updateSetupSteps.ts +1 -1
- package/src/crons.ts +2 -1
- package/src/endpoints/global/events/PatchEventsEndpoint.ts +12 -24
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +7 -21
- package/src/endpoints/global/payments/StripeWebhookEndpoint.ts +5 -13
- package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +5 -12
- package/src/endpoints/global/registration/PatchUserMembersEndpoint.ts +0 -15
- package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +43 -27
- package/src/endpoints/global/registration-periods/PatchRegistrationPeriodsEndpoint.ts +0 -19
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +3 -13
- package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +20 -43
- package/src/endpoints/organization/dashboard/stripe/ConnectStripeEndpoint.ts +0 -6
- package/src/endpoints/organization/dashboard/stripe/DeleteStripeAccountEndpoint.ts +0 -6
- package/src/endpoints/organization/dashboard/stripe/UpdateStripeAccountEndpoint.ts +5 -14
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.ts +7 -4
- package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +8 -2
- package/src/helpers/AuthenticatedStructures.ts +16 -1
- package/src/helpers/Context.ts +8 -2
- package/src/helpers/MemberUserSyncer.ts +45 -40
- package/src/helpers/PeriodHelper.ts +5 -5
- package/src/helpers/SetupStepUpdater.ts +503 -0
- package/src/helpers/TagHelper.ts +23 -20
- package/src/seeds/1722344162-update-membership.ts +2 -2
- package/src/seeds/1726572303-schedule-stock-updates.ts +2 -1
- package/src/seeds/1726847064-setup-steps.ts +1 -1
- package/src/seeds/1733319079-fill-paying-organization-ids.ts +68 -0
- package/src/services/AuditLogService.ts +81 -296
- package/src/services/BalanceItemPaymentService.ts +1 -1
- package/src/services/BalanceItemService.ts +14 -5
- package/src/services/DocumentService.ts +43 -0
- package/src/services/MemberNumberService.ts +120 -0
- package/src/services/PaymentService.ts +199 -193
- package/src/services/PlatformMembershipService.ts +284 -0
- package/src/services/RegistrationService.ts +78 -27
- package/src/services/diff.ts +512 -0
- package/src/sql-filters/events.ts +13 -1
- package/src/helpers/MembershipHelper.ts +0 -54
- package/src/services/explainPatch.ts +0 -782
|
@@ -1,782 +0,0 @@
|
|
|
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
|
-
import { Formatter } from '@stamhoofd/utility';
|
|
4
|
-
import { get } from 'http';
|
|
5
|
-
|
|
6
|
-
export type PatchExplainer = {
|
|
7
|
-
key: string;
|
|
8
|
-
handler: (oldValue: unknown, value: unknown) => AuditLogPatchItem[];
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
function createStringChangeHandler(key: string) {
|
|
12
|
-
return (oldValue: unknown, value: unknown) => {
|
|
13
|
-
if (oldValue === value) {
|
|
14
|
-
return [];
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
if (value === undefined) {
|
|
18
|
-
// Not altered
|
|
19
|
-
return [];
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return [
|
|
23
|
-
AuditLogPatchItem.create({
|
|
24
|
-
key: getAutoEncoderKey(key),
|
|
25
|
-
oldValue: getAutoEncoderValue(oldValue) || getAutoEncoderName(oldValue) || undefined,
|
|
26
|
-
value: getAutoEncoderValue(value) || getAutoEncoderName(value) || undefined,
|
|
27
|
-
}).autoType(),
|
|
28
|
-
];
|
|
29
|
-
};
|
|
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
|
-
}
|
|
52
|
-
function createIntegerChangeHandler(key: string) {
|
|
53
|
-
return (oldValue: unknown, value: unknown) => {
|
|
54
|
-
if ((typeof oldValue !== 'number' && oldValue !== null) || (typeof value !== 'number' && value !== null)) {
|
|
55
|
-
return [];
|
|
56
|
-
}
|
|
57
|
-
if (oldValue === value) {
|
|
58
|
-
return [];
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const formatter: (typeof Formatter.price | typeof Formatter.integer) = key.toLowerCase().includes('price') ? Formatter.price.bind(Formatter) : Formatter.integer.bind(Formatter);
|
|
62
|
-
return [
|
|
63
|
-
AuditLogPatchItem.create({
|
|
64
|
-
key: getAutoEncoderKey(key),
|
|
65
|
-
oldValue: oldValue !== null ? AuditLogReplacement.string(formatter(oldValue)) : undefined,
|
|
66
|
-
value: value !== null ? AuditLogReplacement.string(formatter(value)) : undefined,
|
|
67
|
-
}).autoType(),
|
|
68
|
-
];
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function createDateChangeHandler(key: string) {
|
|
73
|
-
return (oldValue: unknown, value: unknown) => {
|
|
74
|
-
if (!(oldValue instanceof Date) && oldValue !== null) {
|
|
75
|
-
return [];
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if ((!(value instanceof Date)) && value !== null) {
|
|
79
|
-
return [];
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (oldValue?.getTime() === value?.getTime()) {
|
|
83
|
-
return [];
|
|
84
|
-
}
|
|
85
|
-
let dno = oldValue ? Formatter.dateNumber(oldValue, true) : undefined;
|
|
86
|
-
let dn = value ? Formatter.dateNumber(value, true) : undefined;
|
|
87
|
-
|
|
88
|
-
if (dno && dn && (dno === dn || (Formatter.time(oldValue!) !== Formatter.time(value!)))) {
|
|
89
|
-
// Add time
|
|
90
|
-
dno += ' ' + Formatter.time(oldValue!);
|
|
91
|
-
dn += ' ' + Formatter.time(value!);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return [
|
|
95
|
-
AuditLogPatchItem.create({
|
|
96
|
-
key: getAutoEncoderKey(key),
|
|
97
|
-
oldValue: dno ? AuditLogReplacement.string(dno) : undefined,
|
|
98
|
-
value: dn ? AuditLogReplacement.string(dn) : undefined,
|
|
99
|
-
}).autoType(),
|
|
100
|
-
];
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function createBooleanChangeHandler(key: string) {
|
|
105
|
-
return (oldValue: unknown, value: unknown) => {
|
|
106
|
-
if (typeof oldValue !== 'boolean' && oldValue !== null) {
|
|
107
|
-
return [];
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (typeof value !== 'boolean' && value !== null) {
|
|
111
|
-
return [];
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if (oldValue === value) {
|
|
115
|
-
return [];
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
return [
|
|
119
|
-
AuditLogPatchItem.create({
|
|
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),
|
|
123
|
-
}).autoType(),
|
|
124
|
-
];
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
function getAutoEncoderKey(autoEncoder: string): AuditLogReplacement;
|
|
129
|
-
function getAutoEncoderKey(autoEncoder: unknown): AuditLogReplacement | null;
|
|
130
|
-
function getAutoEncoderKey(autoEncoder: unknown): AuditLogReplacement | null {
|
|
131
|
-
if (typeof autoEncoder === 'string') {
|
|
132
|
-
if (isUuid(autoEncoder)) {
|
|
133
|
-
return AuditLogReplacement.uuid(autoEncoder);
|
|
134
|
-
}
|
|
135
|
-
return AuditLogReplacement.key(autoEncoder);
|
|
136
|
-
}
|
|
137
|
-
return null;
|
|
138
|
-
}
|
|
139
|
-
|
|
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);
|
|
146
|
-
}
|
|
147
|
-
|
|
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
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
if (typeof autoEncoder === 'object' && autoEncoder !== null && 'name' in autoEncoder && typeof autoEncoder.name === 'string') {
|
|
156
|
-
return autoEncoder.name ? AuditLogReplacement.string(autoEncoder.name) : AuditLogReplacement.key('untitled');
|
|
157
|
-
}
|
|
158
|
-
return null;
|
|
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
|
-
}
|
|
172
|
-
|
|
173
|
-
function getAutoEncoderValue(autoEncoder: unknown): AuditLogReplacement | null {
|
|
174
|
-
if (typeof autoEncoder === 'string') {
|
|
175
|
-
if (isUuid(autoEncoder)) {
|
|
176
|
-
return AuditLogReplacement.uuid(autoEncoder);
|
|
177
|
-
}
|
|
178
|
-
return AuditLogReplacement.string(autoEncoder);
|
|
179
|
-
}
|
|
180
|
-
|
|
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');
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
if (typeof autoEncoder === 'number') {
|
|
190
|
-
return AuditLogReplacement.string(Formatter.integer(autoEncoder));
|
|
191
|
-
}
|
|
192
|
-
|
|
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
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
if (autoEncoder instanceof Image) {
|
|
208
|
-
return AuditLogReplacement.create({
|
|
209
|
-
id: autoEncoder.getPathForSize(undefined, undefined),
|
|
210
|
-
value: autoEncoder.source.name ?? undefined,
|
|
211
|
-
type: AuditLogReplacementType.Image,
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
if (autoEncoder instanceof RichText) {
|
|
216
|
-
return AuditLogReplacement.string(autoEncoder.text);
|
|
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
|
-
|
|
238
|
-
return null;
|
|
239
|
-
}
|
|
240
|
-
|
|
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);
|
|
255
|
-
}
|
|
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);
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
if (!original && (v || getAutoEncoderName(put))) {
|
|
290
|
-
// Simplify addition: don't show all added properties
|
|
291
|
-
return items;
|
|
292
|
-
}
|
|
293
|
-
|
|
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
|
-
}
|
|
306
|
-
|
|
307
|
-
function processPatch(key: string, patch: unknown, original: unknown | null): AuditLogPatchItem[] {
|
|
308
|
-
if (!original) {
|
|
309
|
-
// Not supported
|
|
310
|
-
return [];
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
if (patch === original) {
|
|
314
|
-
return [];
|
|
315
|
-
}
|
|
316
|
-
|
|
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
|
-
}
|
|
342
|
-
|
|
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 [];
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
|
|
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 [];
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
const v = getAutoEncoderPutValue(deletedItem);
|
|
383
|
-
|
|
384
|
-
const keySingular = getKeySingular(key);
|
|
385
|
-
const k = AuditLogReplacement.key(keySingular).append(getAutoEncoderName(deletedItem));
|
|
386
|
-
|
|
387
|
-
return [
|
|
388
|
-
AuditLogPatchItem.create({
|
|
389
|
-
key: k,
|
|
390
|
-
type: AuditLogPatchItemType.Removed,
|
|
391
|
-
oldValue: v ?? undefined,
|
|
392
|
-
}),
|
|
393
|
-
];
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
function createArrayChangeHandler(key: string) {
|
|
397
|
-
return (oldValue: unknown, value: unknown) => {
|
|
398
|
-
if (!Array.isArray(oldValue)) {
|
|
399
|
-
// Not supported
|
|
400
|
-
return [];
|
|
401
|
-
}
|
|
402
|
-
const items: AuditLogPatchItem[] = [];
|
|
403
|
-
|
|
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
|
-
}
|
|
419
|
-
|
|
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
|
-
}
|
|
428
|
-
}
|
|
429
|
-
// Not supported
|
|
430
|
-
return items;
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
const createdIdSet = new Set<string>();
|
|
434
|
-
|
|
435
|
-
for (const { put } of value.getPuts()) {
|
|
436
|
-
items.push(...processPut(key, put, findOriginal(put, oldValue), createdIdSet));
|
|
437
|
-
}
|
|
438
|
-
|
|
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));
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
if (value.getMoves().length > 0) {
|
|
448
|
-
items.push(
|
|
449
|
-
AuditLogPatchItem.create({
|
|
450
|
-
key: getAutoEncoderKey(key),
|
|
451
|
-
type: AuditLogPatchItemType.Reordered,
|
|
452
|
-
}),
|
|
453
|
-
);
|
|
454
|
-
}
|
|
455
|
-
return items;
|
|
456
|
-
};
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
function createMapChangeHandler(key?: string) {
|
|
460
|
-
return (oldValue: unknown, value: unknown) => {
|
|
461
|
-
if (!(value instanceof Map)) {
|
|
462
|
-
// Not supported
|
|
463
|
-
return [];
|
|
464
|
-
}
|
|
465
|
-
if (!(oldValue instanceof Map)) {
|
|
466
|
-
// Not supported
|
|
467
|
-
return [];
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
const items: AuditLogPatchItem[] = [];
|
|
471
|
-
const keySingular = key ? key.replace(/ies$/, 'y').replace(/s$/, '') : key;
|
|
472
|
-
const isPatch = isPatchMap(value);
|
|
473
|
-
|
|
474
|
-
for (const [k, v] of value.entries()) {
|
|
475
|
-
if (typeof k !== 'string') {
|
|
476
|
-
// Not supported
|
|
477
|
-
continue;
|
|
478
|
-
}
|
|
479
|
-
let original = oldValue.get(k);
|
|
480
|
-
|
|
481
|
-
if (v instanceof Map && !original) {
|
|
482
|
-
original = new Map();
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
if (v === null && isPatch) {
|
|
486
|
-
// Delete
|
|
487
|
-
if (original) {
|
|
488
|
-
items.push(
|
|
489
|
-
AuditLogPatchItem.create({
|
|
490
|
-
key: AuditLogReplacement.key(keySingular).append(getAutoEncoderKey(k)).append(getAutoEncoderName(original)),
|
|
491
|
-
oldValue: getAutoEncoderPutValue(original) || undefined,
|
|
492
|
-
type: AuditLogPatchItemType.Removed,
|
|
493
|
-
}),
|
|
494
|
-
);
|
|
495
|
-
}
|
|
496
|
-
continue;
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
if (!original) {
|
|
500
|
-
// added
|
|
501
|
-
items.push(
|
|
502
|
-
AuditLogPatchItem.create({
|
|
503
|
-
key: AuditLogReplacement.key(keySingular).append(getAutoEncoderKey(k)).append(getAutoEncoderName(v)),
|
|
504
|
-
value: getAutoEncoderPutValue(v) || undefined,
|
|
505
|
-
type: AuditLogPatchItemType.Added,
|
|
506
|
-
}),
|
|
507
|
-
);
|
|
508
|
-
}
|
|
509
|
-
else {
|
|
510
|
-
let ov = getAutoEncoderValue(original);
|
|
511
|
-
let nv = getAutoEncoderValue(v);
|
|
512
|
-
|
|
513
|
-
const c = explainPatch(
|
|
514
|
-
original,
|
|
515
|
-
v,
|
|
516
|
-
).map((i) => {
|
|
517
|
-
i.key = i.key.prepend(getAutoEncoderName(original) || getAutoEncoderName(v) || getAutoEncoderKey(k));
|
|
518
|
-
i.key = i.key.prepend(AuditLogReplacement.key(keySingular));
|
|
519
|
-
return i;
|
|
520
|
-
});
|
|
521
|
-
|
|
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()) {
|
|
546
|
-
// Manual log
|
|
547
|
-
items.push(
|
|
548
|
-
AuditLogPatchItem.create({
|
|
549
|
-
key: AuditLogReplacement.key(keySingular).append(getAutoEncoderKey(k)).append(getAutoEncoderName(original) || getAutoEncoderName(v)),
|
|
550
|
-
oldValue: getAutoEncoderValue(original) || undefined,
|
|
551
|
-
value: getAutoEncoderValue(v) || undefined,
|
|
552
|
-
type: AuditLogPatchItemType.Changed,
|
|
553
|
-
}),
|
|
554
|
-
);
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
// Changed
|
|
558
|
-
items.push(
|
|
559
|
-
...c,
|
|
560
|
-
);
|
|
561
|
-
}
|
|
562
|
-
}
|
|
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
|
-
|
|
586
|
-
return items;
|
|
587
|
-
};
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
function createUnknownChangeHandler(key: string) {
|
|
591
|
-
return (oldValue: unknown, value: unknown) => {
|
|
592
|
-
if (typeof value !== 'object' && value !== null) {
|
|
593
|
-
return [];
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
if (oldValue === value) {
|
|
597
|
-
return [];
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
if (!oldValue && getAutoEncoderValue(value)) {
|
|
601
|
-
// Simplify addition
|
|
602
|
-
return [
|
|
603
|
-
AuditLogPatchItem.create({
|
|
604
|
-
key: getAutoEncoderKey(key),
|
|
605
|
-
value: getAutoEncoderPutValue(value) || undefined,
|
|
606
|
-
type: AuditLogPatchItemType.Added,
|
|
607
|
-
}),
|
|
608
|
-
];
|
|
609
|
-
}
|
|
610
|
-
|
|
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
|
-
];
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
const items = explainPatch(oldValue, value).map((i) => {
|
|
622
|
-
i.key = i.key.prepend(getAutoEncoderKey(key));
|
|
623
|
-
return i;
|
|
624
|
-
});
|
|
625
|
-
|
|
626
|
-
let v = getAutoEncoderValue(value);
|
|
627
|
-
let ov = getAutoEncoderValue(oldValue);
|
|
628
|
-
|
|
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
|
-
];
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
if (v && ov) {
|
|
641
|
-
// Simplify change
|
|
642
|
-
if (v.toString() === ov.toString()) {
|
|
643
|
-
v = null;
|
|
644
|
-
ov = null;
|
|
645
|
-
|
|
646
|
-
if (items.length === 0) {
|
|
647
|
-
// Probably no change
|
|
648
|
-
return [];
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
return [
|
|
653
|
-
AuditLogPatchItem.create({
|
|
654
|
-
key: getAutoEncoderKey(key),
|
|
655
|
-
value: v || undefined,
|
|
656
|
-
oldValue: ov || undefined,
|
|
657
|
-
type: AuditLogPatchItemType.Changed,
|
|
658
|
-
}),
|
|
659
|
-
];
|
|
660
|
-
}
|
|
661
|
-
return items;
|
|
662
|
-
};
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
function getExplainerForField(field: Field<any>) {
|
|
666
|
-
if (field.decoder === StringDecoder) {
|
|
667
|
-
return createStringChangeHandler(field.property);
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
if (field.decoder instanceof EnumDecoder) {
|
|
671
|
-
return createEnumChangeHandler(field.property);
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
if (field.decoder instanceof SymbolDecoder) {
|
|
675
|
-
if (field.decoder.decoder === StringDecoder) {
|
|
676
|
-
return createStringChangeHandler(field.property);
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
if (field.decoder.decoder instanceof EnumDecoder) {
|
|
680
|
-
return createEnumChangeHandler(field.property);
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
if (field.decoder === DateDecoder) {
|
|
685
|
-
return createDateChangeHandler(field.property);
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
if (field.decoder === BooleanDecoder) {
|
|
689
|
-
return createBooleanChangeHandler(field.property);
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
if (field.decoder === IntegerDecoder) {
|
|
693
|
-
return createIntegerChangeHandler(field.property);
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
if (field.decoder instanceof ArrayDecoder) {
|
|
697
|
-
return createArrayChangeHandler(field.property);
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
if (field.decoder instanceof MapDecoder) {
|
|
701
|
-
return createMapChangeHandler(field.property);
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
if (field.decoder === BooleanStatus) {
|
|
705
|
-
return (oldValue: unknown, value: unknown) => {
|
|
706
|
-
if (value === undefined) {
|
|
707
|
-
return [];
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
const wasTrueOld = oldValue instanceof BooleanStatus ? oldValue.value : null;
|
|
711
|
-
const isTrue = value instanceof BooleanStatus ? value.value : null;
|
|
712
|
-
|
|
713
|
-
if (wasTrueOld === isTrue) {
|
|
714
|
-
return [];
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
return [
|
|
718
|
-
AuditLogPatchItem.create({
|
|
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(),
|
|
723
|
-
];
|
|
724
|
-
};
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
return createUnknownChangeHandler(field.property);
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
export function explainPatch(original: unknown | null, patch: unknown): AuditLogPatchItem[] {
|
|
731
|
-
if (isPatchableArray(patch)) {
|
|
732
|
-
const b = createArrayChangeHandler('items');
|
|
733
|
-
return b(original, patch);
|
|
734
|
-
}
|
|
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
|
-
}
|
|
745
|
-
return [];
|
|
746
|
-
}
|
|
747
|
-
if (original && typeof original !== 'object') {
|
|
748
|
-
return [];
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
const items: AuditLogPatchItem[] = [];
|
|
752
|
-
|
|
753
|
-
for (const key in patch) {
|
|
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
|
-
);
|
|
761
|
-
const oldValue = original?.[key] ?? null;
|
|
762
|
-
const value = patch[key];
|
|
763
|
-
|
|
764
|
-
if (!(patch instanceof AutoEncoder) || !field) {
|
|
765
|
-
// try manual without type information
|
|
766
|
-
items.push(...createUnknownChangeHandler(key)(oldValue, value));
|
|
767
|
-
continue;
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
if (patch.isPut() && key === 'id') {
|
|
771
|
-
continue;
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
const handler = getExplainerForField(field);
|
|
775
|
-
if (!handler) {
|
|
776
|
-
continue;
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
items.push(...handler(oldValue, value));
|
|
780
|
-
}
|
|
781
|
-
return items;
|
|
782
|
-
}
|