@devrev/airsync-sdk 2.0.0-beta.1 → 2.0.0-beta.3
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 +2059 -0
- package/dist/attachments-streaming/attachments-streaming-pool.d.ts +2 -1
- package/dist/attachments-streaming/attachments-streaming-pool.d.ts.map +1 -1
- package/dist/attachments-streaming/attachments-streaming-pool.interfaces.d.ts +8 -2
- package/dist/attachments-streaming/attachments-streaming-pool.interfaces.d.ts.map +1 -1
- package/dist/attachments-streaming/attachments-streaming-pool.js +27 -16
- package/dist/attachments-streaming/attachments-streaming-pool.test.js +246 -27
- 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 +5 -2
- 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 +21 -0
- 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 +12 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -13
- 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/adapters/base-adapter.d.ts +56 -0
- package/dist/multithreading/adapters/base-adapter.d.ts.map +1 -0
- package/dist/multithreading/adapters/base-adapter.emit.test.d.ts +2 -0
- package/dist/multithreading/adapters/base-adapter.emit.test.d.ts.map +1 -0
- package/dist/multithreading/adapters/base-adapter.emit.test.js +403 -0
- package/dist/multithreading/adapters/base-adapter.js +105 -0
- package/dist/multithreading/adapters/extraction-adapter.artifacts.test.d.ts +2 -0
- package/dist/multithreading/adapters/extraction-adapter.artifacts.test.d.ts.map +1 -0
- package/dist/multithreading/{worker-adapter/worker-adapter.artifacts.test.js → adapters/extraction-adapter.artifacts.test.js} +53 -5
- package/dist/multithreading/adapters/extraction-adapter.d.ts +58 -0
- package/dist/multithreading/adapters/extraction-adapter.d.ts.map +1 -0
- package/dist/multithreading/adapters/extraction-adapter.js +325 -0
- package/dist/multithreading/adapters/extraction-adapter.test.d.ts +2 -0
- package/dist/multithreading/adapters/extraction-adapter.test.d.ts.map +1 -0
- package/dist/multithreading/adapters/extraction-adapter.test.js +789 -0
- package/dist/multithreading/adapters/loading-adapter.d.ts +44 -0
- package/dist/multithreading/adapters/loading-adapter.d.ts.map +1 -0
- package/dist/multithreading/{worker-adapter/worker-adapter.helpers.d.ts → adapters/loading-adapter.helpers.d.ts} +1 -1
- package/dist/multithreading/adapters/loading-adapter.helpers.d.ts.map +1 -0
- package/dist/multithreading/adapters/loading-adapter.helpers.test.d.ts +2 -0
- package/dist/multithreading/adapters/loading-adapter.helpers.test.d.ts.map +1 -0
- package/dist/multithreading/{worker-adapter/worker-adapter.helpers.test.js → adapters/loading-adapter.helpers.test.js} +26 -26
- package/dist/multithreading/adapters/loading-adapter.js +461 -0
- package/dist/multithreading/adapters/loading-adapter.test.d.ts +2 -0
- package/dist/multithreading/adapters/loading-adapter.test.d.ts.map +1 -0
- package/dist/multithreading/adapters/loading-adapter.test.js +589 -0
- package/dist/multithreading/adapters/serialization.test.d.ts +2 -0
- package/dist/multithreading/adapters/serialization.test.d.ts.map +1 -0
- package/dist/multithreading/adapters/serialization.test.js +71 -0
- package/dist/multithreading/create-worker.test.js +36 -17
- package/dist/multithreading/process-task.d.ts +16 -1
- package/dist/multithreading/process-task.d.ts.map +1 -1
- package/dist/multithreading/process-task.js +97 -82
- 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 +3 -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 +20 -25
- 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/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/base-state.d.ts +75 -0
- package/dist/state/base-state.d.ts.map +1 -0
- package/dist/state/base-state.js +261 -0
- package/dist/state/extraction-state.d.ts +29 -0
- package/dist/state/extraction-state.d.ts.map +1 -0
- package/dist/state/extraction-state.js +122 -0
- package/dist/state/loading-state.d.ts +19 -0
- package/dist/state/loading-state.d.ts.map +1 -0
- package/dist/state/loading-state.js +40 -0
- 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 +44 -27
- package/dist/state/state.interfaces.d.ts.map +1 -1
- package/dist/state/state.interfaces.js +18 -5
- 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 +78 -0
- package/dist/state/state.test.js +135 -88
- 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 +20 -1
- package/dist/types/external-domain-metadata.d.ts.map +1 -1
- package/dist/types/extraction.d.ts +92 -221
- 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 +3 -3
- 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 +47 -29
- 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 +10 -13
- 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/http/types.js +0 -2
- package/dist/multithreading/worker-adapter/worker-adapter.artifacts.test.d.ts +0 -2
- package/dist/multithreading/worker-adapter/worker-adapter.artifacts.test.d.ts.map +0 -1
- package/dist/multithreading/worker-adapter/worker-adapter.d.ts +0 -91
- package/dist/multithreading/worker-adapter/worker-adapter.d.ts.map +0 -1
- package/dist/multithreading/worker-adapter/worker-adapter.helpers.d.ts.map +0 -1
- package/dist/multithreading/worker-adapter/worker-adapter.helpers.test.d.ts +0 -2
- package/dist/multithreading/worker-adapter/worker-adapter.helpers.test.d.ts.map +0 -1
- package/dist/multithreading/worker-adapter/worker-adapter.js +0 -745
- 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/state/state.d.ts +0 -30
- package/dist/state/state.d.ts.map +0 -1
- package/dist/state/state.js +0 -166
- /package/dist/multithreading/{worker-adapter/worker-adapter.helpers.js → adapters/loading-adapter.helpers.js} +0 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { BaseState } from '../../state/base-state';
|
|
2
|
+
import { BaseSdkState } from '../../state/state.interfaces';
|
|
3
|
+
import { AirSyncEvent, EventData, ExtractorEventType } from '../../types/extraction';
|
|
4
|
+
import { LoaderEventType } from '../../types/loading';
|
|
5
|
+
import { WorkerAdapterOptions } from '../../types/workers';
|
|
6
|
+
import { Uploader } from '../../uploader/uploader';
|
|
7
|
+
/**
|
|
8
|
+
* BaseAdapter holds the state and behavior shared by both sync modes and owns
|
|
9
|
+
* the `emit` control-protocol flow as a template method. Mode-specific adapters
|
|
10
|
+
* (`ExtractionAdapter`, `LoadingAdapter`) implement the abstract hooks to inject
|
|
11
|
+
* their own pre-emit work and event payload shaping.
|
|
12
|
+
*
|
|
13
|
+
* @typeParam ConnectorState - the connector-owned state shape
|
|
14
|
+
* @typeParam StateClass - the mode's `BaseState` subclass
|
|
15
|
+
*/
|
|
16
|
+
export declare abstract class BaseAdapter<ConnectorState, StateClass extends BaseState<ConnectorState, BaseSdkState>> {
|
|
17
|
+
readonly event: AirSyncEvent;
|
|
18
|
+
readonly options?: WorkerAdapterOptions;
|
|
19
|
+
isTimeout: boolean;
|
|
20
|
+
hasWorkerEmitted: boolean;
|
|
21
|
+
protected adapterState: StateClass;
|
|
22
|
+
protected uploader: Uploader;
|
|
23
|
+
constructor({ event, adapterState, options, }: {
|
|
24
|
+
event: AirSyncEvent;
|
|
25
|
+
adapterState: StateClass;
|
|
26
|
+
options?: WorkerAdapterOptions;
|
|
27
|
+
});
|
|
28
|
+
get state(): ConnectorState;
|
|
29
|
+
set state(value: ConnectorState);
|
|
30
|
+
postState(): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Pre-emit hook run before any state is persisted or the event is sent.
|
|
33
|
+
* Extraction uploads pending repos and updates extraction boundaries here;
|
|
34
|
+
* loading has nothing to do. Throwing aborts the emit (the caller signals
|
|
35
|
+
* worker exit).
|
|
36
|
+
*/
|
|
37
|
+
protected abstract beforeEmit(newEventType: ExtractorEventType | LoaderEventType): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Builds the mode-specific extras merged into the emitted event payload
|
|
40
|
+
* (extraction: artifacts; loading: reports + processed files).
|
|
41
|
+
*/
|
|
42
|
+
protected abstract buildEmitPayload(newEventType: ExtractorEventType | LoaderEventType): EventData;
|
|
43
|
+
/**
|
|
44
|
+
* Post-emit hook run after the event has been sent successfully. Extraction
|
|
45
|
+
* clears its accumulated artifacts here; loading has nothing to do.
|
|
46
|
+
*/
|
|
47
|
+
protected abstract afterEmit(newEventType: ExtractorEventType | LoaderEventType): void;
|
|
48
|
+
/**
|
|
49
|
+
* Emits an event to the platform.
|
|
50
|
+
*
|
|
51
|
+
* @param newEventType - The event type to be emitted
|
|
52
|
+
* @param data - The data to be sent with the event
|
|
53
|
+
*/
|
|
54
|
+
emit(newEventType: ExtractorEventType | LoaderEventType, data?: EventData): Promise<void>;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=base-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-adapter.d.ts","sourceRoot":"","sources":["../../../src/multithreading/adapters/base-adapter.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EACL,YAAY,EACZ,SAAS,EACT,kBAAkB,EACnB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EACL,oBAAoB,EAGrB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAEnD;;;;;;;;GAQG;AACH,8BAAsB,WAAW,CAC/B,cAAc,EACd,UAAU,SAAS,SAAS,CAAC,cAAc,EAAE,YAAY,CAAC;IAE1D,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,OAAO,CAAC,EAAE,oBAAoB,CAAC;IACxC,SAAS,EAAE,OAAO,CAAC;IACnB,gBAAgB,EAAE,OAAO,CAAC;IAE1B,SAAS,CAAC,YAAY,EAAE,UAAU,CAAC;IACnC,SAAS,CAAC,QAAQ,EAAE,QAAQ,CAAC;gBAEjB,EACV,KAAK,EACL,YAAY,EACZ,OAAO,GACR,EAAE;QACD,KAAK,EAAE,YAAY,CAAC;QACpB,YAAY,EAAE,UAAU,CAAC;QACzB,OAAO,CAAC,EAAE,oBAAoB,CAAC;KAChC;IAYD,IAAI,KAAK,IAAI,cAAc,CAE1B;IAED,IAAI,KAAK,CAAC,KAAK,EAAE,cAAc,EAE9B;IAEK,SAAS;IAMf;;;;;OAKG;IACH,SAAS,CAAC,QAAQ,CAAC,UAAU,CAC3B,YAAY,EAAE,kBAAkB,GAAG,eAAe,GACjD,OAAO,CAAC,IAAI,CAAC;IAEhB;;;OAGG;IACH,SAAS,CAAC,QAAQ,CAAC,gBAAgB,CACjC,YAAY,EAAE,kBAAkB,GAAG,eAAe,GACjD,SAAS;IAEZ;;;OAGG;IACH,SAAS,CAAC,QAAQ,CAAC,SAAS,CAC1B,YAAY,EAAE,kBAAkB,GAAG,eAAe,GACjD,IAAI;IAEP;;;;;OAKG;IACG,IAAI,CACR,YAAY,EAAE,kBAAkB,GAAG,eAAe,EAClD,IAAI,CAAC,EAAE,SAAS,GACf,OAAO,CAAC,IAAI,CAAC;CAkEjB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-adapter.emit.test.d.ts","sourceRoot":"","sources":["../../../src/multithreading/adapters/base-adapter.emit.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const constants_1 = require("../../common/constants");
|
|
4
|
+
const extraction_state_1 = require("../../state/extraction-state");
|
|
5
|
+
const loading_state_1 = require("../../state/loading-state");
|
|
6
|
+
const jest_setup_1 = require("../../tests/jest.setup");
|
|
7
|
+
const test_utils_1 = require("../../common/test-utils");
|
|
8
|
+
const types_1 = require("../../types");
|
|
9
|
+
const loading_1 = require("../../types/loading");
|
|
10
|
+
const extraction_adapter_1 = require("./extraction-adapter");
|
|
11
|
+
const loading_adapter_1 = require("./loading-adapter");
|
|
12
|
+
/* eslint-disable @typescript-eslint/no-require-imports */
|
|
13
|
+
jest.mock('../../common/control-protocol', () => ({
|
|
14
|
+
emit: jest.fn().mockResolvedValue({}),
|
|
15
|
+
}));
|
|
16
|
+
jest.mock('../../mappers/mappers');
|
|
17
|
+
jest.mock('../../uploader/uploader');
|
|
18
|
+
jest.mock('../../repo/repo');
|
|
19
|
+
jest.mock('node:worker_threads', () => ({
|
|
20
|
+
parentPort: { postMessage: jest.fn() },
|
|
21
|
+
}));
|
|
22
|
+
jest.mock('../../attachments-streaming/attachments-streaming-pool', () => ({
|
|
23
|
+
AttachmentsStreamingPool: jest.fn().mockImplementation(() => ({
|
|
24
|
+
streamAll: jest.fn().mockResolvedValue(undefined),
|
|
25
|
+
})),
|
|
26
|
+
}));
|
|
27
|
+
function makeAdapter(eventType = types_1.EventType.StartExtractingData) {
|
|
28
|
+
const event = (0, test_utils_1.createMockEvent)(jest_setup_1.mockServer.baseUrl, {
|
|
29
|
+
payload: { event_type: eventType },
|
|
30
|
+
});
|
|
31
|
+
const initialState = {
|
|
32
|
+
attachments: { completed: false },
|
|
33
|
+
};
|
|
34
|
+
const adapterState = new extraction_state_1.ExtractionState({ event, initialState });
|
|
35
|
+
adapterState.sdkState.snapInVersionId = '';
|
|
36
|
+
adapterState.sdkState.toDevRev = {
|
|
37
|
+
attachmentsMetadata: {
|
|
38
|
+
artifactIds: [],
|
|
39
|
+
lastProcessed: 0,
|
|
40
|
+
lastProcessedAttachmentsIdsList: [],
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
const adapter = new extraction_adapter_1.ExtractionAdapter({ event, adapterState });
|
|
44
|
+
return { adapter, event, adapterState };
|
|
45
|
+
}
|
|
46
|
+
function makeLoadingAdapter(eventType = types_1.EventType.StartLoadingData) {
|
|
47
|
+
const event = (0, test_utils_1.createMockEvent)(jest_setup_1.mockServer.baseUrl, {
|
|
48
|
+
payload: { event_type: eventType },
|
|
49
|
+
});
|
|
50
|
+
const initialState = {
|
|
51
|
+
attachments: { completed: false },
|
|
52
|
+
};
|
|
53
|
+
const adapterState = new loading_state_1.LoadingState({ event, initialState });
|
|
54
|
+
adapterState.sdkState.snapInVersionId = '';
|
|
55
|
+
const adapter = new loading_adapter_1.LoadingAdapter({ event, adapterState });
|
|
56
|
+
return { adapter, event, adapterState };
|
|
57
|
+
}
|
|
58
|
+
describe(`${extraction_adapter_1.ExtractionAdapter.name}.emit`, () => {
|
|
59
|
+
let adapter;
|
|
60
|
+
let mockPostMessage;
|
|
61
|
+
beforeEach(() => {
|
|
62
|
+
jest.clearAllMocks();
|
|
63
|
+
({ adapter } = makeAdapter());
|
|
64
|
+
const workerThreads = require('node:worker_threads');
|
|
65
|
+
mockPostMessage = jest.fn();
|
|
66
|
+
if (workerThreads.parentPort) {
|
|
67
|
+
jest
|
|
68
|
+
.spyOn(workerThreads.parentPort, 'postMessage')
|
|
69
|
+
.mockImplementation(mockPostMessage);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
workerThreads.parentPort = { postMessage: mockPostMessage };
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
afterEach(() => {
|
|
76
|
+
jest.restoreAllMocks();
|
|
77
|
+
});
|
|
78
|
+
it('should emit only one event when multiple events of same type are sent', async () => {
|
|
79
|
+
// Arrange
|
|
80
|
+
adapter['adapterState'].postState = jest.fn().mockResolvedValue(undefined);
|
|
81
|
+
adapter.uploadAllRepos = jest.fn().mockResolvedValue(undefined);
|
|
82
|
+
// Act
|
|
83
|
+
await adapter.emit(types_1.ExtractorEventType.MetadataExtractionError, {
|
|
84
|
+
reports: [],
|
|
85
|
+
processed_files: [],
|
|
86
|
+
});
|
|
87
|
+
await adapter.emit(types_1.ExtractorEventType.MetadataExtractionError, {
|
|
88
|
+
reports: [],
|
|
89
|
+
processed_files: [],
|
|
90
|
+
});
|
|
91
|
+
// Assert
|
|
92
|
+
expect(mockPostMessage).toHaveBeenCalledTimes(1);
|
|
93
|
+
});
|
|
94
|
+
it('should emit only once even when a different event type follows', async () => {
|
|
95
|
+
// Arrange
|
|
96
|
+
adapter['adapterState'].postState = jest.fn().mockResolvedValue(undefined);
|
|
97
|
+
adapter.uploadAllRepos = jest.fn().mockResolvedValue(undefined);
|
|
98
|
+
// Act
|
|
99
|
+
await adapter.emit(types_1.ExtractorEventType.MetadataExtractionError, {
|
|
100
|
+
reports: [],
|
|
101
|
+
processed_files: [],
|
|
102
|
+
});
|
|
103
|
+
await adapter.emit(types_1.ExtractorEventType.DataExtractionError, {
|
|
104
|
+
reports: [],
|
|
105
|
+
processed_files: [],
|
|
106
|
+
});
|
|
107
|
+
await adapter.emit(types_1.ExtractorEventType.AttachmentExtractionError, {
|
|
108
|
+
reports: [],
|
|
109
|
+
processed_files: [],
|
|
110
|
+
});
|
|
111
|
+
// Assert
|
|
112
|
+
expect(mockPostMessage).toHaveBeenCalledTimes(1);
|
|
113
|
+
});
|
|
114
|
+
it('should correctly emit one event even if postState errors', async () => {
|
|
115
|
+
// Arrange
|
|
116
|
+
adapter['adapterState'].postState = jest
|
|
117
|
+
.fn()
|
|
118
|
+
.mockRejectedValue(new Error('postState error'));
|
|
119
|
+
adapter.uploadAllRepos = jest.fn().mockResolvedValue(undefined);
|
|
120
|
+
// Act
|
|
121
|
+
await adapter.emit(types_1.ExtractorEventType.MetadataExtractionError, {
|
|
122
|
+
reports: [],
|
|
123
|
+
processed_files: [],
|
|
124
|
+
});
|
|
125
|
+
// Assert
|
|
126
|
+
expect(mockPostMessage).toHaveBeenCalledTimes(1);
|
|
127
|
+
});
|
|
128
|
+
it('should correctly emit one event even if uploadAllRepos errors', async () => {
|
|
129
|
+
// Arrange
|
|
130
|
+
adapter['adapterState'].postState = jest.fn().mockResolvedValue(undefined);
|
|
131
|
+
adapter.uploadAllRepos = jest
|
|
132
|
+
.fn()
|
|
133
|
+
.mockRejectedValue(new Error('uploadAllRepos error'));
|
|
134
|
+
// Act
|
|
135
|
+
await adapter.emit(types_1.ExtractorEventType.MetadataExtractionError, {
|
|
136
|
+
reports: [],
|
|
137
|
+
processed_files: [],
|
|
138
|
+
});
|
|
139
|
+
// Assert
|
|
140
|
+
expect(mockPostMessage).toHaveBeenCalledTimes(1);
|
|
141
|
+
});
|
|
142
|
+
it('should include artifacts in data for extraction events', async () => {
|
|
143
|
+
// Arrange
|
|
144
|
+
const { emit: mockEmit } = require('../../common/control-protocol');
|
|
145
|
+
adapter['adapterState'].postState = jest.fn().mockResolvedValue(undefined);
|
|
146
|
+
adapter.uploadAllRepos = jest.fn().mockResolvedValue(undefined);
|
|
147
|
+
adapter['_artifacts'] = [
|
|
148
|
+
{ id: 'art-1', item_count: 10, item_type: 'issues' },
|
|
149
|
+
];
|
|
150
|
+
// Act
|
|
151
|
+
await adapter.emit(types_1.ExtractorEventType.DataExtractionDone);
|
|
152
|
+
// Assert
|
|
153
|
+
expect(mockEmit).toHaveBeenCalledWith(expect.objectContaining({
|
|
154
|
+
data: expect.objectContaining({
|
|
155
|
+
artifacts: expect.arrayContaining([
|
|
156
|
+
expect.objectContaining({ id: 'art-1' }),
|
|
157
|
+
]),
|
|
158
|
+
}),
|
|
159
|
+
}));
|
|
160
|
+
const callData = mockEmit.mock.calls[0][0].data;
|
|
161
|
+
expect(callData).not.toHaveProperty('reports');
|
|
162
|
+
expect(callData).not.toHaveProperty('processed_files');
|
|
163
|
+
});
|
|
164
|
+
it('should include reports and processed_files in data for loader events', async () => {
|
|
165
|
+
// Arrange
|
|
166
|
+
const { emit: mockEmit } = require('../../common/control-protocol');
|
|
167
|
+
const { adapter: loadingAdapter } = makeLoadingAdapter();
|
|
168
|
+
loadingAdapter['adapterState'].postState = jest
|
|
169
|
+
.fn()
|
|
170
|
+
.mockResolvedValue(undefined);
|
|
171
|
+
loadingAdapter['loaderReports'] = [
|
|
172
|
+
{ item_type: 'tasks', [loading_1.ActionType.CREATED]: 5 },
|
|
173
|
+
];
|
|
174
|
+
loadingAdapter['_processedFiles'] = ['file-1', 'file-2'];
|
|
175
|
+
// Act
|
|
176
|
+
await loadingAdapter.emit(types_1.LoaderEventType.DataLoadingDone);
|
|
177
|
+
// Assert
|
|
178
|
+
expect(mockEmit).toHaveBeenCalledWith(expect.objectContaining({
|
|
179
|
+
data: expect.objectContaining({
|
|
180
|
+
reports: expect.arrayContaining([
|
|
181
|
+
expect.objectContaining({ item_type: 'tasks' }),
|
|
182
|
+
]),
|
|
183
|
+
processed_files: ['file-1', 'file-2'],
|
|
184
|
+
}),
|
|
185
|
+
}));
|
|
186
|
+
const callData = mockEmit.mock.calls[0][0].data;
|
|
187
|
+
expect(callData).not.toHaveProperty('artifacts');
|
|
188
|
+
});
|
|
189
|
+
it('should not include artifacts, reports, or processed_files for unknown event types', async () => {
|
|
190
|
+
// Arrange
|
|
191
|
+
const { emit: mockEmit } = require('../../common/control-protocol');
|
|
192
|
+
adapter['adapterState'].postState = jest.fn().mockResolvedValue(undefined);
|
|
193
|
+
adapter.uploadAllRepos = jest.fn().mockResolvedValue(undefined);
|
|
194
|
+
adapter['_artifacts'] = [
|
|
195
|
+
{ id: 'art-1', item_count: 10, item_type: 'issues' },
|
|
196
|
+
];
|
|
197
|
+
// Act
|
|
198
|
+
await adapter.emit('SOME_UNKNOWN_EVENT');
|
|
199
|
+
// Assert
|
|
200
|
+
const callData = mockEmit.mock.calls[0][0].data;
|
|
201
|
+
expect(callData).not.toHaveProperty('artifacts');
|
|
202
|
+
expect(callData).not.toHaveProperty('reports');
|
|
203
|
+
expect(callData).not.toHaveProperty('processed_files');
|
|
204
|
+
});
|
|
205
|
+
it('should include artifacts for all ExtractorEventType values', async () => {
|
|
206
|
+
var _a, _b;
|
|
207
|
+
// Arrange
|
|
208
|
+
const { emit: mockEmit } = require('../../common/control-protocol');
|
|
209
|
+
const extractorEvents = [
|
|
210
|
+
types_1.ExtractorEventType.DataExtractionDone,
|
|
211
|
+
types_1.ExtractorEventType.DataExtractionProgress,
|
|
212
|
+
types_1.ExtractorEventType.DataExtractionError,
|
|
213
|
+
types_1.ExtractorEventType.AttachmentExtractionDone,
|
|
214
|
+
types_1.ExtractorEventType.AttachmentExtractionProgress,
|
|
215
|
+
];
|
|
216
|
+
for (const eventType of extractorEvents) {
|
|
217
|
+
jest.clearAllMocks();
|
|
218
|
+
adapter.hasWorkerEmitted = false;
|
|
219
|
+
adapter['adapterState'].postState = jest
|
|
220
|
+
.fn()
|
|
221
|
+
.mockResolvedValue(undefined);
|
|
222
|
+
adapter.uploadAllRepos = jest.fn().mockResolvedValue(undefined);
|
|
223
|
+
// Act
|
|
224
|
+
await adapter.emit(eventType);
|
|
225
|
+
// Assert
|
|
226
|
+
const callData = (_b = (_a = mockEmit.mock.calls[0]) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.data;
|
|
227
|
+
expect(callData).toHaveProperty('artifacts');
|
|
228
|
+
expect(callData).not.toHaveProperty('reports');
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
it('should include reports and processed_files for all LoaderEventType values', async () => {
|
|
232
|
+
var _a, _b;
|
|
233
|
+
// Arrange
|
|
234
|
+
const { emit: mockEmit } = require('../../common/control-protocol');
|
|
235
|
+
const loaderEvents = [
|
|
236
|
+
types_1.LoaderEventType.DataLoadingDone,
|
|
237
|
+
types_1.LoaderEventType.DataLoadingProgress,
|
|
238
|
+
types_1.LoaderEventType.DataLoadingError,
|
|
239
|
+
types_1.LoaderEventType.AttachmentLoadingDone,
|
|
240
|
+
types_1.LoaderEventType.AttachmentLoadingProgress,
|
|
241
|
+
];
|
|
242
|
+
const { adapter: loadingAdapter } = makeLoadingAdapter();
|
|
243
|
+
loadingAdapter['adapterState'].postState = jest
|
|
244
|
+
.fn()
|
|
245
|
+
.mockResolvedValue(undefined);
|
|
246
|
+
for (const eventType of loaderEvents) {
|
|
247
|
+
jest.clearAllMocks();
|
|
248
|
+
loadingAdapter.hasWorkerEmitted = false;
|
|
249
|
+
// Act
|
|
250
|
+
await loadingAdapter.emit(eventType);
|
|
251
|
+
// Assert
|
|
252
|
+
const callData = (_b = (_a = mockEmit.mock.calls[0]) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.data;
|
|
253
|
+
expect(callData).toHaveProperty('reports');
|
|
254
|
+
expect(callData).toHaveProperty('processed_files');
|
|
255
|
+
expect(callData).not.toHaveProperty('artifacts');
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
it('should truncate a long error message, preserving the original prefix', async () => {
|
|
259
|
+
var _a, _b;
|
|
260
|
+
// Arrange
|
|
261
|
+
adapter['adapterState'].postState = jest.fn().mockResolvedValue(undefined);
|
|
262
|
+
adapter.uploadAllRepos = jest.fn().mockResolvedValue(undefined);
|
|
263
|
+
const longMessage = 'E'.repeat(20000);
|
|
264
|
+
// Act
|
|
265
|
+
await adapter.emit(types_1.ExtractorEventType.DataExtractionError, {
|
|
266
|
+
error: { message: longMessage },
|
|
267
|
+
});
|
|
268
|
+
// Assert
|
|
269
|
+
const { emit: mockEmit } = require('../../common/control-protocol');
|
|
270
|
+
const emittedMessage = (_b = (_a = mockEmit.mock.calls[0][0].data) === null || _a === void 0 ? void 0 : _a.error) === null || _b === void 0 ? void 0 : _b.message;
|
|
271
|
+
expect(emittedMessage.length).toBeLessThan(longMessage.length);
|
|
272
|
+
expect(emittedMessage.startsWith('E'.repeat(100))).toBe(true);
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
describe('WorkerAdapter — workersOldest / workersNewest boundary updates', () => {
|
|
276
|
+
let adapter;
|
|
277
|
+
let adapterState;
|
|
278
|
+
let mockPostMessage;
|
|
279
|
+
beforeEach(() => {
|
|
280
|
+
jest.clearAllMocks();
|
|
281
|
+
({ adapter, adapterState } = makeAdapter());
|
|
282
|
+
const workerThreads = require('node:worker_threads');
|
|
283
|
+
mockPostMessage = jest.fn();
|
|
284
|
+
if (workerThreads.parentPort) {
|
|
285
|
+
jest
|
|
286
|
+
.spyOn(workerThreads.parentPort, 'postMessage')
|
|
287
|
+
.mockImplementation(mockPostMessage);
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
workerThreads.parentPort = { postMessage: mockPostMessage };
|
|
291
|
+
}
|
|
292
|
+
adapter['adapterState'].postState = jest.fn().mockResolvedValue(undefined);
|
|
293
|
+
adapter.uploadAllRepos = jest.fn().mockResolvedValue(undefined);
|
|
294
|
+
});
|
|
295
|
+
afterEach(() => {
|
|
296
|
+
jest.restoreAllMocks();
|
|
297
|
+
});
|
|
298
|
+
async function emitDone(adapterInstance, extractionStart, extractionEnd) {
|
|
299
|
+
adapterInstance.event.payload.event_context.extract_from = extractionStart;
|
|
300
|
+
adapterInstance.event.payload.event_context.extract_to = extractionEnd;
|
|
301
|
+
// Reset the emit guard so we can emit multiple times within one test.
|
|
302
|
+
adapterInstance['hasWorkerEmitted'] = false;
|
|
303
|
+
await adapterInstance.emit(types_1.ExtractorEventType.AttachmentExtractionDone, {
|
|
304
|
+
reports: [],
|
|
305
|
+
processed_files: [],
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
describe('initial import with UNBOUNDED start', () => {
|
|
309
|
+
it('should set workersOldest to UNBOUNDED_DATE_TIME_VALUE and workersNewest to extraction end', async () => {
|
|
310
|
+
await emitDone(adapter, constants_1.UNBOUNDED_DATE_TIME_VALUE, '2025-06-01T00:00:00.000Z');
|
|
311
|
+
expect(adapterState.sdkState.workersOldest).toBe(constants_1.UNBOUNDED_DATE_TIME_VALUE);
|
|
312
|
+
expect(adapterState.sdkState.workersNewest).toBe('2025-06-01T00:00:00.000Z');
|
|
313
|
+
});
|
|
314
|
+
});
|
|
315
|
+
describe('reconciliation after UNBOUNDED initial import', () => {
|
|
316
|
+
it('should NOT overwrite workersOldest when reconciliation start is later than sentinel', async () => {
|
|
317
|
+
await emitDone(adapter, constants_1.UNBOUNDED_DATE_TIME_VALUE, '2025-06-01T00:00:00.000Z');
|
|
318
|
+
await emitDone(adapter, '2025-01-01T00:00:00.000Z', '2025-03-01T00:00:00.000Z');
|
|
319
|
+
expect(adapterState.sdkState.workersOldest).toBe(constants_1.UNBOUNDED_DATE_TIME_VALUE);
|
|
320
|
+
expect(adapterState.sdkState.workersNewest).toBe('2025-06-01T00:00:00.000Z');
|
|
321
|
+
});
|
|
322
|
+
it('should NOT overwrite workersOldest even when reconciliation start is very early', async () => {
|
|
323
|
+
await emitDone(adapter, constants_1.UNBOUNDED_DATE_TIME_VALUE, '2025-06-01T00:00:00.000Z');
|
|
324
|
+
await emitDone(adapter, '1980-01-01T00:00:00.000Z', '1990-01-01T00:00:00.000Z');
|
|
325
|
+
expect(adapterState.sdkState.workersOldest).toBe(constants_1.UNBOUNDED_DATE_TIME_VALUE);
|
|
326
|
+
expect(adapterState.sdkState.workersNewest).toBe('2025-06-01T00:00:00.000Z');
|
|
327
|
+
});
|
|
328
|
+
});
|
|
329
|
+
describe('forward sync after UNBOUNDED initial import', () => {
|
|
330
|
+
it('should expand workersNewest forward while preserving workersOldest', async () => {
|
|
331
|
+
await emitDone(adapter, constants_1.UNBOUNDED_DATE_TIME_VALUE, '2025-06-01T00:00:00.000Z');
|
|
332
|
+
await emitDone(adapter, '2025-06-01T00:00:00.000Z', '2025-07-01T00:00:00.000Z');
|
|
333
|
+
expect(adapterState.sdkState.workersOldest).toBe(constants_1.UNBOUNDED_DATE_TIME_VALUE);
|
|
334
|
+
expect(adapterState.sdkState.workersNewest).toBe('2025-07-01T00:00:00.000Z');
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
describe('reconciliation with end beyond current newest', () => {
|
|
338
|
+
it('should expand workersNewest when reconciliation end is later', async () => {
|
|
339
|
+
await emitDone(adapter, constants_1.UNBOUNDED_DATE_TIME_VALUE, '2025-06-01T00:00:00.000Z');
|
|
340
|
+
await emitDone(adapter, '2024-01-01T00:00:00.000Z', '2025-08-01T00:00:00.000Z');
|
|
341
|
+
expect(adapterState.sdkState.workersOldest).toBe(constants_1.UNBOUNDED_DATE_TIME_VALUE);
|
|
342
|
+
expect(adapterState.sdkState.workersNewest).toBe('2025-08-01T00:00:00.000Z');
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
describe('first sync with absolute dates (no UNBOUNDED)', () => {
|
|
346
|
+
it('should set both boundaries from the extraction range', async () => {
|
|
347
|
+
await emitDone(adapter, '2025-01-01T00:00:00.000Z', '2025-03-01T00:00:00.000Z');
|
|
348
|
+
expect(adapterState.sdkState.workersOldest).toBe('2025-01-01T00:00:00.000Z');
|
|
349
|
+
expect(adapterState.sdkState.workersNewest).toBe('2025-03-01T00:00:00.000Z');
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
describe('reconciliation after absolute initial sync', () => {
|
|
353
|
+
it('should expand workersOldest backward when reconciliation start is earlier', async () => {
|
|
354
|
+
await emitDone(adapter, '2025-01-01T00:00:00.000Z', '2025-03-01T00:00:00.000Z');
|
|
355
|
+
await emitDone(adapter, '2024-06-01T00:00:00.000Z', '2025-02-01T00:00:00.000Z');
|
|
356
|
+
expect(adapterState.sdkState.workersOldest).toBe('2024-06-01T00:00:00.000Z');
|
|
357
|
+
expect(adapterState.sdkState.workersNewest).toBe('2025-03-01T00:00:00.000Z');
|
|
358
|
+
});
|
|
359
|
+
it('should NOT change boundaries when reconciliation is within existing range', async () => {
|
|
360
|
+
await emitDone(adapter, '2025-01-01T00:00:00.000Z', '2025-03-01T00:00:00.000Z');
|
|
361
|
+
await emitDone(adapter, '2025-01-15T00:00:00.000Z', '2025-02-15T00:00:00.000Z');
|
|
362
|
+
expect(adapterState.sdkState.workersOldest).toBe('2025-01-01T00:00:00.000Z');
|
|
363
|
+
expect(adapterState.sdkState.workersNewest).toBe('2025-03-01T00:00:00.000Z');
|
|
364
|
+
});
|
|
365
|
+
it('should expand both boundaries when reconciliation exceeds both', async () => {
|
|
366
|
+
await emitDone(adapter, '2025-01-01T00:00:00.000Z', '2025-03-01T00:00:00.000Z');
|
|
367
|
+
await emitDone(adapter, '2024-06-01T00:00:00.000Z', '2025-09-01T00:00:00.000Z');
|
|
368
|
+
expect(adapterState.sdkState.workersOldest).toBe('2024-06-01T00:00:00.000Z');
|
|
369
|
+
expect(adapterState.sdkState.workersNewest).toBe('2025-09-01T00:00:00.000Z');
|
|
370
|
+
});
|
|
371
|
+
});
|
|
372
|
+
describe('multiple forward syncs', () => {
|
|
373
|
+
it('should progressively expand workersNewest while preserving workersOldest', async () => {
|
|
374
|
+
await emitDone(adapter, constants_1.UNBOUNDED_DATE_TIME_VALUE, '2025-06-01T00:00:00.000Z');
|
|
375
|
+
await emitDone(adapter, '2025-06-01T00:00:00.000Z', '2025-07-01T00:00:00.000Z');
|
|
376
|
+
expect(adapterState.sdkState.workersNewest).toBe('2025-07-01T00:00:00.000Z');
|
|
377
|
+
await emitDone(adapter, '2025-07-01T00:00:00.000Z', '2025-08-01T00:00:00.000Z');
|
|
378
|
+
expect(adapterState.sdkState.workersNewest).toBe('2025-08-01T00:00:00.000Z');
|
|
379
|
+
expect(adapterState.sdkState.workersOldest).toBe(constants_1.UNBOUNDED_DATE_TIME_VALUE);
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
describe('non-AttachmentExtractionDone events should NOT update boundaries', () => {
|
|
383
|
+
it.each([
|
|
384
|
+
types_1.ExtractorEventType.DataExtractionDone,
|
|
385
|
+
types_1.ExtractorEventType.DataExtractionProgress,
|
|
386
|
+
types_1.ExtractorEventType.MetadataExtractionError,
|
|
387
|
+
types_1.ExtractorEventType.AttachmentExtractionError,
|
|
388
|
+
])('should not update boundaries on %s', async (eventType) => {
|
|
389
|
+
adapterState.sdkState.workersOldest = '2025-01-01T00:00:00.000Z';
|
|
390
|
+
adapterState.sdkState.workersNewest = '2025-03-01T00:00:00.000Z';
|
|
391
|
+
adapter.event.payload.event_context.extract_from =
|
|
392
|
+
'2024-01-01T00:00:00.000Z';
|
|
393
|
+
adapter.event.payload.event_context.extract_to =
|
|
394
|
+
'2025-12-01T00:00:00.000Z';
|
|
395
|
+
await adapter.emit(eventType, {
|
|
396
|
+
reports: [],
|
|
397
|
+
processed_files: [],
|
|
398
|
+
});
|
|
399
|
+
expect(adapterState.sdkState.workersOldest).toBe('2025-01-01T00:00:00.000Z');
|
|
400
|
+
expect(adapterState.sdkState.workersNewest).toBe('2025-03-01T00:00:00.000Z');
|
|
401
|
+
});
|
|
402
|
+
});
|
|
403
|
+
});
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BaseAdapter = void 0;
|
|
4
|
+
const node_worker_threads_1 = require("node:worker_threads");
|
|
5
|
+
const constants_1 = require("../../common/constants");
|
|
6
|
+
const control_protocol_1 = require("../../common/control-protocol");
|
|
7
|
+
const helpers_1 = require("../../common/helpers");
|
|
8
|
+
const logger_1 = require("../../logger/logger");
|
|
9
|
+
const logger_context_1 = require("../../logger/logger.context");
|
|
10
|
+
const workers_1 = require("../../types/workers");
|
|
11
|
+
const uploader_1 = require("../../uploader/uploader");
|
|
12
|
+
/**
|
|
13
|
+
* BaseAdapter holds the state and behavior shared by both sync modes and owns
|
|
14
|
+
* the `emit` control-protocol flow as a template method. Mode-specific adapters
|
|
15
|
+
* (`ExtractionAdapter`, `LoadingAdapter`) implement the abstract hooks to inject
|
|
16
|
+
* their own pre-emit work and event payload shaping.
|
|
17
|
+
*
|
|
18
|
+
* @typeParam ConnectorState - the connector-owned state shape
|
|
19
|
+
* @typeParam StateClass - the mode's `BaseState` subclass
|
|
20
|
+
*/
|
|
21
|
+
class BaseAdapter {
|
|
22
|
+
constructor({ event, adapterState, options, }) {
|
|
23
|
+
this.event = event;
|
|
24
|
+
this.options = options;
|
|
25
|
+
this.adapterState = adapterState;
|
|
26
|
+
this.hasWorkerEmitted = false;
|
|
27
|
+
this.isTimeout = false;
|
|
28
|
+
this.uploader = new uploader_1.Uploader({
|
|
29
|
+
event,
|
|
30
|
+
options,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
get state() {
|
|
34
|
+
return this.adapterState.state;
|
|
35
|
+
}
|
|
36
|
+
set state(value) {
|
|
37
|
+
this.adapterState.state = value;
|
|
38
|
+
}
|
|
39
|
+
async postState() {
|
|
40
|
+
return (0, logger_context_1.runWithSdkLogContext)(async () => {
|
|
41
|
+
await this.adapterState.postState();
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Emits an event to the platform.
|
|
46
|
+
*
|
|
47
|
+
* @param newEventType - The event type to be emitted
|
|
48
|
+
* @param data - The data to be sent with the event
|
|
49
|
+
*/
|
|
50
|
+
async emit(newEventType, data) {
|
|
51
|
+
return (0, logger_context_1.runWithSdkLogContext)(async () => {
|
|
52
|
+
var _a;
|
|
53
|
+
if (this.hasWorkerEmitted) {
|
|
54
|
+
console.warn(`Trying to emit event with event type: ${newEventType}. Ignoring emit request because it has already been emitted.`);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
await this.beforeEmit(newEventType);
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
console.error('Error while preparing to emit event', error);
|
|
62
|
+
node_worker_threads_1.parentPort === null || node_worker_threads_1.parentPort === void 0 ? void 0 : node_worker_threads_1.parentPort.postMessage(workers_1.WorkerMessageSubject.WorkerMessageExit);
|
|
63
|
+
this.hasWorkerEmitted = true;
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
// We want to save the state every time we emit an event, except for the start and delete events
|
|
67
|
+
if (!constants_1.STATELESS_EVENT_TYPES.includes(this.event.payload.event_type)) {
|
|
68
|
+
console.log(`Saving state before emitting event with event type: ${newEventType}.`);
|
|
69
|
+
try {
|
|
70
|
+
await this.adapterState.postState();
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
console.error('Error while posting state', error);
|
|
74
|
+
node_worker_threads_1.parentPort === null || node_worker_threads_1.parentPort === void 0 ? void 0 : node_worker_threads_1.parentPort.postMessage(workers_1.WorkerMessageSubject.WorkerMessageExit);
|
|
75
|
+
this.hasWorkerEmitted = true;
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
// Always prune error messages to make them shorter before emit
|
|
81
|
+
if ((_a = data === null || data === void 0 ? void 0 : data.error) === null || _a === void 0 ? void 0 : _a.message) {
|
|
82
|
+
data.error.message = (0, helpers_1.truncateMessage)(data.error.message);
|
|
83
|
+
}
|
|
84
|
+
await (0, control_protocol_1.emit)({
|
|
85
|
+
eventType: newEventType,
|
|
86
|
+
event: this.event,
|
|
87
|
+
data: Object.assign(Object.assign({}, data), this.buildEmitPayload(newEventType)),
|
|
88
|
+
});
|
|
89
|
+
const message = {
|
|
90
|
+
subject: workers_1.WorkerMessageSubject.WorkerMessageEmitted,
|
|
91
|
+
payload: { eventType: newEventType },
|
|
92
|
+
};
|
|
93
|
+
this.afterEmit(newEventType);
|
|
94
|
+
node_worker_threads_1.parentPort === null || node_worker_threads_1.parentPort === void 0 ? void 0 : node_worker_threads_1.parentPort.postMessage(message);
|
|
95
|
+
this.hasWorkerEmitted = true;
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
console.error(`Error while emitting event with event type: ${newEventType}.`, (0, logger_1.serializeError)(error));
|
|
99
|
+
node_worker_threads_1.parentPort === null || node_worker_threads_1.parentPort === void 0 ? void 0 : node_worker_threads_1.parentPort.postMessage(workers_1.WorkerMessageSubject.WorkerMessageExit);
|
|
100
|
+
this.hasWorkerEmitted = true;
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
exports.BaseAdapter = BaseAdapter;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extraction-adapter.artifacts.test.d.ts","sourceRoot":"","sources":["../../../src/multithreading/adapters/extraction-adapter.artifacts.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const
|
|
3
|
+
const constants_1 = require("../../common/constants");
|
|
4
|
+
const extraction_state_1 = require("../../state/extraction-state");
|
|
5
|
+
const jest_setup_1 = require("../../tests/jest.setup");
|
|
4
6
|
const test_helpers_1 = require("../../tests/test-helpers");
|
|
7
|
+
const test_utils_1 = require("../../common/test-utils");
|
|
5
8
|
const types_1 = require("../../types");
|
|
6
|
-
const
|
|
9
|
+
const extraction_adapter_1 = require("./extraction-adapter");
|
|
7
10
|
// 1. Create a mock function for the method you want to override.
|
|
8
11
|
const mockUpload = (itemType, objects) => {
|
|
9
12
|
return {
|
|
@@ -49,12 +52,14 @@ describe('Artifact ordering when artifacts overflow batch sizes in repositories'
|
|
|
49
52
|
let testAdapter;
|
|
50
53
|
beforeEach(() => {
|
|
51
54
|
// Create a fresh adapter instance for this test to avoid mocking conflicts
|
|
52
|
-
const mockEvent = (0,
|
|
53
|
-
|
|
55
|
+
const mockEvent = (0, test_utils_1.createMockEvent)(jest_setup_1.mockServer.baseUrl, {
|
|
56
|
+
payload: { event_type: types_1.EventType.StartExtractingData },
|
|
57
|
+
});
|
|
58
|
+
const mockAdapterState = new extraction_state_1.ExtractionState({
|
|
54
59
|
event: mockEvent,
|
|
55
60
|
initialState: { attachments: { completed: false } },
|
|
56
61
|
});
|
|
57
|
-
testAdapter = new
|
|
62
|
+
testAdapter = new extraction_adapter_1.ExtractionAdapter({
|
|
58
63
|
event: mockEvent,
|
|
59
64
|
adapterState: mockAdapterState,
|
|
60
65
|
options: {
|
|
@@ -125,3 +130,46 @@ describe('Artifact ordering when artifacts overflow batch sizes in repositories'
|
|
|
125
130
|
expect(checkArtifactOrder(artifacts, repos)).toBe(true);
|
|
126
131
|
});
|
|
127
132
|
});
|
|
133
|
+
describe('External sync units splitting into artifacts', () => {
|
|
134
|
+
let testAdapter;
|
|
135
|
+
beforeEach(() => {
|
|
136
|
+
const mockEvent = (0, test_utils_1.createMockEvent)(jest_setup_1.mockServer.baseUrl, {
|
|
137
|
+
payload: { event_type: types_1.EventType.StartExtractingExternalSyncUnits },
|
|
138
|
+
});
|
|
139
|
+
const mockAdapterState = new extraction_state_1.ExtractionState({
|
|
140
|
+
event: mockEvent,
|
|
141
|
+
initialState: {},
|
|
142
|
+
});
|
|
143
|
+
testAdapter = new extraction_adapter_1.ExtractionAdapter({
|
|
144
|
+
event: mockEvent,
|
|
145
|
+
adapterState: mockAdapterState,
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
it('should split 125k external sync units into 5 artifacts', async () => {
|
|
149
|
+
const BATCH_SIZE = 25000;
|
|
150
|
+
const TOTAL_UNITS = 125000;
|
|
151
|
+
const externalSyncUnits = Array.from({ length: TOTAL_UNITS }, (_, i) => ({
|
|
152
|
+
id: String(i),
|
|
153
|
+
name: `Unit ${i}`,
|
|
154
|
+
description: `Description ${i}`,
|
|
155
|
+
}));
|
|
156
|
+
testAdapter.initializeRepos([
|
|
157
|
+
{
|
|
158
|
+
itemType: constants_1.AirSyncDefaultItemTypes.EXTERNAL_SYNC_UNITS,
|
|
159
|
+
overridenOptions: {
|
|
160
|
+
batchSize: BATCH_SIZE,
|
|
161
|
+
skipConfirmation: true,
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
]);
|
|
165
|
+
const repo = testAdapter.getRepo(constants_1.AirSyncDefaultItemTypes.EXTERNAL_SYNC_UNITS);
|
|
166
|
+
const chunkSize = 10000;
|
|
167
|
+
for (let i = 0; i < TOTAL_UNITS; i += chunkSize) {
|
|
168
|
+
await (repo === null || repo === void 0 ? void 0 : repo.push(externalSyncUnits.slice(i, i + chunkSize)));
|
|
169
|
+
}
|
|
170
|
+
await testAdapter.uploadAllRepos();
|
|
171
|
+
expect(testAdapter.artifacts.length).toBe(TOTAL_UNITS / BATCH_SIZE // 5
|
|
172
|
+
);
|
|
173
|
+
expect(testAdapter.artifacts.every((a) => a.item_type === constants_1.AirSyncDefaultItemTypes.EXTERNAL_SYNC_UNITS)).toBe(true);
|
|
174
|
+
});
|
|
175
|
+
});
|