@devrev/airsync-sdk 2.0.0-beta.0 → 2.0.0-beta.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/README.md +0 -4
- package/dist/airsync-sdk.d.ts +1802 -0
- package/dist/attachments-streaming/attachments-streaming-pool.d.ts +7 -0
- package/dist/attachments-streaming/attachments-streaming-pool.d.ts.map +1 -1
- package/dist/attachments-streaming/attachments-streaming-pool.js +44 -4
- package/dist/attachments-streaming/attachments-streaming-pool.test.js +259 -10
- package/dist/common/constants.d.ts +16 -5
- package/dist/common/constants.d.ts.map +1 -1
- package/dist/common/constants.js +20 -6
- package/dist/common/control-protocol.d.ts +2 -2
- package/dist/common/control-protocol.d.ts.map +1 -1
- package/dist/common/control-protocol.js +1 -5
- package/dist/common/helpers.d.ts +8 -0
- package/dist/common/helpers.d.ts.map +1 -1
- package/dist/common/helpers.js +15 -0
- package/dist/common/install-initial-domain-mapping.d.ts +2 -2
- package/dist/common/install-initial-domain-mapping.test.js +7 -3
- package/dist/common/test-utils.d.ts +19 -0
- package/dist/common/test-utils.d.ts.map +1 -0
- package/dist/common/test-utils.js +101 -0
- package/dist/common/time-value-resolver.d.ts +47 -0
- package/dist/common/time-value-resolver.d.ts.map +1 -0
- package/dist/common/time-value-resolver.js +141 -0
- package/dist/common/time-value-resolver.test.d.ts +2 -0
- package/dist/common/time-value-resolver.test.d.ts.map +1 -0
- package/dist/common/time-value-resolver.test.js +268 -0
- package/dist/http/axios-client-internal.d.ts.map +1 -1
- package/dist/http/axios-client-internal.js +7 -0
- package/dist/http/axios-client-internal.test.js +27 -6
- package/dist/http/axios-client.d.ts.map +1 -1
- package/dist/http/axios-client.js +6 -3
- package/dist/http/index.d.ts +0 -1
- package/dist/http/index.d.ts.map +1 -1
- package/dist/http/index.js +0 -1
- package/dist/index.d.ts +9 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -10
- package/dist/logger/logger.d.ts +1 -17
- package/dist/logger/logger.d.ts.map +1 -1
- package/dist/logger/logger.interfaces.d.ts +2 -2
- package/dist/logger/logger.js +35 -27
- package/dist/logger/logger.test.js +66 -19
- package/dist/mappers/mappers.interface.d.ts +5 -5
- package/dist/mappers/mappers.interface.js +3 -3
- package/dist/mappers/mappers.test.js +8 -10
- package/dist/mock-server/mock-server.d.ts +47 -0
- package/dist/mock-server/mock-server.d.ts.map +1 -0
- package/dist/mock-server/mock-server.interfaces.d.ts +73 -0
- package/dist/mock-server/mock-server.interfaces.d.ts.map +1 -0
- package/dist/mock-server/mock-server.interfaces.js +4 -0
- package/dist/mock-server/mock-server.js +278 -0
- package/dist/multithreading/create-worker.test.js +36 -17
- package/dist/multithreading/process-task.d.ts.map +1 -1
- package/dist/multithreading/process-task.js +46 -44
- package/dist/multithreading/process-task.test.d.ts +2 -0
- package/dist/multithreading/process-task.test.d.ts.map +1 -0
- package/dist/multithreading/process-task.test.js +146 -0
- package/dist/multithreading/spawn/spawn.d.ts +4 -3
- package/dist/multithreading/spawn/spawn.d.ts.map +1 -1
- package/dist/multithreading/spawn/spawn.helpers.d.ts.map +1 -1
- package/dist/multithreading/spawn/spawn.helpers.js +6 -14
- package/dist/multithreading/spawn/spawn.helpers.test.js +8 -8
- package/dist/multithreading/spawn/spawn.js +41 -31
- package/dist/multithreading/spawn/spawn.test.d.ts +2 -0
- package/dist/multithreading/spawn/spawn.test.d.ts.map +1 -0
- package/dist/multithreading/spawn/spawn.test.js +232 -0
- package/dist/multithreading/worker-adapter/worker-adapter.artifacts.test.js +49 -1
- package/dist/multithreading/worker-adapter/worker-adapter.d.ts +11 -5
- package/dist/multithreading/worker-adapter/worker-adapter.d.ts.map +1 -1
- package/dist/multithreading/worker-adapter/worker-adapter.emit.test.d.ts +2 -0
- package/dist/multithreading/worker-adapter/worker-adapter.emit.test.d.ts.map +1 -0
- package/dist/multithreading/worker-adapter/worker-adapter.emit.test.js +390 -0
- package/dist/multithreading/worker-adapter/worker-adapter.extraction.test.d.ts +2 -0
- package/dist/multithreading/worker-adapter/worker-adapter.extraction.test.d.ts.map +1 -0
- package/dist/multithreading/worker-adapter/worker-adapter.extraction.test.js +799 -0
- package/dist/multithreading/worker-adapter/worker-adapter.js +233 -134
- package/dist/multithreading/worker-adapter/worker-adapter.loading.test.d.ts +2 -0
- package/dist/multithreading/worker-adapter/worker-adapter.loading.test.d.ts.map +1 -0
- package/dist/multithreading/worker-adapter/worker-adapter.loading.test.js +596 -0
- package/dist/multithreading/worker-adapter/worker-adapter.serialization.test.d.ts +2 -0
- package/dist/multithreading/worker-adapter/worker-adapter.serialization.test.d.ts.map +1 -0
- package/dist/multithreading/worker-adapter/worker-adapter.serialization.test.js +71 -0
- package/dist/repo/repo.d.ts.map +1 -1
- package/dist/repo/repo.interfaces.d.ts +4 -2
- package/dist/repo/repo.interfaces.d.ts.map +1 -1
- package/dist/repo/repo.js +4 -3
- package/dist/repo/repo.test.js +60 -7
- package/dist/state/state.d.ts +7 -1
- package/dist/state/state.d.ts.map +1 -1
- package/dist/state/state.extract-window.test.d.ts +2 -0
- package/dist/state/state.extract-window.test.d.ts.map +1 -0
- package/dist/state/state.extract-window.test.js +163 -0
- package/dist/state/state.interfaces.d.ts +25 -8
- package/dist/state/state.interfaces.d.ts.map +1 -1
- package/dist/state/state.interfaces.js +4 -2
- package/dist/state/state.js +111 -12
- package/dist/state/state.pending-boundaries.test.d.ts +2 -0
- package/dist/state/state.pending-boundaries.test.d.ts.map +1 -0
- package/dist/state/state.pending-boundaries.test.js +189 -0
- package/dist/state/state.post-state.test.d.ts +2 -0
- package/dist/state/state.post-state.test.d.ts.map +1 -0
- package/dist/state/state.post-state.test.js +77 -0
- package/dist/state/state.test.js +117 -73
- package/dist/state/state.time-value-resolution.test.d.ts +2 -0
- package/dist/state/state.time-value-resolution.test.d.ts.map +1 -0
- package/dist/state/state.time-value-resolution.test.js +175 -0
- package/dist/tsdoc-metadata.json +11 -0
- package/dist/types/common.d.ts +0 -25
- package/dist/types/common.d.ts.map +1 -1
- package/dist/types/common.js +1 -11
- package/dist/types/external-domain-metadata.d.ts +338 -0
- package/dist/types/external-domain-metadata.d.ts.map +1 -0
- package/dist/types/extraction.d.ts +89 -218
- package/dist/types/extraction.d.ts.map +1 -1
- package/dist/types/extraction.js +56 -124
- package/dist/types/extraction.test.js +69 -43
- package/dist/types/index.d.ts +4 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +6 -3
- package/dist/types/loading.d.ts +4 -24
- package/dist/types/loading.d.ts.map +1 -1
- package/dist/types/loading.js +1 -22
- package/dist/types/workers.d.ts +31 -14
- package/dist/types/workers.d.ts.map +1 -1
- package/dist/types/workers.js +1 -0
- package/dist/uploader/uploader.d.ts +1 -0
- package/dist/uploader/uploader.d.ts.map +1 -1
- package/dist/uploader/uploader.helpers.test.js +0 -11
- package/dist/uploader/uploader.interfaces.d.ts +3 -3
- package/dist/uploader/uploader.js +53 -20
- package/dist/uploader/uploader.test.js +22 -10
- package/package.json +12 -11
- package/dist/common/event-type-translation.d.ts +0 -24
- package/dist/common/event-type-translation.d.ts.map +0 -1
- package/dist/common/event-type-translation.js +0 -117
- package/dist/deprecated/adapter/index.d.ts +0 -62
- package/dist/deprecated/adapter/index.d.ts.map +0 -1
- package/dist/deprecated/adapter/index.js +0 -151
- package/dist/deprecated/common/helpers.d.ts +0 -7
- package/dist/deprecated/common/helpers.d.ts.map +0 -1
- package/dist/deprecated/common/helpers.js +0 -47
- package/dist/deprecated/demo-extractor/external_domain_metadata.json +0 -38
- package/dist/deprecated/demo-extractor/index.d.ts +0 -18
- package/dist/deprecated/demo-extractor/index.d.ts.map +0 -1
- package/dist/deprecated/demo-extractor/index.js +0 -161
- package/dist/deprecated/http/client.d.ts +0 -22
- package/dist/deprecated/http/client.d.ts.map +0 -1
- package/dist/deprecated/http/client.js +0 -161
- package/dist/deprecated/uploader/index.d.ts +0 -35
- package/dist/deprecated/uploader/index.d.ts.map +0 -1
- package/dist/deprecated/uploader/index.js +0 -161
- package/dist/http/types.d.ts +0 -17
- package/dist/http/types.d.ts.map +0 -1
- package/dist/multithreading/worker-adapter/worker-adapter.test.d.ts +0 -2
- package/dist/multithreading/worker-adapter/worker-adapter.test.d.ts.map +0 -1
- package/dist/multithreading/worker-adapter/worker-adapter.test.js +0 -483
- /package/dist/{http/types.js → types/external-domain-metadata.js} +0 -0
package/dist/state/state.js
CHANGED
|
@@ -6,12 +6,15 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.State = void 0;
|
|
7
7
|
exports.createAdapterState = createAdapterState;
|
|
8
8
|
const axios_1 = __importDefault(require("axios"));
|
|
9
|
+
const node_worker_threads_1 = require("node:worker_threads");
|
|
9
10
|
const constants_1 = require("../common/constants");
|
|
10
11
|
const install_initial_domain_mapping_1 = require("../common/install-initial-domain-mapping");
|
|
12
|
+
const time_value_resolver_1 = require("../common/time-value-resolver");
|
|
11
13
|
const axios_client_internal_1 = require("../http/axios-client-internal");
|
|
12
14
|
const logger_1 = require("../logger/logger");
|
|
13
15
|
const common_1 = require("../types/common");
|
|
14
16
|
const extraction_1 = require("../types/extraction");
|
|
17
|
+
const workers_1 = require("../types/workers");
|
|
15
18
|
const state_interfaces_1 = require("./state.interfaces");
|
|
16
19
|
async function createAdapterState({ event, initialState, initialDomainMapping, options, }) {
|
|
17
20
|
// Deep clone the initial state to avoid mutating the original state
|
|
@@ -43,21 +46,88 @@ async function createAdapterState({ event, initialState, initialDomainMapping, o
|
|
|
43
46
|
}
|
|
44
47
|
}
|
|
45
48
|
catch (error) {
|
|
46
|
-
|
|
49
|
+
const errorMessage = `Error while installing initial domain mapping. ${(0, logger_1.serializeError)(error)}`;
|
|
50
|
+
console.error(errorMessage);
|
|
51
|
+
node_worker_threads_1.parentPort === null || node_worker_threads_1.parentPort === void 0 ? void 0 : node_worker_threads_1.parentPort.postMessage({
|
|
52
|
+
subject: workers_1.WorkerMessageSubject.WorkerMessageFailed,
|
|
53
|
+
payload: { message: errorMessage },
|
|
54
|
+
});
|
|
47
55
|
process.exit(1);
|
|
48
56
|
}
|
|
49
57
|
}
|
|
50
|
-
//
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
58
|
+
// Resolve extraction timestamps from TimeValue objects, or reuse pending values from a prior invocation.
|
|
59
|
+
// On StartExtractingMetadata: resolve fresh from TimeValue objects and store in pending state (always overwrite).
|
|
60
|
+
// On all other events: reuse the pending values cached during StartExtractingMetadata.
|
|
61
|
+
const eventContext = event.payload.event_context;
|
|
62
|
+
if (event.payload.event_type === extraction_1.EventType.StartExtractingMetadata) {
|
|
63
|
+
const timeFields = [
|
|
64
|
+
{
|
|
65
|
+
source: 'extraction_start_time',
|
|
66
|
+
target: 'extract_from',
|
|
67
|
+
pending: 'pendingWorkersOldest',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
source: 'extraction_end_time',
|
|
71
|
+
target: 'extract_to',
|
|
72
|
+
pending: 'pendingWorkersNewest',
|
|
73
|
+
},
|
|
74
|
+
];
|
|
75
|
+
for (const { source, target, pending } of timeFields) {
|
|
76
|
+
const timeValue = eventContext[source];
|
|
77
|
+
if (timeValue && timeValue.type) {
|
|
78
|
+
try {
|
|
79
|
+
const resolved = (0, time_value_resolver_1.resolveTimeValue)(timeValue, as.state);
|
|
80
|
+
eventContext[target] = resolved;
|
|
81
|
+
as.state[pending] = resolved;
|
|
82
|
+
console.log(`Resolved ${target} to ${resolved}. Stored in ${pending}.`);
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
const errorMessage = `Failed to resolve ${source}: ${(0, logger_1.serializeError)(error)}`;
|
|
86
|
+
console.error(errorMessage);
|
|
87
|
+
node_worker_threads_1.parentPort === null || node_worker_threads_1.parentPort === void 0 ? void 0 : node_worker_threads_1.parentPort.postMessage({
|
|
88
|
+
subject: workers_1.WorkerMessageSubject.WorkerMessageFailed,
|
|
89
|
+
payload: { message: errorMessage },
|
|
90
|
+
});
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
// Non-StartExtractingMetadata events: reuse pending values from state
|
|
98
|
+
if (as.state.pendingWorkersOldest) {
|
|
99
|
+
eventContext.extract_from = as.state.pendingWorkersOldest;
|
|
100
|
+
console.log(`Reusing pendingWorkersOldest as extract_from: ${as.state.pendingWorkersOldest}.`);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
console.log('pendingWorkersOldest is not set in state. extract_from will not be populated for this invocation.');
|
|
104
|
+
}
|
|
105
|
+
if (as.state.pendingWorkersNewest) {
|
|
106
|
+
eventContext.extract_to = as.state.pendingWorkersNewest;
|
|
107
|
+
console.log(`Reusing pendingWorkersNewest as extract_to: ${as.state.pendingWorkersNewest}.`);
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
console.log('pendingWorkersNewest is not set in state. extract_to will not be populated for this invocation.');
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// Validate that extract_from is before extract_to
|
|
114
|
+
if (eventContext.extract_from && eventContext.extract_to) {
|
|
115
|
+
if (eventContext.extract_from >= eventContext.extract_to) {
|
|
116
|
+
const errorMessage = `Invalid extraction window: extract_from (${eventContext.extract_from}) must be older than extract_to (${eventContext.extract_to}). This indicates an error in the platform.`;
|
|
117
|
+
console.error(errorMessage);
|
|
118
|
+
node_worker_threads_1.parentPort === null || node_worker_threads_1.parentPort === void 0 ? void 0 : node_worker_threads_1.parentPort.postMessage({
|
|
119
|
+
subject: workers_1.WorkerMessageSubject.WorkerMessageFailed,
|
|
120
|
+
payload: { message: errorMessage },
|
|
121
|
+
});
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
55
124
|
}
|
|
56
125
|
}
|
|
57
126
|
return as;
|
|
58
127
|
}
|
|
59
128
|
class State {
|
|
60
129
|
constructor({ event, initialState }) {
|
|
130
|
+
this._extractionScope = {};
|
|
61
131
|
this.initialSdkState =
|
|
62
132
|
event.payload.event_context.mode === common_1.SyncMode.LOADING
|
|
63
133
|
? state_interfaces_1.loadingSdkState
|
|
@@ -74,6 +144,9 @@ class State {
|
|
|
74
144
|
set state(value) {
|
|
75
145
|
this._state = value;
|
|
76
146
|
}
|
|
147
|
+
get extractionScope() {
|
|
148
|
+
return this._extractionScope;
|
|
149
|
+
}
|
|
77
150
|
/**
|
|
78
151
|
* Initializes the state for this adapter instance by fetching from API
|
|
79
152
|
* or creating an initial state if none exists (404).
|
|
@@ -82,7 +155,7 @@ class State {
|
|
|
82
155
|
async init(initialState) {
|
|
83
156
|
var _a;
|
|
84
157
|
try {
|
|
85
|
-
const stringifiedState = await this.fetchState();
|
|
158
|
+
const { state: stringifiedState, objects } = await this.fetchState();
|
|
86
159
|
if (!stringifiedState) {
|
|
87
160
|
throw new Error('No state found in response.');
|
|
88
161
|
}
|
|
@@ -95,6 +168,14 @@ class State {
|
|
|
95
168
|
}
|
|
96
169
|
this.state = parsedState;
|
|
97
170
|
console.log('State fetched successfully. Current state', (0, logger_1.getPrintableState)(this.state));
|
|
171
|
+
if (objects) {
|
|
172
|
+
try {
|
|
173
|
+
this._extractionScope = JSON.parse(objects);
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
console.warn(`Failed to parse extractionScope. ${error}`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
98
179
|
}
|
|
99
180
|
catch (error) {
|
|
100
181
|
if (axios_1.default.isAxiosError(error) && ((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) === 404) {
|
|
@@ -104,7 +185,12 @@ class State {
|
|
|
104
185
|
await this.postState(initialAdapterState);
|
|
105
186
|
}
|
|
106
187
|
else {
|
|
107
|
-
|
|
188
|
+
const errorMessage = `Failed to init state. ${(0, logger_1.serializeError)(error)}`;
|
|
189
|
+
console.error(errorMessage);
|
|
190
|
+
node_worker_threads_1.parentPort === null || node_worker_threads_1.parentPort === void 0 ? void 0 : node_worker_threads_1.parentPort.postMessage({
|
|
191
|
+
subject: workers_1.WorkerMessageSubject.WorkerMessageFailed,
|
|
192
|
+
payload: { message: errorMessage },
|
|
193
|
+
});
|
|
108
194
|
process.exit(1);
|
|
109
195
|
}
|
|
110
196
|
}
|
|
@@ -121,7 +207,12 @@ class State {
|
|
|
121
207
|
stringifiedState = JSON.stringify(this.state);
|
|
122
208
|
}
|
|
123
209
|
catch (error) {
|
|
124
|
-
|
|
210
|
+
const errorMessage = `Failed to stringify state. ${(0, logger_1.serializeError)(error)}`;
|
|
211
|
+
console.error(errorMessage);
|
|
212
|
+
node_worker_threads_1.parentPort === null || node_worker_threads_1.parentPort === void 0 ? void 0 : node_worker_threads_1.parentPort.postMessage({
|
|
213
|
+
subject: workers_1.WorkerMessageSubject.WorkerMessageFailed,
|
|
214
|
+
payload: { message: errorMessage },
|
|
215
|
+
});
|
|
125
216
|
process.exit(1);
|
|
126
217
|
}
|
|
127
218
|
try {
|
|
@@ -139,7 +230,12 @@ class State {
|
|
|
139
230
|
console.log('State updated successfully to', (0, logger_1.getPrintableState)(this.state));
|
|
140
231
|
}
|
|
141
232
|
catch (error) {
|
|
142
|
-
|
|
233
|
+
const errorMessage = `Failed to update the state. ${(0, logger_1.serializeError)(error)}`;
|
|
234
|
+
console.error(errorMessage);
|
|
235
|
+
node_worker_threads_1.parentPort === null || node_worker_threads_1.parentPort === void 0 ? void 0 : node_worker_threads_1.parentPort.postMessage({
|
|
236
|
+
subject: workers_1.WorkerMessageSubject.WorkerMessageFailed,
|
|
237
|
+
payload: { message: errorMessage },
|
|
238
|
+
});
|
|
143
239
|
process.exit(1);
|
|
144
240
|
}
|
|
145
241
|
}
|
|
@@ -148,7 +244,7 @@ class State {
|
|
|
148
244
|
* @return The raw state data from API
|
|
149
245
|
*/
|
|
150
246
|
async fetchState() {
|
|
151
|
-
var _a;
|
|
247
|
+
var _a, _b;
|
|
152
248
|
console.log(`Fetching state with sync unit id ${this.syncUnitId} and request id ${this.requestId}.`);
|
|
153
249
|
const url = this.workerUrl + '.get';
|
|
154
250
|
const response = await axios_client_internal_1.axiosClient.get(url, {
|
|
@@ -160,7 +256,10 @@ class State {
|
|
|
160
256
|
request_id: this.requestId,
|
|
161
257
|
},
|
|
162
258
|
});
|
|
163
|
-
return
|
|
259
|
+
return {
|
|
260
|
+
state: (_a = response.data) === null || _a === void 0 ? void 0 : _a.state,
|
|
261
|
+
objects: (_b = response.data) === null || _b === void 0 ? void 0 : _b.objects,
|
|
262
|
+
};
|
|
164
263
|
}
|
|
165
264
|
}
|
|
166
265
|
exports.State = State;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.pending-boundaries.test.d.ts","sourceRoot":"","sources":["../../src/state/state.pending-boundaries.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const jest_setup_1 = require("../tests/jest.setup");
|
|
4
|
+
const test_utils_1 = require("../common/test-utils");
|
|
5
|
+
const extraction_1 = require("../types/extraction");
|
|
6
|
+
const state_1 = require("./state");
|
|
7
|
+
/* eslint-disable @typescript-eslint/no-require-imports */
|
|
8
|
+
const FIXED_NOW = '2026-03-26T10:00:00.000Z';
|
|
9
|
+
describe('State — pending extraction boundaries', () => {
|
|
10
|
+
let postStateSpy;
|
|
11
|
+
let fetchStateSpy;
|
|
12
|
+
let installInitialDomainMappingSpy;
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
jest.clearAllMocks();
|
|
15
|
+
jest.restoreAllMocks();
|
|
16
|
+
postStateSpy = jest.spyOn(state_1.State.prototype, 'postState');
|
|
17
|
+
fetchStateSpy = jest.spyOn(state_1.State.prototype, 'fetchState');
|
|
18
|
+
installInitialDomainMappingSpy = jest.spyOn(require('../common/install-initial-domain-mapping'), 'installInitialDomainMapping');
|
|
19
|
+
jest.spyOn(process, 'exit').mockImplementation(() => {
|
|
20
|
+
throw new Error('process.exit called');
|
|
21
|
+
});
|
|
22
|
+
jest.useFakeTimers();
|
|
23
|
+
jest.setSystemTime(new Date(FIXED_NOW));
|
|
24
|
+
});
|
|
25
|
+
afterEach(() => {
|
|
26
|
+
jest.useRealTimers();
|
|
27
|
+
});
|
|
28
|
+
it('should store resolved values in pendingWorkersOldest/pendingWorkersNewest on StartExtractingMetadata', async () => {
|
|
29
|
+
// Arrange
|
|
30
|
+
const event = (0, test_utils_1.createMockEvent)(jest_setup_1.mockServer.baseUrl, {
|
|
31
|
+
context: {
|
|
32
|
+
snap_in_version_id: '',
|
|
33
|
+
},
|
|
34
|
+
payload: {
|
|
35
|
+
event_type: extraction_1.EventType.StartExtractingMetadata,
|
|
36
|
+
event_context: {
|
|
37
|
+
extraction_start_time: {
|
|
38
|
+
type: extraction_1.TimeValueType.UNBOUNDED,
|
|
39
|
+
},
|
|
40
|
+
extraction_end_time: {
|
|
41
|
+
type: extraction_1.TimeValueType.CURRENT_TIME,
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
fetchStateSpy.mockRejectedValue({
|
|
47
|
+
isAxiosError: true,
|
|
48
|
+
response: { status: 404 },
|
|
49
|
+
});
|
|
50
|
+
installInitialDomainMappingSpy.mockResolvedValue({ success: true });
|
|
51
|
+
postStateSpy.mockResolvedValue({ success: true });
|
|
52
|
+
// Act
|
|
53
|
+
const state = await (0, state_1.createAdapterState)({
|
|
54
|
+
event,
|
|
55
|
+
initialState: {},
|
|
56
|
+
initialDomainMapping: {},
|
|
57
|
+
});
|
|
58
|
+
// Assert
|
|
59
|
+
expect(state.state.pendingWorkersOldest).toBe('1970-01-01T00:00:00.000Z');
|
|
60
|
+
expect(state.state.pendingWorkersNewest).toBe(FIXED_NOW);
|
|
61
|
+
expect(event.payload.event_context.extract_from).toBe('1970-01-01T00:00:00.000Z');
|
|
62
|
+
expect(event.payload.event_context.extract_to).toBe(FIXED_NOW);
|
|
63
|
+
});
|
|
64
|
+
it('should overwrite pending values on a retry (new StartExtractingMetadata after failure)', async () => {
|
|
65
|
+
// Arrange: state has stale pending values from a previous failed attempt
|
|
66
|
+
const staleOldest = '2026-03-25T08:00:00.000Z';
|
|
67
|
+
const staleNewest = '2026-03-25T09:00:00.000Z';
|
|
68
|
+
const event = (0, test_utils_1.createMockEvent)(jest_setup_1.mockServer.baseUrl, {
|
|
69
|
+
context: {
|
|
70
|
+
snap_in_version_id: 'test_snap_in_version_id',
|
|
71
|
+
},
|
|
72
|
+
payload: {
|
|
73
|
+
event_type: extraction_1.EventType.StartExtractingMetadata,
|
|
74
|
+
event_context: {
|
|
75
|
+
extraction_start_time: {
|
|
76
|
+
type: extraction_1.TimeValueType.UNBOUNDED,
|
|
77
|
+
},
|
|
78
|
+
extraction_end_time: {
|
|
79
|
+
type: extraction_1.TimeValueType.CURRENT_TIME,
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
const stringifiedState = JSON.stringify({
|
|
85
|
+
snapInVersionId: 'test_snap_in_version_id',
|
|
86
|
+
pendingWorkersOldest: staleOldest,
|
|
87
|
+
pendingWorkersNewest: staleNewest,
|
|
88
|
+
});
|
|
89
|
+
fetchStateSpy.mockResolvedValue({ state: stringifiedState });
|
|
90
|
+
// Act
|
|
91
|
+
const state = await (0, state_1.createAdapterState)({
|
|
92
|
+
event,
|
|
93
|
+
initialState: {},
|
|
94
|
+
initialDomainMapping: {},
|
|
95
|
+
});
|
|
96
|
+
// Assert: pending values are overwritten with fresh resolution, not stale values
|
|
97
|
+
expect(state.state.pendingWorkersOldest).toBe('1970-01-01T00:00:00.000Z');
|
|
98
|
+
expect(state.state.pendingWorkersNewest).toBe(FIXED_NOW);
|
|
99
|
+
expect(state.state.pendingWorkersNewest).not.toBe(staleNewest);
|
|
100
|
+
});
|
|
101
|
+
it('should reuse pending values from state on ContinueExtractingData instead of re-resolving', async () => {
|
|
102
|
+
// Arrange: state has pending values from a prior StartExtractingMetadata phase
|
|
103
|
+
const pendingOldest = '1970-01-01T00:00:00.000Z';
|
|
104
|
+
const pendingNewest = '2026-03-26T08:00:00.000Z'; // Earlier than FIXED_NOW
|
|
105
|
+
const event = (0, test_utils_1.createMockEvent)(jest_setup_1.mockServer.baseUrl, {
|
|
106
|
+
context: {
|
|
107
|
+
snap_in_version_id: 'test_snap_in_version_id',
|
|
108
|
+
},
|
|
109
|
+
payload: {
|
|
110
|
+
event_type: extraction_1.EventType.ContinueExtractingData,
|
|
111
|
+
event_context: {
|
|
112
|
+
// Platform still sends TimeValue objects, but they should be ignored
|
|
113
|
+
extraction_start_time: {
|
|
114
|
+
type: extraction_1.TimeValueType.CURRENT_TIME,
|
|
115
|
+
},
|
|
116
|
+
extraction_end_time: {
|
|
117
|
+
type: extraction_1.TimeValueType.CURRENT_TIME,
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
const stringifiedState = JSON.stringify({
|
|
123
|
+
snapInVersionId: 'test_snap_in_version_id',
|
|
124
|
+
pendingWorkersOldest: pendingOldest,
|
|
125
|
+
pendingWorkersNewest: pendingNewest,
|
|
126
|
+
});
|
|
127
|
+
fetchStateSpy.mockResolvedValue({ state: stringifiedState });
|
|
128
|
+
// Act
|
|
129
|
+
const state = await (0, state_1.createAdapterState)({
|
|
130
|
+
event,
|
|
131
|
+
initialState: {},
|
|
132
|
+
initialDomainMapping: {},
|
|
133
|
+
});
|
|
134
|
+
// Assert: uses cached pending values, NOT new Date() resolution
|
|
135
|
+
expect(event.payload.event_context.extract_from).toBe(pendingOldest);
|
|
136
|
+
expect(event.payload.event_context.extract_to).toBe(pendingNewest);
|
|
137
|
+
// Pending values in state remain unchanged
|
|
138
|
+
expect(state.state.pendingWorkersOldest).toBe(pendingOldest);
|
|
139
|
+
expect(state.state.pendingWorkersNewest).toBe(pendingNewest);
|
|
140
|
+
});
|
|
141
|
+
it('should not set extract_from/extract_to on ContinueExtractingData if no pending values exist', async () => {
|
|
142
|
+
// Arrange: state has no pending values (e.g. old state from before this feature)
|
|
143
|
+
const event = (0, test_utils_1.createMockEvent)(jest_setup_1.mockServer.baseUrl, {
|
|
144
|
+
context: {
|
|
145
|
+
snap_in_version_id: 'test_snap_in_version_id',
|
|
146
|
+
},
|
|
147
|
+
payload: { event_type: extraction_1.EventType.ContinueExtractingData },
|
|
148
|
+
});
|
|
149
|
+
const stringifiedState = JSON.stringify({
|
|
150
|
+
snapInVersionId: 'test_snap_in_version_id',
|
|
151
|
+
});
|
|
152
|
+
fetchStateSpy.mockResolvedValue({ state: stringifiedState });
|
|
153
|
+
// Act
|
|
154
|
+
await (0, state_1.createAdapterState)({
|
|
155
|
+
event,
|
|
156
|
+
initialState: {},
|
|
157
|
+
initialDomainMapping: {},
|
|
158
|
+
});
|
|
159
|
+
// Assert: no extraction timestamps are set
|
|
160
|
+
expect(event.payload.event_context.extract_from).toBeUndefined();
|
|
161
|
+
expect(event.payload.event_context.extract_to).toBeUndefined();
|
|
162
|
+
});
|
|
163
|
+
it('should reuse pending values on StartExtractingAttachments', async () => {
|
|
164
|
+
// Arrange: state has pending values from the StartExtractingMetadata phase
|
|
165
|
+
const pendingOldest = '1970-01-01T00:00:00.000Z';
|
|
166
|
+
const pendingNewest = '2026-03-26T08:00:00.000Z';
|
|
167
|
+
const event = (0, test_utils_1.createMockEvent)(jest_setup_1.mockServer.baseUrl, {
|
|
168
|
+
context: {
|
|
169
|
+
snap_in_version_id: 'test_snap_in_version_id',
|
|
170
|
+
},
|
|
171
|
+
payload: { event_type: extraction_1.EventType.StartExtractingAttachments },
|
|
172
|
+
});
|
|
173
|
+
const stringifiedState = JSON.stringify({
|
|
174
|
+
snapInVersionId: 'test_snap_in_version_id',
|
|
175
|
+
pendingWorkersOldest: pendingOldest,
|
|
176
|
+
pendingWorkersNewest: pendingNewest,
|
|
177
|
+
});
|
|
178
|
+
fetchStateSpy.mockResolvedValue({ state: stringifiedState });
|
|
179
|
+
// Act
|
|
180
|
+
await (0, state_1.createAdapterState)({
|
|
181
|
+
event,
|
|
182
|
+
initialState: {},
|
|
183
|
+
initialDomainMapping: {},
|
|
184
|
+
});
|
|
185
|
+
// Assert: pending values are reused
|
|
186
|
+
expect(event.payload.event_context.extract_from).toBe(pendingOldest);
|
|
187
|
+
expect(event.payload.event_context.extract_to).toBe(pendingNewest);
|
|
188
|
+
});
|
|
189
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.post-state.test.d.ts","sourceRoot":"","sources":["../../src/state/state.post-state.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const jest_setup_1 = require("../tests/jest.setup");
|
|
4
|
+
const test_utils_1 = require("../common/test-utils");
|
|
5
|
+
const extraction_1 = require("../types/extraction");
|
|
6
|
+
const state_1 = require("./state");
|
|
7
|
+
/* eslint-disable @typescript-eslint/no-require-imports */
|
|
8
|
+
describe('State.postState', () => {
|
|
9
|
+
let postStateSpy;
|
|
10
|
+
let fetchStateSpy;
|
|
11
|
+
let processExitSpy;
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
jest.clearAllMocks();
|
|
14
|
+
jest.restoreAllMocks();
|
|
15
|
+
postStateSpy = jest.spyOn(state_1.State.prototype, 'postState');
|
|
16
|
+
fetchStateSpy = jest.spyOn(state_1.State.prototype, 'fetchState');
|
|
17
|
+
processExitSpy = jest.spyOn(process, 'exit').mockImplementation(() => {
|
|
18
|
+
throw new Error('process.exit called');
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
it('should POST the stringified state to the update endpoint', async () => {
|
|
22
|
+
// Arrange
|
|
23
|
+
const event = (0, test_utils_1.createMockEvent)(jest_setup_1.mockServer.baseUrl, {
|
|
24
|
+
context: {
|
|
25
|
+
snap_in_version_id: '1.0.0',
|
|
26
|
+
secrets: { service_account_token: 'test_token' },
|
|
27
|
+
},
|
|
28
|
+
payload: { event_type: extraction_1.EventType.StartExtractingData },
|
|
29
|
+
});
|
|
30
|
+
const stateToPost = { snapInVersionId: '1.0.0', foo: 'bar' };
|
|
31
|
+
fetchStateSpy.mockResolvedValue({
|
|
32
|
+
state: JSON.stringify({ snapInVersionId: '1.0.0' }),
|
|
33
|
+
});
|
|
34
|
+
postStateSpy.mockRestore();
|
|
35
|
+
const adapterState = await (0, state_1.createAdapterState)({
|
|
36
|
+
event,
|
|
37
|
+
initialState: {},
|
|
38
|
+
initialDomainMapping: {},
|
|
39
|
+
});
|
|
40
|
+
// Act
|
|
41
|
+
await adapterState.postState(stateToPost);
|
|
42
|
+
// Assert: the mock server records all incoming requests — inspect what was sent
|
|
43
|
+
const requests = jest_setup_1.mockServer.getRequests('POST', '/worker_data_url.update');
|
|
44
|
+
expect(requests).toHaveLength(1);
|
|
45
|
+
const body = requests[0].body;
|
|
46
|
+
// Body must contain the stringified state, preserving the original fields
|
|
47
|
+
expect(typeof body.state).toBe('string');
|
|
48
|
+
const parsed = JSON.parse(body.state);
|
|
49
|
+
expect(parsed.foo).toBe('bar');
|
|
50
|
+
expect(parsed.snapInVersionId).toBe('1.0.0');
|
|
51
|
+
});
|
|
52
|
+
it('should exit(1) when postState HTTP request fails', async () => {
|
|
53
|
+
// Arrange
|
|
54
|
+
const event = (0, test_utils_1.createMockEvent)(jest_setup_1.mockServer.baseUrl, {
|
|
55
|
+
context: { snap_in_version_id: '1.0.0' },
|
|
56
|
+
payload: { event_type: extraction_1.EventType.StartExtractingData },
|
|
57
|
+
});
|
|
58
|
+
fetchStateSpy.mockResolvedValue({
|
|
59
|
+
state: JSON.stringify({ snapInVersionId: '1.0.0' }),
|
|
60
|
+
});
|
|
61
|
+
postStateSpy.mockRestore();
|
|
62
|
+
const adapterState = await (0, state_1.createAdapterState)({
|
|
63
|
+
event,
|
|
64
|
+
initialState: {},
|
|
65
|
+
initialDomainMapping: {},
|
|
66
|
+
});
|
|
67
|
+
// Mock axiosClient.post directly to bypass the retry backoff
|
|
68
|
+
const axiosClientModule = require('../http/axios-client-internal');
|
|
69
|
+
const axiosPostSpy = jest
|
|
70
|
+
.spyOn(axiosClientModule.axiosClient, 'post')
|
|
71
|
+
.mockRejectedValue(new Error('network error'));
|
|
72
|
+
// Act & Assert
|
|
73
|
+
await expect(adapterState.postState()).rejects.toThrow('process.exit called');
|
|
74
|
+
expect(processExitSpy).toHaveBeenCalledWith(1);
|
|
75
|
+
axiosPostSpy.mockRestore();
|
|
76
|
+
});
|
|
77
|
+
});
|