@devrev/ts-adaas 0.0.3 → 1.0.1
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 +186 -116
- package/dist/common/constants.d.ts +10 -0
- package/dist/common/constants.js +27 -0
- package/dist/common/control-protocol.d.ts +7 -0
- package/dist/common/control-protocol.js +41 -0
- package/dist/common/helpers.d.ts +4 -0
- package/dist/common/helpers.js +38 -0
- package/dist/common/install-initial-domain-mapping.d.ts +3 -0
- package/dist/common/install-initial-domain-mapping.js +87 -0
- package/dist/{src → deprecated}/adapter/index.d.ts +10 -7
- package/dist/{src → deprecated}/adapter/index.js +13 -10
- package/dist/{src → deprecated}/common/helpers.d.ts +1 -2
- package/dist/deprecated/common/helpers.js +47 -0
- package/dist/deprecated/demo-extractor/index.d.ts +17 -0
- package/dist/{src → deprecated}/demo-extractor/index.js +28 -22
- package/dist/{src → deprecated}/uploader/index.d.ts +1 -1
- package/dist/{src → deprecated}/uploader/index.js +3 -1
- package/dist/{src/http → http}/client.d.ts +2 -1
- package/dist/index.d.ts +10 -0
- package/dist/{src/index.js → index.js} +11 -3
- package/dist/logger/logger.d.ts +14 -0
- package/dist/logger/logger.interfaces.d.ts +14 -0
- package/dist/logger/logger.interfaces.js +9 -0
- package/dist/logger/logger.js +91 -0
- package/dist/logger/logger.test.js +49 -0
- package/dist/repo/repo.d.ts +14 -0
- package/dist/repo/repo.interfaces.d.ts +43 -0
- package/dist/repo/repo.interfaces.js +2 -0
- package/dist/repo/repo.js +68 -0
- package/dist/repo/repo.test.js +74 -0
- package/dist/state/state.d.ts +24 -0
- package/dist/state/state.interfaces.d.ts +24 -0
- package/dist/state/state.interfaces.js +2 -0
- package/dist/state/state.js +115 -0
- package/dist/tests/test-helpers.d.ts +9 -0
- package/dist/tests/test-helpers.interfaces.d.ts +11 -0
- package/dist/tests/test-helpers.interfaces.js +2 -0
- package/dist/tests/test-helpers.js +86 -0
- package/dist/tests/test-worker.js +16 -0
- package/dist/types/common.d.ts +38 -0
- package/dist/{src/types → types}/common.js +4 -0
- package/dist/{src/types → types}/extraction.d.ts +56 -14
- package/dist/{src/types → types}/extraction.js +12 -29
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.js +11 -0
- package/dist/types/workers.d.ts +142 -0
- package/dist/types/workers.js +23 -0
- package/dist/uploader/uploader.d.ts +38 -0
- package/dist/uploader/uploader.interfaces.d.ts +63 -0
- package/dist/uploader/uploader.interfaces.js +2 -0
- package/dist/uploader/uploader.js +351 -0
- package/dist/{tests → uploader}/uploader.test.js +7 -5
- package/dist/workers/create-worker.d.ts +4 -0
- package/dist/workers/create-worker.js +30 -0
- package/dist/workers/create-worker.test.js +25 -0
- package/dist/workers/default-workers/attachments-deletion.d.ts +1 -0
- package/dist/workers/default-workers/attachments-deletion.js +13 -0
- package/dist/workers/default-workers/attachments-extraction.d.ts +1 -0
- package/dist/workers/default-workers/attachments-extraction.js +49 -0
- package/dist/workers/default-workers/data-deletion.d.ts +1 -0
- package/dist/workers/default-workers/data-deletion.js +15 -0
- package/dist/workers/default-workers/data-extraction.d.ts +1 -0
- package/dist/workers/default-workers/data-extraction.js +101 -0
- package/dist/workers/default-workers/external-sync-units-extraction.d.ts +1 -0
- package/dist/workers/default-workers/external-sync-units-extraction.js +27 -0
- package/dist/workers/default-workers/metadata-extraction.d.ts +1 -0
- package/dist/workers/default-workers/metadata-extraction.js +26 -0
- package/dist/workers/dummy-extractor/data-normalization.d.ts +4 -0
- package/dist/workers/dummy-extractor/data-normalization.js +41 -0
- package/dist/workers/dummy-extractor/external_domain_metadata.json +58 -0
- package/dist/workers/process-task.d.ts +2 -0
- package/dist/workers/process-task.js +44 -0
- package/dist/workers/spawn.d.ts +25 -0
- package/dist/workers/spawn.js +164 -0
- package/dist/workers/worker-adapter.d.ts +48 -0
- package/dist/workers/worker-adapter.js +138 -0
- package/dist/workers/worker.d.ts +1 -0
- package/dist/workers/worker.js +6 -0
- package/package.json +9 -5
- package/dist/src/common/constants.d.ts +0 -2
- package/dist/src/common/constants.js +0 -10
- package/dist/src/common/helpers.js +0 -59
- package/dist/src/common/install-initial-domain-mapping.d.ts +0 -3
- package/dist/src/common/install-initial-domain-mapping.js +0 -60
- package/dist/src/demo-extractor/index.d.ts +0 -11
- package/dist/src/index.d.ts +0 -6
- package/dist/src/logging/index.d.ts +0 -18
- package/dist/src/logging/index.js +0 -39
- package/dist/src/state/index.d.ts +0 -23
- package/dist/src/state/index.js +0 -111
- package/dist/src/types/common.d.ts +0 -37
- package/dist/src/types/index.d.ts +0 -2
- package/dist/src/types/index.js +0 -18
- package/dist/tests/adapter.helpers.test.js +0 -60
- package/dist/tests/adapter.test.js +0 -123
- package/dist/tests/demo-extractor.test.js +0 -61
- package/dist/tests/state.test.js +0 -101
- /package/dist/{src → deprecated}/demo-extractor/external_domain_metadata.json +0 -0
- /package/dist/{src/http → http}/client.js +0 -0
- /package/dist/{src/http → http}/constants.d.ts +0 -0
- /package/dist/{src/http → http}/constants.js +0 -0
- /package/dist/{src/http → http}/index.d.ts +0 -0
- /package/dist/{src/http → http}/index.js +0 -0
- /package/dist/{src/http → http}/types.d.ts +0 -0
- /package/dist/{src/http → http}/types.js +0 -0
- /package/dist/{tests/adapter.helpers.test.d.ts → logger/logger.test.d.ts} +0 -0
- /package/dist/{tests/adapter.test.d.ts → repo/repo.test.d.ts} +0 -0
- /package/dist/tests/{demo-extractor.test.d.ts → test-worker.d.ts} +0 -0
- /package/dist/{tests → uploader}/uploader.test.d.ts +0 -0
- /package/dist/{tests/state.test.d.ts → workers/create-worker.test.d.ts} +0 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { Worker } from 'worker_threads';
|
|
2
|
+
import { MessagePort } from 'node:worker_threads';
|
|
3
|
+
import { State } from '../state/state';
|
|
4
|
+
import { WorkerAdapter } from '../workers/worker-adapter';
|
|
5
|
+
import { ExtractorEventType, AirdropEvent } from './extraction';
|
|
6
|
+
/**
|
|
7
|
+
* WorkerAdapterInterface is an interface for WorkerAdapter class.
|
|
8
|
+
* @interface WorkerAdapterInterface
|
|
9
|
+
* @constructor
|
|
10
|
+
* @param {AirdropEvent} event - The event object received from the platform
|
|
11
|
+
* @param {object=} initialState - The initial state of the adapter
|
|
12
|
+
* @param {MessagePort} parentPort - The parent port of the worker thread
|
|
13
|
+
* @param {WorkerAdapterInterface} options - The options to create a new instance of WorkerAdapter class
|
|
14
|
+
*/
|
|
15
|
+
export interface WorkerAdapterInterface<ConnectorState> {
|
|
16
|
+
event: AirdropEvent;
|
|
17
|
+
adapterState: State<ConnectorState>;
|
|
18
|
+
parentPort: MessagePort;
|
|
19
|
+
options: WorkerAdapterOptions;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* WorkerAdapterOptions represents the options for WorkerAdapter class.
|
|
23
|
+
* @interface WorkerAdapterOptions
|
|
24
|
+
* @constructor
|
|
25
|
+
* @param {boolean=} isLocalDevelopment - A flag to indicate if the adapter is being used in local development
|
|
26
|
+
* @param {number=} timeout - The timeout for the worker thread
|
|
27
|
+
*/
|
|
28
|
+
export interface WorkerAdapterOptions {
|
|
29
|
+
isLocalDevelopment?: boolean;
|
|
30
|
+
timeout?: number;
|
|
31
|
+
}
|
|
32
|
+
export type SpawnResolve = (value: boolean | PromiseLike<boolean>) => void;
|
|
33
|
+
/**
|
|
34
|
+
* SpawnInterface is an interface for Spawn class.
|
|
35
|
+
* @interface SpawnInterface
|
|
36
|
+
* @constructor
|
|
37
|
+
* @param {AirdropEvent} event - The event object received from the platform
|
|
38
|
+
* @param {Worker} worker - The worker thread
|
|
39
|
+
*/
|
|
40
|
+
export interface SpawnInterface {
|
|
41
|
+
event: AirdropEvent;
|
|
42
|
+
worker: Worker;
|
|
43
|
+
options: WorkerAdapterOptions | null;
|
|
44
|
+
resolve: SpawnResolve;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* SpawnFactoryInterface is an interface for Spawn class factory.
|
|
48
|
+
* Spawn class is responsible for spawning a new worker thread and managing the lifecycle of the worker.
|
|
49
|
+
* The class provides utilities to emit control events to the platform and exit the worker gracefully.
|
|
50
|
+
* In case of lambda timeout, the class emits a lambda timeout event to the platform.
|
|
51
|
+
* @interface SpawnFactoryInterface
|
|
52
|
+
* @constructor
|
|
53
|
+
* @param {AirdropEvent} event - The event object received from the platform
|
|
54
|
+
* @param {object=} initialState - The initial state of the adapter
|
|
55
|
+
* @param {string} workerPath - The path to the worker file
|
|
56
|
+
* @param {WorkerAdapterOptions} options - The options to create a new instance of Spawn class
|
|
57
|
+
*/
|
|
58
|
+
export interface SpawnFactoryInterface<ConnectorState> {
|
|
59
|
+
event: AirdropEvent;
|
|
60
|
+
initialState: ConnectorState;
|
|
61
|
+
workerPath?: string;
|
|
62
|
+
options?: WorkerAdapterOptions;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* TaskAdapterInterface is an interface for TaskAdapter class.
|
|
66
|
+
* @interface TaskAdapterInterface
|
|
67
|
+
* @constructor
|
|
68
|
+
* @param {WorkerAdapter} adapter - The adapter object
|
|
69
|
+
*/
|
|
70
|
+
export interface TaskAdapterInterface<ConnectorState> {
|
|
71
|
+
adapter: WorkerAdapter<ConnectorState>;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* ProcessTaskInterface is an interface for ProcessTask class.
|
|
75
|
+
* @interface ProcessTaskInterface
|
|
76
|
+
* @constructor
|
|
77
|
+
* @param {function} task - The task to be executed, returns exit code
|
|
78
|
+
* @param {function} onTimeout - The task to be executed on timeout, returns exit code
|
|
79
|
+
*/
|
|
80
|
+
export interface ProcessTaskInterface<ConnectorState> {
|
|
81
|
+
task: (params: TaskAdapterInterface<ConnectorState>) => void;
|
|
82
|
+
onTimeout: (params: TaskAdapterInterface<ConnectorState>) => void;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* WorkerEvent represents the standard worker events.
|
|
86
|
+
*/
|
|
87
|
+
export declare enum WorkerEvent {
|
|
88
|
+
WorkerMessage = "message",
|
|
89
|
+
WorkerOnline = "online",
|
|
90
|
+
WorkerError = "error",
|
|
91
|
+
WorkerExit = "exit"
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* WorkerMessageSubject represents the handled worker message subjects.
|
|
95
|
+
*/
|
|
96
|
+
export declare enum WorkerMessageSubject {
|
|
97
|
+
WorkerMessageEmitted = "emit",
|
|
98
|
+
WorkerMessageDone = "done",
|
|
99
|
+
WorkerMessageExit = "exit",
|
|
100
|
+
WorkerMessageLog = "log"
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* WorkerMessageEmitted interface represents the structure of the emitted worker message.
|
|
104
|
+
*/
|
|
105
|
+
export interface WorkerMessageEmitted {
|
|
106
|
+
subject: WorkerMessageSubject.WorkerMessageEmitted;
|
|
107
|
+
payload: {
|
|
108
|
+
eventType: ExtractorEventType;
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* WorkerMessageDone interface represents the structure of the done worker message.
|
|
113
|
+
*/
|
|
114
|
+
export interface WorkerMessageDone {
|
|
115
|
+
subject: WorkerMessageSubject.WorkerMessageDone;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* WorkerMessageExit interface represents the structure of the exit worker message.
|
|
119
|
+
*/
|
|
120
|
+
export interface WorkerMessageExit {
|
|
121
|
+
subject: WorkerMessageSubject.WorkerMessageExit;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* WorkerMessage represents the structure of the worker message.
|
|
125
|
+
*/
|
|
126
|
+
export type WorkerMessage = WorkerMessageDone | WorkerMessageEmitted | WorkerMessageExit;
|
|
127
|
+
/**
|
|
128
|
+
* WorkerData represents the structure of the worker data object.
|
|
129
|
+
*/
|
|
130
|
+
export interface WorkerData<ConnectorState> {
|
|
131
|
+
event: AirdropEvent;
|
|
132
|
+
initialState: ConnectorState;
|
|
133
|
+
workerPath: string;
|
|
134
|
+
options?: WorkerAdapterOptions;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* GetWorkerPathInterface is an interface for getting the worker path.
|
|
138
|
+
*/
|
|
139
|
+
export interface GetWorkerPathInterface {
|
|
140
|
+
event: AirdropEvent;
|
|
141
|
+
connectorWorkerPath?: string | null;
|
|
142
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WorkerMessageSubject = exports.WorkerEvent = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* WorkerEvent represents the standard worker events.
|
|
6
|
+
*/
|
|
7
|
+
var WorkerEvent;
|
|
8
|
+
(function (WorkerEvent) {
|
|
9
|
+
WorkerEvent["WorkerMessage"] = "message";
|
|
10
|
+
WorkerEvent["WorkerOnline"] = "online";
|
|
11
|
+
WorkerEvent["WorkerError"] = "error";
|
|
12
|
+
WorkerEvent["WorkerExit"] = "exit";
|
|
13
|
+
})(WorkerEvent || (exports.WorkerEvent = WorkerEvent = {}));
|
|
14
|
+
/**
|
|
15
|
+
* WorkerMessageSubject represents the handled worker message subjects.
|
|
16
|
+
*/
|
|
17
|
+
var WorkerMessageSubject;
|
|
18
|
+
(function (WorkerMessageSubject) {
|
|
19
|
+
WorkerMessageSubject["WorkerMessageEmitted"] = "emit";
|
|
20
|
+
WorkerMessageSubject["WorkerMessageDone"] = "done";
|
|
21
|
+
WorkerMessageSubject["WorkerMessageExit"] = "exit";
|
|
22
|
+
WorkerMessageSubject["WorkerMessageLog"] = "log";
|
|
23
|
+
})(WorkerMessageSubject || (exports.WorkerMessageSubject = WorkerMessageSubject = {}));
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { UploadResponse, StreamAttachmentsResponse, UploaderFactoryInterface } from './uploader.interfaces';
|
|
2
|
+
export declare class Uploader {
|
|
3
|
+
private event;
|
|
4
|
+
private betaDevrevSdk;
|
|
5
|
+
private isLocalDevelopment?;
|
|
6
|
+
constructor({ event, options }: UploaderFactoryInterface);
|
|
7
|
+
/**
|
|
8
|
+
* Uploads the fetched objects to the DevRev platform.
|
|
9
|
+
* The fetched objects are uploaded to the platform and the artifact information is returned.
|
|
10
|
+
* @param {string} filename - The name of the file to be uploaded
|
|
11
|
+
* @param {string} itemType - The type of the item to be uploaded
|
|
12
|
+
* @param {object[] | object} fetchedObjects - The fetched objects to be uploaded
|
|
13
|
+
* @returns {Promise<UploadResponse>} - The response object containing the artifact information
|
|
14
|
+
* or error information if there was an error
|
|
15
|
+
*/
|
|
16
|
+
upload(itemType: string, fetchedObjects: object[] | object): Promise<UploadResponse>;
|
|
17
|
+
private prepareArtifact;
|
|
18
|
+
private uploadToArtifact;
|
|
19
|
+
private streamToArtifact;
|
|
20
|
+
/**
|
|
21
|
+
* Streams the attachments to the DevRev platform.
|
|
22
|
+
* The attachments are streamed to the platform and the artifact information is returned.
|
|
23
|
+
* @param {string} attachmentsMetadataArtifactId - The artifact ID of the attachments metadata
|
|
24
|
+
* @returns {Promise<UploadResponse>} - The response object containing the ssoAttachment artifact information
|
|
25
|
+
* or error information if there was an error
|
|
26
|
+
*/
|
|
27
|
+
streamAttachments({ attachmentsMetadataArtifactId, }: {
|
|
28
|
+
attachmentsMetadataArtifactId: string;
|
|
29
|
+
}): Promise<StreamAttachmentsResponse>;
|
|
30
|
+
private getArtifactDownloadUrl;
|
|
31
|
+
private downloadArtifact;
|
|
32
|
+
private compressGzip;
|
|
33
|
+
private decompressGzip;
|
|
34
|
+
private parseJsonl;
|
|
35
|
+
private stream;
|
|
36
|
+
private getFileStreamResponse;
|
|
37
|
+
private downloadToLocal;
|
|
38
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { AirdropEvent } from '../types/extraction';
|
|
2
|
+
import { WorkerAdapterOptions } from '../types/workers';
|
|
3
|
+
import { ErrorRecord } from '../types/common';
|
|
4
|
+
export interface UploaderFactoryInterface {
|
|
5
|
+
event: AirdropEvent;
|
|
6
|
+
options?: WorkerAdapterOptions;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Artifact is an interface that defines the structure of an artifact. Artifact is a file that is generated by the extractor and uploaded to ADaaS.
|
|
10
|
+
*/
|
|
11
|
+
export interface Artifact {
|
|
12
|
+
id: string;
|
|
13
|
+
item_type: string;
|
|
14
|
+
item_count: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* ArtifactsPrepareResponse is an interface that defines the structure of the response from the prepare artifacts endpoint.
|
|
18
|
+
* @deprecated
|
|
19
|
+
*/
|
|
20
|
+
export interface ArtifactsPrepareResponse {
|
|
21
|
+
url: string;
|
|
22
|
+
id: string;
|
|
23
|
+
form_data: {
|
|
24
|
+
key: string;
|
|
25
|
+
value: string;
|
|
26
|
+
}[];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* UploadResponse is an interface that defines the structure of the response from upload through Uploader.
|
|
30
|
+
*/
|
|
31
|
+
export interface UploadResponse {
|
|
32
|
+
artifact?: Artifact;
|
|
33
|
+
error?: ErrorRecord;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* StreamAttachmentsResponse is an interface that defines the structure of the response from the stream attachments through Uploader.
|
|
37
|
+
*/
|
|
38
|
+
export interface StreamAttachmentsResponse {
|
|
39
|
+
ssorAttachments?: SsorAttachment[];
|
|
40
|
+
error?: ErrorRecord;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* StreamResponse is an interface that defines the structure of the response from the stream of single attachment through Uploader.
|
|
44
|
+
*/
|
|
45
|
+
export interface StreamResponse {
|
|
46
|
+
ssorAttachment?: SsorAttachment;
|
|
47
|
+
error?: ErrorRecord;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* SsorAttachment is an interface that defines the structure of the SSOR attachment.
|
|
51
|
+
*/
|
|
52
|
+
export interface SsorAttachment {
|
|
53
|
+
id: {
|
|
54
|
+
devrev: string;
|
|
55
|
+
external: string;
|
|
56
|
+
};
|
|
57
|
+
parent_id: {
|
|
58
|
+
external: string;
|
|
59
|
+
};
|
|
60
|
+
actor_id: {
|
|
61
|
+
external: string;
|
|
62
|
+
};
|
|
63
|
+
}
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.Uploader = void 0;
|
|
30
|
+
const axios_1 = __importDefault(require("axios"));
|
|
31
|
+
const fs_1 = __importStar(require("fs"));
|
|
32
|
+
const zlib_1 = __importDefault(require("zlib"));
|
|
33
|
+
const js_jsonl_1 = require("js-jsonl");
|
|
34
|
+
const form_data_1 = __importDefault(require("form-data"));
|
|
35
|
+
const typescript_sdk_1 = require("@devrev/typescript-sdk");
|
|
36
|
+
const constants_1 = require("../common/constants");
|
|
37
|
+
const logger_1 = require("../logger/logger");
|
|
38
|
+
class Uploader {
|
|
39
|
+
constructor({ event, options }) {
|
|
40
|
+
this.event = event;
|
|
41
|
+
this.betaDevrevSdk = typescript_sdk_1.client.setupBeta({
|
|
42
|
+
endpoint: event.execution_metadata.devrev_endpoint,
|
|
43
|
+
token: event.context.secrets.service_account_token,
|
|
44
|
+
});
|
|
45
|
+
this.isLocalDevelopment = options === null || options === void 0 ? void 0 : options.isLocalDevelopment;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Uploads the fetched objects to the DevRev platform.
|
|
49
|
+
* The fetched objects are uploaded to the platform and the artifact information is returned.
|
|
50
|
+
* @param {string} filename - The name of the file to be uploaded
|
|
51
|
+
* @param {string} itemType - The type of the item to be uploaded
|
|
52
|
+
* @param {object[] | object} fetchedObjects - The fetched objects to be uploaded
|
|
53
|
+
* @returns {Promise<UploadResponse>} - The response object containing the artifact information
|
|
54
|
+
* or error information if there was an error
|
|
55
|
+
*/
|
|
56
|
+
async upload(itemType, fetchedObjects) {
|
|
57
|
+
if (this.isLocalDevelopment) {
|
|
58
|
+
this.downloadToLocal(itemType, fetchedObjects);
|
|
59
|
+
}
|
|
60
|
+
// 1. Compress the fetched objects to a gzipped jsonl object
|
|
61
|
+
const file = await this.compressGzip(js_jsonl_1.jsonl.stringify(fetchedObjects));
|
|
62
|
+
if (!file) {
|
|
63
|
+
return {
|
|
64
|
+
error: { message: 'Error while compressing jsonl object.' },
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
const filename = itemType + '.jsonl.gz';
|
|
68
|
+
const fileType = 'application/x-gzip';
|
|
69
|
+
// 2. Prepare the artifact for uploading
|
|
70
|
+
const preparedArtifact = await this.prepareArtifact(filename, fileType);
|
|
71
|
+
if (!preparedArtifact) {
|
|
72
|
+
return {
|
|
73
|
+
error: { message: 'Error while preparing artifact.' },
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
// 3. Upload the file to the prepared artifact
|
|
77
|
+
const uploadedArtifact = await this.uploadToArtifact(preparedArtifact, file);
|
|
78
|
+
if (!uploadedArtifact) {
|
|
79
|
+
return {
|
|
80
|
+
error: { message: 'Error while uploading artifact.' },
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
// 4. Return the artifact information to the platform
|
|
84
|
+
const artifact = {
|
|
85
|
+
id: preparedArtifact.id,
|
|
86
|
+
item_type: itemType,
|
|
87
|
+
item_count: Array.isArray(fetchedObjects) ? fetchedObjects.length : 1,
|
|
88
|
+
};
|
|
89
|
+
console.log('Successful upload of artifact: ', artifact);
|
|
90
|
+
return { artifact };
|
|
91
|
+
}
|
|
92
|
+
async prepareArtifact(filename, fileType) {
|
|
93
|
+
try {
|
|
94
|
+
const response = await this.betaDevrevSdk.artifactsPrepare({
|
|
95
|
+
file_name: filename,
|
|
96
|
+
file_type: fileType,
|
|
97
|
+
});
|
|
98
|
+
return response.data;
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
if (axios_1.default.isAxiosError(error)) {
|
|
102
|
+
console.error('Error while preparing artifact.', (0, logger_1.formatAxiosError)(error));
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
console.error('Error while preparing artifact.', error);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async uploadToArtifact(preparedArtifact, file
|
|
110
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
111
|
+
) {
|
|
112
|
+
const formData = new form_data_1.default();
|
|
113
|
+
for (const field of preparedArtifact.form_data) {
|
|
114
|
+
formData.append(field.key, field.value);
|
|
115
|
+
}
|
|
116
|
+
formData.append('file', file);
|
|
117
|
+
try {
|
|
118
|
+
const response = await axios_1.default.post(preparedArtifact.url, formData, {
|
|
119
|
+
headers: Object.assign({}, formData.getHeaders()),
|
|
120
|
+
});
|
|
121
|
+
return response;
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
if (axios_1.default.isAxiosError(error)) {
|
|
125
|
+
console.error('Error while uploading artifact.', (0, logger_1.formatAxiosError)(error));
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
console.error('Error while uploading artifact.', error);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
async streamToArtifact(preparedArtifact,
|
|
133
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
134
|
+
fileStreamResponse) {
|
|
135
|
+
const formData = new form_data_1.default();
|
|
136
|
+
for (const field of preparedArtifact.form_data) {
|
|
137
|
+
formData.append(field.key, field.value);
|
|
138
|
+
}
|
|
139
|
+
formData.append('file', fileStreamResponse.data);
|
|
140
|
+
try {
|
|
141
|
+
const response = await axios_1.default.post(preparedArtifact.url, formData, {
|
|
142
|
+
headers: Object.assign(Object.assign({}, formData.getHeaders()), (!fileStreamResponse.headers['content-length'] && {
|
|
143
|
+
'Content-Length': constants_1.MAX_DEVREV_ARTIFACT_SIZE,
|
|
144
|
+
})),
|
|
145
|
+
});
|
|
146
|
+
return response;
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
if (axios_1.default.isAxiosError(error)) {
|
|
150
|
+
console.error('Error while streaming artifact.', (0, logger_1.formatAxiosError)(error));
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
console.error('Error while streaming artifact.', error);
|
|
154
|
+
}
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Streams the attachments to the DevRev platform.
|
|
160
|
+
* The attachments are streamed to the platform and the artifact information is returned.
|
|
161
|
+
* @param {string} attachmentsMetadataArtifactId - The artifact ID of the attachments metadata
|
|
162
|
+
* @returns {Promise<UploadResponse>} - The response object containing the ssoAttachment artifact information
|
|
163
|
+
* or error information if there was an error
|
|
164
|
+
*/
|
|
165
|
+
async streamAttachments({ attachmentsMetadataArtifactId, }) {
|
|
166
|
+
console.log('Started streaming attachments to the platform.');
|
|
167
|
+
// 1. Get the URL of the attachments metadata artifact
|
|
168
|
+
const artifactUrl = await this.getArtifactDownloadUrl(attachmentsMetadataArtifactId);
|
|
169
|
+
if (!artifactUrl) {
|
|
170
|
+
return {
|
|
171
|
+
error: { message: 'Error while getting artifact download URL.' },
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
// 2. Download artifact from the URL
|
|
175
|
+
const gzippedJsonlObject = await this.downloadArtifact(artifactUrl);
|
|
176
|
+
if (!gzippedJsonlObject) {
|
|
177
|
+
return {
|
|
178
|
+
error: { message: 'Error while downloading gzipped jsonl object.' },
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
// 3. Decompress the gzipped jsonl object
|
|
182
|
+
const jsonlObject = await this.decompressGzip(gzippedJsonlObject);
|
|
183
|
+
if (!jsonlObject) {
|
|
184
|
+
return {
|
|
185
|
+
error: { message: 'Error while decompressing gzipped jsonl object.' },
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
// 4. Parse the jsonl object to get the attachment metadata
|
|
189
|
+
const jsonObject = (await this.parseJsonl(jsonlObject));
|
|
190
|
+
if (!jsonObject) {
|
|
191
|
+
return {
|
|
192
|
+
error: { message: 'Error while parsing jsonl object.' },
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
// 5. Stream each attachment to the platform, and push the ssorAttachment to the ssorAttachments array
|
|
196
|
+
const ssorAttachments = [];
|
|
197
|
+
for (const attachmentMetadata of jsonObject) {
|
|
198
|
+
const { ssorAttachment, error } = await this.stream(attachmentMetadata);
|
|
199
|
+
if (error || !ssorAttachment) {
|
|
200
|
+
console.warn('Error while streaming attachment', error);
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
ssorAttachments.push(ssorAttachment);
|
|
204
|
+
}
|
|
205
|
+
if (!ssorAttachments.length) {
|
|
206
|
+
console.warn('No attachments were streamed to the platform.');
|
|
207
|
+
return {
|
|
208
|
+
error: { message: 'No attachments were streamed to the platform.' },
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
return { ssorAttachments };
|
|
212
|
+
}
|
|
213
|
+
async getArtifactDownloadUrl(artifactId) {
|
|
214
|
+
try {
|
|
215
|
+
const response = await this.betaDevrevSdk.artifactsLocate({
|
|
216
|
+
id: artifactId,
|
|
217
|
+
});
|
|
218
|
+
return response.data.url;
|
|
219
|
+
}
|
|
220
|
+
catch (error) {
|
|
221
|
+
console.error('Error while getting artifact download URL.', error);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
async downloadArtifact(artifactUrl) {
|
|
225
|
+
try {
|
|
226
|
+
const response = await axios_1.default.get(artifactUrl, {
|
|
227
|
+
responseType: 'arraybuffer',
|
|
228
|
+
});
|
|
229
|
+
return response.data;
|
|
230
|
+
}
|
|
231
|
+
catch (error) {
|
|
232
|
+
if (axios_1.default.isAxiosError(error)) {
|
|
233
|
+
console.error('Error while downloading artifact from URL.', (0, logger_1.formatAxiosError)(error));
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
console.error('Error while downloading artifact from URL.', error);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
async compressGzip(jsonlObject) {
|
|
241
|
+
try {
|
|
242
|
+
return zlib_1.default.gzipSync(jsonlObject);
|
|
243
|
+
}
|
|
244
|
+
catch (error) {
|
|
245
|
+
console.error('Error while compressing jsonl object.', error);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
async decompressGzip(gzippedJsonlObject) {
|
|
249
|
+
try {
|
|
250
|
+
const jsonlObject = await zlib_1.default.gunzipSync(gzippedJsonlObject);
|
|
251
|
+
return jsonlObject.toString();
|
|
252
|
+
}
|
|
253
|
+
catch (error) {
|
|
254
|
+
console.error('Error while decompressing gzipped jsonl object.', error);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
async parseJsonl(jsonlObject) {
|
|
258
|
+
try {
|
|
259
|
+
return js_jsonl_1.jsonl.parse(jsonlObject);
|
|
260
|
+
}
|
|
261
|
+
catch (error) {
|
|
262
|
+
console.error('Error while parsing jsonl object.', error);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
async stream(attachmentMetadata) {
|
|
266
|
+
var _a;
|
|
267
|
+
const { id: externalId, file_name: filename, url, parent_id: parentId, author_id: actorId, } = attachmentMetadata;
|
|
268
|
+
const fileStreamResponse = await this.getFileStreamResponse(url);
|
|
269
|
+
if (!fileStreamResponse) {
|
|
270
|
+
return {
|
|
271
|
+
error: { message: 'Error while fetching attachment from URL' },
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
const fileType = ((_a = fileStreamResponse.headers) === null || _a === void 0 ? void 0 : _a['content-type']) ||
|
|
275
|
+
'application/octet-stream';
|
|
276
|
+
const preparedArtifact = await this.prepareArtifact(filename, fileType);
|
|
277
|
+
if (!preparedArtifact) {
|
|
278
|
+
return {
|
|
279
|
+
error: { message: 'Error while preparing artifact.' },
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
const uploadedArtifact = await this.streamToArtifact(preparedArtifact, fileStreamResponse);
|
|
283
|
+
if (!uploadedArtifact) {
|
|
284
|
+
return {
|
|
285
|
+
error: { message: 'Error while streaming artifact.' },
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
const ssorAttachment = {
|
|
289
|
+
id: {
|
|
290
|
+
devrev: preparedArtifact.id,
|
|
291
|
+
external: externalId,
|
|
292
|
+
},
|
|
293
|
+
parent_id: {
|
|
294
|
+
external: parentId,
|
|
295
|
+
},
|
|
296
|
+
actor_id: {
|
|
297
|
+
external: actorId,
|
|
298
|
+
},
|
|
299
|
+
};
|
|
300
|
+
console.log('Successful stream of attachment: ', ssorAttachment);
|
|
301
|
+
return { ssorAttachment };
|
|
302
|
+
}
|
|
303
|
+
async getFileStreamResponse(url) {
|
|
304
|
+
try {
|
|
305
|
+
const fileStreamResponse = await axios_1.default.get(url, {
|
|
306
|
+
responseType: 'stream',
|
|
307
|
+
headers: {
|
|
308
|
+
Authorization: this.event.payload.connection_data.key,
|
|
309
|
+
},
|
|
310
|
+
});
|
|
311
|
+
return fileStreamResponse;
|
|
312
|
+
}
|
|
313
|
+
catch (error) {
|
|
314
|
+
if (axios_1.default.isAxiosError(error)) {
|
|
315
|
+
console.error('Error while fetching attachment from URL.', (0, logger_1.formatAxiosError)(error));
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
console.error('Error while fetching attachment from URL.', error);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
async downloadToLocal(itemType, fetchedObjects) {
|
|
323
|
+
console.log(`Downloading ${itemType} to local file system.`);
|
|
324
|
+
try {
|
|
325
|
+
if (!fs_1.default.existsSync('extracted_files')) {
|
|
326
|
+
fs_1.default.mkdirSync('extracted_files');
|
|
327
|
+
}
|
|
328
|
+
const timestamp = new Date().getTime();
|
|
329
|
+
const filePath = `extracted_files/extractor_${itemType}_${timestamp}.${itemType === 'external_domain_metadata' ? 'json' : 'jsonl'}`;
|
|
330
|
+
const fileHandle = await fs_1.promises.open(filePath, 'w');
|
|
331
|
+
let objArray = [];
|
|
332
|
+
if (!Array.isArray(fetchedObjects)) {
|
|
333
|
+
objArray.push(fetchedObjects);
|
|
334
|
+
}
|
|
335
|
+
else {
|
|
336
|
+
objArray = fetchedObjects;
|
|
337
|
+
}
|
|
338
|
+
for (const jsonObject of objArray) {
|
|
339
|
+
const jsonLine = JSON.stringify(jsonObject) + '\n';
|
|
340
|
+
await fileHandle.write(jsonLine);
|
|
341
|
+
}
|
|
342
|
+
await fileHandle.close();
|
|
343
|
+
console.log('Data successfully written to', filePath);
|
|
344
|
+
}
|
|
345
|
+
catch (error) {
|
|
346
|
+
console.error('Error writing data to file.', error);
|
|
347
|
+
return Promise.reject(error);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
exports.Uploader = Uploader;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const
|
|
3
|
+
const test_helpers_1 = require("../tests/test-helpers");
|
|
4
|
+
const types_1 = require("../types");
|
|
5
|
+
const uploader_1 = require("./uploader");
|
|
4
6
|
// mock uploader.upload method
|
|
5
|
-
jest.mock('
|
|
7
|
+
jest.mock('./uploader', () => {
|
|
6
8
|
return {
|
|
7
9
|
Uploader: jest.fn().mockImplementation(() => {
|
|
8
10
|
return {
|
|
@@ -15,12 +17,12 @@ jest.mock('../src/uploader', () => {
|
|
|
15
17
|
};
|
|
16
18
|
});
|
|
17
19
|
describe('uploader.ts', () => {
|
|
18
|
-
const
|
|
20
|
+
const mockEvent = (0, test_helpers_1.createEvent)({ eventType: types_1.EventType.ExtractionDataStart });
|
|
21
|
+
const uploader = new uploader_1.Uploader({ event: mockEvent });
|
|
19
22
|
it('should upload the file to the DevRev platform and return the artifact information', async () => {
|
|
20
|
-
const filename = 'filename';
|
|
21
23
|
const entity = 'entity';
|
|
22
24
|
const fetchedObjects = [{ key: 'value' }];
|
|
23
|
-
const uploadResponse = await uploader.upload(
|
|
25
|
+
const uploadResponse = await uploader.upload(entity, fetchedObjects);
|
|
24
26
|
expect(uploadResponse).toEqual({
|
|
25
27
|
artifact: { key: 'value' },
|
|
26
28
|
error: undefined,
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createWorker = createWorker;
|
|
4
|
+
const node_worker_threads_1 = require("node:worker_threads");
|
|
5
|
+
const workers_1 = require("../types/workers");
|
|
6
|
+
const logger_1 = require("../logger/logger");
|
|
7
|
+
async function createWorker(workerData) {
|
|
8
|
+
return new Promise((resolve, reject) => {
|
|
9
|
+
if (node_worker_threads_1.isMainThread) {
|
|
10
|
+
const logger = new logger_1.Logger(workerData.event);
|
|
11
|
+
const workerFile = __dirname + '/worker.js';
|
|
12
|
+
const worker = new node_worker_threads_1.Worker(workerFile, {
|
|
13
|
+
workerData,
|
|
14
|
+
});
|
|
15
|
+
worker.on(workers_1.WorkerEvent.WorkerError, (error) => {
|
|
16
|
+
logger.error('Worker error', error);
|
|
17
|
+
reject(error);
|
|
18
|
+
});
|
|
19
|
+
worker.on(workers_1.WorkerEvent.WorkerOnline, () => {
|
|
20
|
+
resolve(worker);
|
|
21
|
+
logger.info('Worker is online. Started processing the task with event type: ' +
|
|
22
|
+
workerData.event.payload.event_type +
|
|
23
|
+
'.');
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
reject(new Error('Worker threads can not start more worker threads.'));
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
}
|