@echovisionlab/geul-telemetry 0.1.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/LICENSE.md +6 -0
- package/README.md +38 -0
- package/fixtures/audit-records.json +316 -0
- package/fixtures/collaboration-checkpoint-entity-types.json +15 -0
- package/fixtures/domain-audit-actions.json +132 -0
- package/fixtures/domain-audit-wire-parity.json +1751 -0
- package/fixtures/http-request-results.json +21 -0
- package/fixtures/job-catalog.json +23 -0
- package/fixtures/locale-content-audit.json +126 -0
- package/fixtures/log-redaction.json +72 -0
- package/fixtures/request-record.json +14 -0
- package/fixtures/security-access-records.json +61 -0
- package/fixtures/service-identities.json +11 -0
- package/fixtures/system-catalog.json +28 -0
- package/fixtures/translation-job-terminal-catalog.json +45 -0
- package/fixtures/translation-settings-fields.json +1 -0
- package/package.json +61 -0
- package/policy/log-redaction.json +113 -0
- package/src/actor.ts +81 -0
- package/src/audit/account.ts +206 -0
- package/src/audit/actor-policy.ts +63 -0
- package/src/audit/artist-label.ts +312 -0
- package/src/audit/attributes.ts +107 -0
- package/src/audit/builder.ts +25 -0
- package/src/audit/catalog.ts +116 -0
- package/src/audit/content.ts +723 -0
- package/src/audit/email-authoring.ts +220 -0
- package/src/audit/file.ts +168 -0
- package/src/audit/form.ts +229 -0
- package/src/audit/integrations.ts +207 -0
- package/src/audit/locale-code.ts +6 -0
- package/src/audit/locale-content.ts +358 -0
- package/src/audit/member-account.ts +313 -0
- package/src/audit/member.ts +171 -0
- package/src/audit/program.ts +339 -0
- package/src/audit/reference-data.ts +230 -0
- package/src/audit/reference-entities.ts +332 -0
- package/src/audit/relation-download-policy.ts +292 -0
- package/src/audit/release-campaign.ts +422 -0
- package/src/audit/series-type.ts +298 -0
- package/src/audit/settings.ts +306 -0
- package/src/audit/source-locale.ts +337 -0
- package/src/audit/types.ts +9 -0
- package/src/audit.ts +21 -0
- package/src/authentication.ts +183 -0
- package/src/error-metadata.ts +11 -0
- package/src/index.ts +11 -0
- package/src/job.ts +56 -0
- package/src/log-attribute-policy.ts +21 -0
- package/src/otel.ts +64 -0
- package/src/records.ts +1244 -0
- package/src/redaction.ts +35 -0
- package/src/request-context.ts +73 -0
- package/src/request-id.ts +13 -0
- package/src/request-result.ts +73 -0
- package/src/request.ts +60 -0
- package/src/system.ts +355 -0
- package/src/trace.ts +13 -0
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AuditCollectionOperation,
|
|
3
|
+
AuditItemOperation,
|
|
4
|
+
AuditRecord,
|
|
5
|
+
AuditState,
|
|
6
|
+
} from "../records.ts";
|
|
7
|
+
import {
|
|
8
|
+
assertOnlyAuditAttributes,
|
|
9
|
+
canonicalAuditValues,
|
|
10
|
+
} from "./attributes.ts";
|
|
11
|
+
import { buildAuditRecord } from "./builder.ts";
|
|
12
|
+
import {
|
|
13
|
+
AUDIT_ACTIONS,
|
|
14
|
+
AUDIT_CATALOG,
|
|
15
|
+
requireCatalogTarget,
|
|
16
|
+
} from "./catalog.ts";
|
|
17
|
+
import {
|
|
18
|
+
isRelationDownloadPolicyAuditRecord,
|
|
19
|
+
validateRelationDownloadPolicyAuditRecord,
|
|
20
|
+
} from "./relation-download-policy.ts";
|
|
21
|
+
import type { AuditMetadata } from "./types.ts";
|
|
22
|
+
|
|
23
|
+
type ReleaseCampaignAction = Extract<
|
|
24
|
+
(typeof AUDIT_ACTIONS)[number],
|
|
25
|
+
| "release.created"
|
|
26
|
+
| "release.updated"
|
|
27
|
+
| "release.deleted"
|
|
28
|
+
| "campaign.created"
|
|
29
|
+
| "campaign.updated"
|
|
30
|
+
| "campaign.deleted"
|
|
31
|
+
>;
|
|
32
|
+
export type ReleaseMetadataField =
|
|
33
|
+
| "artists"
|
|
34
|
+
| "categories"
|
|
35
|
+
| "credits"
|
|
36
|
+
| "date"
|
|
37
|
+
| "formats"
|
|
38
|
+
| "genres"
|
|
39
|
+
| "labels"
|
|
40
|
+
| "links"
|
|
41
|
+
| "slug"
|
|
42
|
+
| "styles"
|
|
43
|
+
| "type";
|
|
44
|
+
export type CampaignTargetLayoutField =
|
|
45
|
+
"layout" | "recipient_scope" | "segment" | "target_mode";
|
|
46
|
+
export type CampaignMetadataField = CampaignTargetLayoutField | "name";
|
|
47
|
+
type ReleaseLifecycleState = Extract<AuditState, "draft" | "published">;
|
|
48
|
+
|
|
49
|
+
const actions = new Set<ReleaseCampaignAction>([
|
|
50
|
+
"release.created",
|
|
51
|
+
"release.updated",
|
|
52
|
+
"release.deleted",
|
|
53
|
+
"campaign.created",
|
|
54
|
+
"campaign.updated",
|
|
55
|
+
"campaign.deleted",
|
|
56
|
+
]);
|
|
57
|
+
const releaseMetadataFields = new Set<ReleaseMetadataField>([
|
|
58
|
+
"artists",
|
|
59
|
+
"categories",
|
|
60
|
+
"credits",
|
|
61
|
+
"date",
|
|
62
|
+
"formats",
|
|
63
|
+
"genres",
|
|
64
|
+
"labels",
|
|
65
|
+
"links",
|
|
66
|
+
"slug",
|
|
67
|
+
"styles",
|
|
68
|
+
"type",
|
|
69
|
+
]);
|
|
70
|
+
const campaignFields = new Set<
|
|
71
|
+
CampaignMetadataField | "delivery_run" | "schedule" | "status"
|
|
72
|
+
>([
|
|
73
|
+
"layout",
|
|
74
|
+
"name",
|
|
75
|
+
"recipient_scope",
|
|
76
|
+
"segment",
|
|
77
|
+
"target_mode",
|
|
78
|
+
"delivery_run",
|
|
79
|
+
"schedule",
|
|
80
|
+
"status",
|
|
81
|
+
]);
|
|
82
|
+
function buildReleaseCampaignAuditRecord(
|
|
83
|
+
metadata: AuditMetadata,
|
|
84
|
+
action: ReleaseCampaignAction,
|
|
85
|
+
targetId: string,
|
|
86
|
+
attributes: Parameters<typeof buildAuditRecord>[2] = {},
|
|
87
|
+
): AuditRecord {
|
|
88
|
+
return buildAuditRecord(
|
|
89
|
+
metadata,
|
|
90
|
+
{ action, target_type: AUDIT_CATALOG[action], target_id: targetId },
|
|
91
|
+
attributes,
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
function requireNoExtra(record: AuditRecord, allowed: readonly string[]): void {
|
|
95
|
+
assertOnlyAuditAttributes(record, allowed);
|
|
96
|
+
}
|
|
97
|
+
function requireIdentifier(name: string, value: string | undefined): void {
|
|
98
|
+
if (!value || value.length > 255 || value.trim() !== value)
|
|
99
|
+
throw new TypeError(`${name} requires an identifier`);
|
|
100
|
+
}
|
|
101
|
+
function requireFields(
|
|
102
|
+
record: AuditRecord,
|
|
103
|
+
allowed: ReadonlySet<string>,
|
|
104
|
+
): void {
|
|
105
|
+
const fields = record.changed_fields;
|
|
106
|
+
if (
|
|
107
|
+
!fields?.length ||
|
|
108
|
+
fields.some(
|
|
109
|
+
(field, i) => !allowed.has(field) || (i > 0 && fields[i - 1] >= field),
|
|
110
|
+
)
|
|
111
|
+
)
|
|
112
|
+
throw new TypeError("invalid or non-canonical changed_fields");
|
|
113
|
+
}
|
|
114
|
+
function requireChild(record: AuditRecord): void {
|
|
115
|
+
if (record.item_ids !== undefined) {
|
|
116
|
+
for (const itemId of record.item_ids) requireIdentifier("item_ids", itemId);
|
|
117
|
+
return requireNoExtra(record, ["changed_fields", "item_ids"]);
|
|
118
|
+
}
|
|
119
|
+
if (
|
|
120
|
+
!record.item_id ||
|
|
121
|
+
!["created", "updated", "deleted"].includes(record.item_operation ?? "")
|
|
122
|
+
)
|
|
123
|
+
throw new TypeError("child requires operation and item");
|
|
124
|
+
requireIdentifier("item_id", record.item_id);
|
|
125
|
+
requireNoExtra(record, ["changed_fields", "item_operation", "item_id"]);
|
|
126
|
+
}
|
|
127
|
+
function requireFileBinding(record: AuditRecord, field: string): void {
|
|
128
|
+
if (
|
|
129
|
+
record.collection_operation !== "added" &&
|
|
130
|
+
record.collection_operation !== "removed"
|
|
131
|
+
)
|
|
132
|
+
throw new TypeError(`${field} requires collection operation`);
|
|
133
|
+
requireIdentifier("file_id", record.file_id);
|
|
134
|
+
requireNoExtra(record, ["changed_fields", "collection_operation", "file_id"]);
|
|
135
|
+
}
|
|
136
|
+
function requireReleaseLifecycle(record: AuditRecord): void {
|
|
137
|
+
if (!(
|
|
138
|
+
(record.previous_state === "draft" && record.new_state === "published") ||
|
|
139
|
+
(record.previous_state === "published" && record.new_state === "draft")
|
|
140
|
+
))
|
|
141
|
+
throw new TypeError(
|
|
142
|
+
"release lifecycle requires draft/published transition",
|
|
143
|
+
);
|
|
144
|
+
requireNoExtra(record, ["changed_fields", "previous_state", "new_state"]);
|
|
145
|
+
}
|
|
146
|
+
function requireShareLink(record: AuditRecord): void {
|
|
147
|
+
if (
|
|
148
|
+
record.item_operation !== "created" &&
|
|
149
|
+
record.item_operation !== "deleted"
|
|
150
|
+
)
|
|
151
|
+
throw new TypeError(
|
|
152
|
+
"share link requires created or deleted item operation",
|
|
153
|
+
);
|
|
154
|
+
requireIdentifier("item_id", record.item_id);
|
|
155
|
+
requireNoExtra(record, ["changed_fields", "item_operation", "item_id"]);
|
|
156
|
+
}
|
|
157
|
+
function validateReleaseUpdate(record: AuditRecord): void {
|
|
158
|
+
if (isRelationDownloadPolicyAuditRecord(record))
|
|
159
|
+
return validateRelationDownloadPolicyAuditRecord(record);
|
|
160
|
+
if (record.changed_fields?.length !== 1) {
|
|
161
|
+
requireFields(record, releaseMetadataFields);
|
|
162
|
+
return requireNoExtra(record, ["changed_fields"]);
|
|
163
|
+
}
|
|
164
|
+
switch (record.changed_fields[0]) {
|
|
165
|
+
case "tracks":
|
|
166
|
+
return requireChild(record);
|
|
167
|
+
case "track_audio":
|
|
168
|
+
if (
|
|
169
|
+
record.collection_operation !== "added" &&
|
|
170
|
+
record.collection_operation !== "removed"
|
|
171
|
+
)
|
|
172
|
+
throw new TypeError("track audio requires binding operation");
|
|
173
|
+
requireIdentifier("item_id", record.item_id);
|
|
174
|
+
requireIdentifier("file_id", record.file_id);
|
|
175
|
+
return requireNoExtra(record, [
|
|
176
|
+
"changed_fields",
|
|
177
|
+
"collection_operation",
|
|
178
|
+
"item_id",
|
|
179
|
+
"file_id",
|
|
180
|
+
]);
|
|
181
|
+
case "artwork":
|
|
182
|
+
return requireFileBinding(record, "artwork");
|
|
183
|
+
case "status":
|
|
184
|
+
return requireReleaseLifecycle(record);
|
|
185
|
+
case "share_links":
|
|
186
|
+
return requireShareLink(record);
|
|
187
|
+
default:
|
|
188
|
+
requireFields(record, releaseMetadataFields);
|
|
189
|
+
return requireNoExtra(record, ["changed_fields"]);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
function validateCampaignUpdate(record: AuditRecord): void {
|
|
193
|
+
requireFields(record, campaignFields);
|
|
194
|
+
if (record.actor_kind === "system") {
|
|
195
|
+
if (
|
|
196
|
+
record.actor_service !== "geul-backend" ||
|
|
197
|
+
record.changed_fields?.length !== 1 ||
|
|
198
|
+
record.changed_fields[0] !== "status"
|
|
199
|
+
)
|
|
200
|
+
throw new TypeError(
|
|
201
|
+
"campaign system actor is limited to terminal status",
|
|
202
|
+
);
|
|
203
|
+
if (record.new_state !== "sent" && record.new_state !== "failed")
|
|
204
|
+
throw new TypeError("campaign system status must be terminal");
|
|
205
|
+
}
|
|
206
|
+
const lifecycle = record.changed_fields!.some((field) =>
|
|
207
|
+
["delivery_run", "schedule", "status"].includes(field),
|
|
208
|
+
);
|
|
209
|
+
if (!lifecycle) return requireNoExtra(record, ["changed_fields"]);
|
|
210
|
+
if (
|
|
211
|
+
record.previous_state === record.new_state ||
|
|
212
|
+
!record.previous_state ||
|
|
213
|
+
!record.new_state
|
|
214
|
+
)
|
|
215
|
+
throw new TypeError("campaign lifecycle requires transition");
|
|
216
|
+
const scheduled = record.changed_fields!.includes("schedule");
|
|
217
|
+
if (scheduled) {
|
|
218
|
+
if (!record.scheduled_at || Number.isNaN(Date.parse(record.scheduled_at)))
|
|
219
|
+
throw new TypeError("campaign schedule requires scheduled_at");
|
|
220
|
+
if (record.scheduled_time_zone !== undefined)
|
|
221
|
+
throw new TypeError("campaign schedule does not store a time zone");
|
|
222
|
+
} else if (
|
|
223
|
+
record.scheduled_at !== undefined ||
|
|
224
|
+
record.scheduled_time_zone !== undefined
|
|
225
|
+
) {
|
|
226
|
+
throw new TypeError(
|
|
227
|
+
"campaign schedule attributes require changed_fields schedule",
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
if (record.changed_fields!.includes("delivery_run"))
|
|
231
|
+
requireIdentifier("item_id", record.item_id);
|
|
232
|
+
else if (record.item_id !== undefined)
|
|
233
|
+
throw new TypeError(
|
|
234
|
+
"campaign item_id requires changed_fields delivery_run",
|
|
235
|
+
);
|
|
236
|
+
requireNoExtra(record, [
|
|
237
|
+
"changed_fields",
|
|
238
|
+
"previous_state",
|
|
239
|
+
"new_state",
|
|
240
|
+
"scheduled_at",
|
|
241
|
+
"item_id",
|
|
242
|
+
]);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/** Returns true only for Release and Campaign catalog actions. */
|
|
246
|
+
export function validateReleaseCampaignAuditRecord(
|
|
247
|
+
record: AuditRecord,
|
|
248
|
+
): boolean {
|
|
249
|
+
if (!actions.has(record.action as ReleaseCampaignAction)) return false;
|
|
250
|
+
requireCatalogTarget(record.action, record.target_type, record.target_id);
|
|
251
|
+
if (!record.action.endsWith(".updated")) {
|
|
252
|
+
requireNoExtra(record, []);
|
|
253
|
+
return true;
|
|
254
|
+
}
|
|
255
|
+
if (record.action === "release.updated") validateReleaseUpdate(record);
|
|
256
|
+
else validateCampaignUpdate(record);
|
|
257
|
+
return true;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export function buildReleaseCreatedAuditRecord(
|
|
261
|
+
metadata: AuditMetadata,
|
|
262
|
+
id: string,
|
|
263
|
+
): AuditRecord {
|
|
264
|
+
return buildReleaseCampaignAuditRecord(metadata, "release.created", id);
|
|
265
|
+
}
|
|
266
|
+
export function buildReleaseDeletedAuditRecord(
|
|
267
|
+
metadata: AuditMetadata,
|
|
268
|
+
id: string,
|
|
269
|
+
): AuditRecord {
|
|
270
|
+
return buildReleaseCampaignAuditRecord(metadata, "release.deleted", id);
|
|
271
|
+
}
|
|
272
|
+
export function buildReleaseMetadataAuditRecord(
|
|
273
|
+
metadata: AuditMetadata,
|
|
274
|
+
id: string,
|
|
275
|
+
fields: readonly ReleaseMetadataField[],
|
|
276
|
+
): AuditRecord {
|
|
277
|
+
return buildReleaseCampaignAuditRecord(metadata, "release.updated", id, {
|
|
278
|
+
changed_fields: canonicalAuditValues(fields),
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
export function buildReleaseTrackAuditRecord(
|
|
282
|
+
metadata: AuditMetadata,
|
|
283
|
+
id: string,
|
|
284
|
+
trackId: string,
|
|
285
|
+
itemOperation: AuditItemOperation,
|
|
286
|
+
): AuditRecord {
|
|
287
|
+
return buildReleaseCampaignAuditRecord(metadata, "release.updated", id, {
|
|
288
|
+
changed_fields: ["tracks"],
|
|
289
|
+
item_id: trackId,
|
|
290
|
+
item_operation: itemOperation,
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
export function buildReleaseTrackOrderAuditRecord(
|
|
294
|
+
metadata: AuditMetadata,
|
|
295
|
+
id: string,
|
|
296
|
+
trackIds: readonly string[],
|
|
297
|
+
): AuditRecord {
|
|
298
|
+
return buildReleaseCampaignAuditRecord(metadata, "release.updated", id, {
|
|
299
|
+
changed_fields: ["tracks"],
|
|
300
|
+
item_ids: [...trackIds],
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
export function buildReleaseTrackAudioAuditRecord(
|
|
304
|
+
metadata: AuditMetadata,
|
|
305
|
+
id: string,
|
|
306
|
+
trackId: string,
|
|
307
|
+
fileId: string,
|
|
308
|
+
collectionOperation: AuditCollectionOperation,
|
|
309
|
+
): AuditRecord {
|
|
310
|
+
return buildReleaseCampaignAuditRecord(metadata, "release.updated", id, {
|
|
311
|
+
changed_fields: ["track_audio"],
|
|
312
|
+
item_id: trackId,
|
|
313
|
+
file_id: fileId,
|
|
314
|
+
collection_operation: collectionOperation,
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
export function buildReleaseArtworkAuditRecord(
|
|
318
|
+
metadata: AuditMetadata,
|
|
319
|
+
id: string,
|
|
320
|
+
fileId: string,
|
|
321
|
+
collectionOperation: AuditCollectionOperation,
|
|
322
|
+
): AuditRecord {
|
|
323
|
+
return buildReleaseCampaignAuditRecord(metadata, "release.updated", id, {
|
|
324
|
+
changed_fields: ["artwork"],
|
|
325
|
+
file_id: fileId,
|
|
326
|
+
collection_operation: collectionOperation,
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
export function buildReleaseLifecycleAuditRecord(
|
|
330
|
+
metadata: AuditMetadata,
|
|
331
|
+
id: string,
|
|
332
|
+
previousState: ReleaseLifecycleState,
|
|
333
|
+
newState: ReleaseLifecycleState,
|
|
334
|
+
): AuditRecord {
|
|
335
|
+
return buildReleaseCampaignAuditRecord(metadata, "release.updated", id, {
|
|
336
|
+
changed_fields: ["status"],
|
|
337
|
+
previous_state: previousState,
|
|
338
|
+
new_state: newState,
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
export function buildReleaseShareLinkAuditRecord(
|
|
342
|
+
metadata: AuditMetadata,
|
|
343
|
+
id: string,
|
|
344
|
+
shareLinkId: string,
|
|
345
|
+
itemOperation: Extract<AuditItemOperation, "created" | "deleted">,
|
|
346
|
+
): AuditRecord {
|
|
347
|
+
return buildReleaseCampaignAuditRecord(metadata, "release.updated", id, {
|
|
348
|
+
changed_fields: ["share_links"],
|
|
349
|
+
item_id: shareLinkId,
|
|
350
|
+
item_operation: itemOperation,
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
export function buildCampaignCreatedAuditRecord(
|
|
354
|
+
metadata: AuditMetadata,
|
|
355
|
+
id: string,
|
|
356
|
+
): AuditRecord {
|
|
357
|
+
return buildReleaseCampaignAuditRecord(metadata, "campaign.created", id);
|
|
358
|
+
}
|
|
359
|
+
export function buildCampaignDeletedAuditRecord(
|
|
360
|
+
metadata: AuditMetadata,
|
|
361
|
+
id: string,
|
|
362
|
+
): AuditRecord {
|
|
363
|
+
return buildReleaseCampaignAuditRecord(metadata, "campaign.deleted", id);
|
|
364
|
+
}
|
|
365
|
+
export function buildCampaignTargetLayoutAuditRecord(
|
|
366
|
+
metadata: AuditMetadata,
|
|
367
|
+
id: string,
|
|
368
|
+
fields: readonly CampaignTargetLayoutField[],
|
|
369
|
+
): AuditRecord {
|
|
370
|
+
return buildReleaseCampaignAuditRecord(metadata, "campaign.updated", id, {
|
|
371
|
+
changed_fields: canonicalAuditValues(fields),
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
export function buildCampaignMetadataAuditRecord(
|
|
375
|
+
metadata: AuditMetadata,
|
|
376
|
+
id: string,
|
|
377
|
+
fields: readonly CampaignMetadataField[],
|
|
378
|
+
): AuditRecord {
|
|
379
|
+
return buildReleaseCampaignAuditRecord(metadata, "campaign.updated", id, {
|
|
380
|
+
changed_fields: canonicalAuditValues(fields),
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
export function buildCampaignStatusLifecycleAuditRecord(
|
|
384
|
+
metadata: AuditMetadata,
|
|
385
|
+
id: string,
|
|
386
|
+
previousState: AuditState,
|
|
387
|
+
newState: AuditState,
|
|
388
|
+
): AuditRecord {
|
|
389
|
+
return buildReleaseCampaignAuditRecord(metadata, "campaign.updated", id, {
|
|
390
|
+
changed_fields: ["status"],
|
|
391
|
+
previous_state: previousState,
|
|
392
|
+
new_state: newState,
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
export function buildCampaignScheduleLifecycleAuditRecord(
|
|
396
|
+
metadata: AuditMetadata,
|
|
397
|
+
id: string,
|
|
398
|
+
previousState: AuditState,
|
|
399
|
+
newState: AuditState,
|
|
400
|
+
scheduledAt: string,
|
|
401
|
+
): AuditRecord {
|
|
402
|
+
return buildReleaseCampaignAuditRecord(metadata, "campaign.updated", id, {
|
|
403
|
+
changed_fields: ["schedule"],
|
|
404
|
+
previous_state: previousState,
|
|
405
|
+
new_state: newState,
|
|
406
|
+
scheduled_at: scheduledAt,
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
export function buildCampaignDeliveryRunLifecycleAuditRecord(
|
|
410
|
+
metadata: AuditMetadata,
|
|
411
|
+
id: string,
|
|
412
|
+
previousState: AuditState,
|
|
413
|
+
newState: AuditState,
|
|
414
|
+
runId: string,
|
|
415
|
+
): AuditRecord {
|
|
416
|
+
return buildReleaseCampaignAuditRecord(metadata, "campaign.updated", id, {
|
|
417
|
+
changed_fields: ["delivery_run"],
|
|
418
|
+
previous_state: previousState,
|
|
419
|
+
new_state: newState,
|
|
420
|
+
item_id: runId,
|
|
421
|
+
});
|
|
422
|
+
}
|