@devrev/ts-adaas 1.2.6 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,3 @@
1
- import { FunctionInput } from '@devrev/typescript-sdk/dist/snap-ins';
1
+ import { AirdropEvent } from '../types/extraction';
2
2
  import { InitialDomainMapping } from '../types/common';
3
- export declare function installInitialDomainMapping(event: FunctionInput, initialDomainMappingJson: InitialDomainMapping): Promise<void>;
3
+ export declare function installInitialDomainMapping(event: AirdropEvent, initialDomainMappingJson: InitialDomainMapping): Promise<void>;
@@ -1,3 +1,26 @@
1
+ /**
2
+ * Axios client setup with retry capabilities using axios-retry.
3
+ *
4
+ * This module exports an Axios client instance (`axiosClient`) that is configured to automatically retry
5
+ * failed requests under certain conditions.
6
+ *
7
+ * Retry Conditions:
8
+ * 1. Network errors (where no response is received).
9
+ * 2. Idempotent requests (defaults include GET, HEAD, OPTIONS, PUT).
10
+ * 3. All 5xx server errors.
11
+ *
12
+ * Retry Strategy:
13
+ * - A maximum of 5 retries are attempted.
14
+ * - Exponential backoff delay is applied between retries, increasing with each retry attempt.
15
+ *
16
+ * Additional Features:
17
+ * - When the maximum number of retry attempts is reached, sensitive headers (like authorization)
18
+ * are removed from error logs for security reasons.
19
+ *
20
+ * Exported:
21
+ * - `axios`: Original axios instance for additional customizations or direct use.
22
+ * - `axiosClient`: Configured axios instance with retry logic.
23
+ */
1
24
  import axios from 'axios';
2
25
  declare const axiosClient: import("axios").AxiosInstance;
3
26
  export { axios, axiosClient };
@@ -1,4 +1,27 @@
1
1
  "use strict";
2
+ /**
3
+ * Axios client setup with retry capabilities using axios-retry.
4
+ *
5
+ * This module exports an Axios client instance (`axiosClient`) that is configured to automatically retry
6
+ * failed requests under certain conditions.
7
+ *
8
+ * Retry Conditions:
9
+ * 1. Network errors (where no response is received).
10
+ * 2. Idempotent requests (defaults include GET, HEAD, OPTIONS, PUT).
11
+ * 3. All 5xx server errors.
12
+ *
13
+ * Retry Strategy:
14
+ * - A maximum of 5 retries are attempted.
15
+ * - Exponential backoff delay is applied between retries, increasing with each retry attempt.
16
+ *
17
+ * Additional Features:
18
+ * - When the maximum number of retry attempts is reached, sensitive headers (like authorization)
19
+ * are removed from error logs for security reasons.
20
+ *
21
+ * Exported:
22
+ * - `axios`: Original axios instance for additional customizations or direct use.
23
+ * - `axiosClient`: Configured axios instance with retry logic.
24
+ */
2
25
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
27
  };
@@ -19,9 +42,10 @@ exports.axiosClient = axiosClient;
19
42
  return delay;
20
43
  },
21
44
  retryCondition: (error) => {
22
- var _a;
23
- return (axios_retry_1.default.isNetworkOrIdempotentRequestError(error) &&
24
- ((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) !== 429);
45
+ var _a, _b, _c;
46
+ return ((axios_retry_1.default.isNetworkOrIdempotentRequestError(error) &&
47
+ ((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) !== 429) ||
48
+ ((_c = (_b = error.response) === null || _b === void 0 ? void 0 : _b.status) !== null && _c !== void 0 ? _c : 0) >= 500);
25
49
  },
26
50
  onMaxRetryTimesExceeded(error) {
27
51
  var _a, _b, _c, _d;
@@ -1,6 +1,6 @@
1
1
  import { ErrorRecord } from '../types/common';
2
2
  import { AdapterState, StateInterface } from './state.interfaces';
3
- export declare function createAdapterState<ConnectorState>({ event, initialState, options, }: StateInterface<ConnectorState>): Promise<State<ConnectorState>>;
3
+ export declare function createAdapterState<ConnectorState>({ event, initialState, initialDomainMapping, options, }: StateInterface<ConnectorState>): Promise<State<ConnectorState>>;
4
4
  export declare class State<ConnectorState> {
5
5
  private _state;
6
6
  private initialSdkState;
@@ -1,14 +1,17 @@
1
1
  import { AirdropEvent } from '../types/extraction';
2
2
  import { FileToLoad } from '../types/loading';
3
3
  import { WorkerAdapterOptions } from '../types/workers';
4
+ import { InitialDomainMapping } from '../types/common';
4
5
  export interface SdkState {
5
6
  lastSyncStarted?: string;
6
7
  lastSuccessfulSyncStarted?: string;
7
8
  toDevRev?: ToDevRev;
8
9
  fromDevRev?: FromDevRev;
10
+ snapInVersionId?: string;
9
11
  }
10
12
  /**
11
- * AdapterState is an interface that defines the structure of the adapter state that is used by the external extractor. It extends the connector state with additional fields: lastSyncStarted, lastSuccessfulSyncStarted, and attachmentsMetadata.
13
+ * AdapterState is an interface that defines the structure of the adapter state that is used by the external extractor.
14
+ * It extends the connector state with additional fields: lastSyncStarted, lastSuccessfulSyncStarted, snapInVersionId and attachmentsMetadata.
12
15
  */
13
16
  export type AdapterState<ConnectorState> = ConnectorState & SdkState;
14
17
  export interface ToDevRev {
@@ -23,5 +26,6 @@ export interface FromDevRev {
23
26
  export interface StateInterface<ConnectorState> {
24
27
  event: AirdropEvent;
25
28
  initialState: ConnectorState;
29
+ initialDomainMapping?: InitialDomainMapping;
26
30
  options?: WorkerAdapterOptions;
27
31
  }
@@ -6,16 +6,36 @@ const axios_client_1 = require("../http/axios-client");
6
6
  const extraction_1 = require("../types/extraction");
7
7
  const constants_1 = require("../common/constants");
8
8
  const logger_1 = require("../logger/logger");
9
+ const install_initial_domain_mapping_1 = require("../common/install-initial-domain-mapping");
9
10
  const helpers_1 = require("../common/helpers");
10
- async function createAdapterState({ event, initialState, options, }) {
11
+ async function createAdapterState({ event, initialState, initialDomainMapping, options, }) {
12
+ var _a;
11
13
  const newInitialState = structuredClone(initialState);
12
14
  const as = new State({
13
15
  event,
14
16
  initialState: newInitialState,
17
+ initialDomainMapping,
15
18
  options,
16
19
  });
17
20
  if (!constants_1.STATELESS_EVENT_TYPES.includes(event.payload.event_type)) {
18
21
  await as.fetchState(newInitialState);
22
+ const snapInVersionId = event.context.snap_in_version_id;
23
+ const hasSnapInVersionInState = 'snapInVersionId' in as.state;
24
+ const shouldUpdateIDM = !hasSnapInVersionInState || as.state.snapInVersionId !== snapInVersionId;
25
+ if (shouldUpdateIDM) {
26
+ console.log(`Snap-in version in state (${(_a = as.state) === null || _a === void 0 ? void 0 : _a.snapInVersionId}) differs from the version in event context (${snapInVersionId}) - initial domain mapping needs to be updated.`);
27
+ if (initialDomainMapping) {
28
+ await (0, install_initial_domain_mapping_1.installInitialDomainMapping)(event, initialDomainMapping);
29
+ as.state.snapInVersionId = snapInVersionId;
30
+ console.log('Successfully installed new initial domain mapping.');
31
+ }
32
+ else {
33
+ console.warn('No initial domain mapping was passed to spawn function. Skipping initial domain mapping installation.');
34
+ }
35
+ }
36
+ else {
37
+ console.log(`Snap-in version in state matches the version in event context (${snapInVersionId}). Skipping initial domain mapping installation.`);
38
+ }
19
39
  if (event.payload.event_type === extraction_1.EventType.ExtractionDataStart &&
20
40
  !as.state.lastSyncStarted) {
21
41
  as.state.lastSyncStarted = new Date().toISOString();
@@ -29,6 +49,7 @@ class State {
29
49
  this.initialSdkState =
30
50
  (0, helpers_1.getSyncDirection)({ event }) === extraction_1.SyncMode.LOADING
31
51
  ? {
52
+ snapInVersionId: '',
32
53
  fromDevRev: {
33
54
  filesToLoad: [],
34
55
  },
@@ -36,6 +57,7 @@ class State {
36
57
  : {
37
58
  lastSyncStarted: '',
38
59
  lastSuccessfulSyncStarted: '',
60
+ snapInVersionId: '',
39
61
  toDevRev: {
40
62
  attachmentsMetadata: {
41
63
  artifactIds: [],
@@ -96,7 +118,7 @@ class State {
96
118
  this.event.payload.event_context.sync_unit_id +
97
119
  '.');
98
120
  try {
99
- const response = await axios_client_1.axiosClient.post(this.workerUrl + '.get', {}, {
121
+ const response = await axios_client_1.axiosClient.get(this.workerUrl + '.get', {
100
122
  headers: {
101
123
  Authorization: this.devrevToken,
102
124
  },
@@ -10,7 +10,7 @@ function createEvent({ eventType, externalSyncUnits = [], progress, error, delay
10
10
  return {
11
11
  context: Object.assign({ secrets: {
12
12
  service_account_token: 'test_token',
13
- }, snap_in_version_id: 'test_snap_in_version_id' }, contextOverrides),
13
+ }, snap_in_version_id: 'test_snap_in_version_id', snap_in_id: 'test_snap_in_id' }, contextOverrides),
14
14
  payload: Object.assign({ connection_data: {
15
15
  org_id: 'test_org_id',
16
16
  org_name: 'test_org_name',
@@ -197,6 +197,7 @@ export interface AirdropEvent {
197
197
  service_account_token: string;
198
198
  };
199
199
  snap_in_version_id: string;
200
+ snap_in_id: string;
200
201
  };
201
202
  payload: AirdropMessage;
202
203
  execution_metadata: {
@@ -3,6 +3,7 @@ import { State } from '../state/state';
3
3
  import { WorkerAdapter } from '../workers/worker-adapter';
4
4
  import { ExtractorEventType, AirdropEvent } from './extraction';
5
5
  import { LoaderEventType } from './loading';
6
+ import { InitialDomainMapping } from './common';
6
7
  /**
7
8
  * WorkerAdapterInterface is an interface for WorkerAdapter class.
8
9
  * @interface WorkerAdapterInterface
@@ -52,6 +53,7 @@ export interface SpawnInterface {
52
53
  * @param {AirdropEvent} event - The event object received from the platform
53
54
  * @param {object=} initialState - The initial state of the adapter
54
55
  * @param {string} workerPath - The path to the worker file
56
+ * @param {string} initialDomainMapping - The initial domain mapping
55
57
  * @param {WorkerAdapterOptions} options - The options to create a new instance of Spawn class
56
58
  */
57
59
  export interface SpawnFactoryInterface<ConnectorState> {
@@ -59,6 +61,7 @@ export interface SpawnFactoryInterface<ConnectorState> {
59
61
  initialState: ConnectorState;
60
62
  workerPath?: string;
61
63
  options?: WorkerAdapterOptions;
64
+ initialDomainMapping?: InitialDomainMapping;
62
65
  }
63
66
  /**
64
67
  * TaskAdapterInterface is an interface for TaskAdapter class.
@@ -130,6 +133,7 @@ export interface WorkerData<ConnectorState> {
130
133
  event: AirdropEvent;
131
134
  initialState: ConnectorState;
132
135
  workerPath: string;
136
+ initialDomainMapping?: InitialDomainMapping;
133
137
  options?: WorkerAdapterOptions;
134
138
  }
135
139
  /**
@@ -11,11 +11,13 @@ function processTask({ task, onTimeout, }) {
11
11
  void (async () => {
12
12
  const event = node_worker_threads_1.workerData.event;
13
13
  const initialState = node_worker_threads_1.workerData.initialState;
14
+ const initialDomainMapping = node_worker_threads_1.workerData.initialDomainMapping;
14
15
  const options = node_worker_threads_1.workerData.options;
15
16
  console = new logger_1.Logger({ event, options });
16
17
  const adapterState = await (0, state_1.createAdapterState)({
17
18
  event,
18
19
  initialState,
20
+ initialDomainMapping,
19
21
  options,
20
22
  });
21
23
  if (node_worker_threads_1.parentPort && node_worker_threads_1.workerData.event) {
@@ -10,7 +10,7 @@ import { SpawnFactoryInterface, SpawnInterface } from '../types/workers';
10
10
  * @param {string} workerPath - The path to the worker file
11
11
  * @returns {Promise<Spawn>} - A new instance of Spawn class
12
12
  */
13
- export declare function spawn<ConnectorState>({ event, initialState, workerPath, options, }: SpawnFactoryInterface<ConnectorState>): Promise<void>;
13
+ export declare function spawn<ConnectorState>({ event, initialState, workerPath, initialDomainMapping, options, }: SpawnFactoryInterface<ConnectorState>): Promise<void>;
14
14
  export declare class Spawn {
15
15
  private event;
16
16
  private alreadyEmitted;
@@ -71,7 +71,7 @@ function getWorkerPath({ event, connectorWorkerPath, }) {
71
71
  * @param {string} workerPath - The path to the worker file
72
72
  * @returns {Promise<Spawn>} - A new instance of Spawn class
73
73
  */
74
- async function spawn({ event, initialState, workerPath, options, }) {
74
+ async function spawn({ event, initialState, workerPath, initialDomainMapping, options, }) {
75
75
  const logger = new logger_1.Logger({ event, options });
76
76
  const script = getWorkerPath({
77
77
  event,
@@ -91,6 +91,7 @@ async function spawn({ event, initialState, workerPath, options, }) {
91
91
  event,
92
92
  initialState,
93
93
  workerPath: script,
94
+ initialDomainMapping,
94
95
  options,
95
96
  });
96
97
  return new Promise((resolve) => {
@@ -72,7 +72,7 @@ export declare class WorkerAdapter<ConnectorState> {
72
72
  * Streams the attachments to the DevRev platform.
73
73
  * The attachments are streamed to the platform and the artifact information is returned.
74
74
  * @param {{ stream, processors }: { stream: ExternalSystemAttachmentStreamingFunction, processors?: ExternalSystemAttachmentProcessors }} Params - The parameters to stream the attachments
75
- * @returns {Promise<StreamAttachmentsReturnType>} - The response object containing the ssoAttachment artifact information
75
+ * @returns {Promise<StreamAttachmentsReturnType>} - The response object containing the ssorAttachment artifact information
76
76
  * or error information if there was an error
77
77
  */
78
78
  streamAttachments<NewBatch>({ stream, processors, }: {
@@ -117,7 +117,14 @@ class WorkerAdapter {
117
117
  }
118
118
  // We want to upload all the repos before emitting the event, except for the external sync units done event
119
119
  if (newEventType !== extraction_1.ExtractorEventType.ExtractionExternalSyncUnitsDone) {
120
- await this.uploadAllRepos();
120
+ try {
121
+ await this.uploadAllRepos();
122
+ }
123
+ catch (error) {
124
+ console.error('Error while uploading repos', error);
125
+ 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);
126
+ return;
127
+ }
121
128
  }
122
129
  // If the extraction is done, we want to save the timestamp of the last successful sync
123
130
  if (newEventType === extraction_1.ExtractorEventType.ExtractionAttachmentsDone) {
@@ -164,7 +171,10 @@ class WorkerAdapter {
164
171
  }
165
172
  async uploadAllRepos() {
166
173
  for (const repo of this.repos) {
167
- await repo.upload();
174
+ const error = await repo.upload();
175
+ if (error) {
176
+ throw error;
177
+ }
168
178
  }
169
179
  }
170
180
  handleTimeout() {
@@ -501,7 +511,7 @@ class WorkerAdapter {
501
511
  event: this.event,
502
512
  });
503
513
  if (error) {
504
- console.warn('Error while streaming attachment', error === null || error === void 0 ? void 0 : error.message);
514
+ console.warn('Error while streaming attachment', error);
505
515
  return { error };
506
516
  }
507
517
  else if (delay) {
@@ -573,27 +583,27 @@ class WorkerAdapter {
573
583
  * Streams the attachments to the DevRev platform.
574
584
  * The attachments are streamed to the platform and the artifact information is returned.
575
585
  * @param {{ stream, processors }: { stream: ExternalSystemAttachmentStreamingFunction, processors?: ExternalSystemAttachmentProcessors }} Params - The parameters to stream the attachments
576
- * @returns {Promise<StreamAttachmentsReturnType>} - The response object containing the ssoAttachment artifact information
586
+ * @returns {Promise<StreamAttachmentsReturnType>} - The response object containing the ssorAttachment artifact information
577
587
  * or error information if there was an error
578
588
  */
579
589
  async streamAttachments({ stream, processors, }) {
580
- var _a, _b, _c, _d;
590
+ var _a, _b;
581
591
  const repos = [
582
592
  {
583
593
  itemType: 'ssor_attachment',
584
594
  },
585
595
  ];
586
596
  this.initializeRepos(repos);
587
- const attachmentsMetadataArtifactIds = (_b = (_a = this.state.toDevRev) === null || _a === void 0 ? void 0 : _a.attachmentsMetadata) === null || _b === void 0 ? void 0 : _b.artifactIds;
588
- if (!attachmentsMetadataArtifactIds ||
589
- attachmentsMetadataArtifactIds.length === 0) {
597
+ if (!((_b = (_a = this.state.toDevRev) === null || _a === void 0 ? void 0 : _a.attachmentsMetadata) === null || _b === void 0 ? void 0 : _b.artifactIds) ||
598
+ this.state.toDevRev.attachmentsMetadata.artifactIds.length === 0) {
590
599
  console.log(`No attachments metadata artifact IDs found in state.`);
591
600
  return;
592
601
  }
593
602
  else {
594
- console.log(`Found ${attachmentsMetadataArtifactIds.length} attachments metadata artifact IDs in state.`);
603
+ console.log(`Found ${this.state.toDevRev.attachmentsMetadata.artifactIds.length} attachments metadata artifact IDs in state.`);
595
604
  }
596
- for (const attachmentsMetadataArtifactId of attachmentsMetadataArtifactIds) {
605
+ while (this.state.toDevRev.attachmentsMetadata.artifactIds.length > 0) {
606
+ const attachmentsMetadataArtifactId = this.state.toDevRev.attachmentsMetadata.artifactIds[0];
597
607
  console.log(`Started processing attachments for attachments metadata artifact ID: ${attachmentsMetadataArtifactId}.`);
598
608
  const { attachments, error } = await this.uploader.getAttachmentsFromArtifactId({
599
609
  artifact: attachmentsMetadataArtifactId,
@@ -604,6 +614,9 @@ class WorkerAdapter {
604
614
  }
605
615
  if (!attachments || attachments.length === 0) {
606
616
  console.warn(`No attachments found for artifact ID: ${attachmentsMetadataArtifactId}.`);
617
+ // Remove empty artifact and reset lastProcessed
618
+ this.state.toDevRev.attachmentsMetadata.artifactIds.shift();
619
+ this.state.toDevRev.attachmentsMetadata.lastProcessed = 0;
607
620
  continue;
608
621
  }
609
622
  console.log(`Found ${attachments.length} attachments for artifact ID: ${attachmentsMetadataArtifactId}.`);
@@ -622,25 +635,24 @@ class WorkerAdapter {
622
635
  }
623
636
  else {
624
637
  console.log(`Using default processors for attachments.`);
625
- const attachmentsToProcess = attachments.slice((_d = (_c = this.state.toDevRev) === null || _c === void 0 ? void 0 : _c.attachmentsMetadata) === null || _d === void 0 ? void 0 : _d.lastProcessed, attachments.length);
638
+ const startIndex = this.state.toDevRev.attachmentsMetadata.lastProcessed || 0;
639
+ const attachmentsToProcess = attachments.slice(startIndex);
626
640
  for (const attachment of attachmentsToProcess) {
627
641
  const response = await this.processAttachment(attachment, stream);
628
642
  if (response === null || response === void 0 ? void 0 : response.delay) {
629
643
  return response;
630
644
  }
631
645
  else if (response === null || response === void 0 ? void 0 : response.error) {
632
- console.warn(`Skipping attachment with ID ${attachment.id} due to error.`);
646
+ console.warn('Skipping attachment due to an error while processing', attachment);
633
647
  }
634
648
  if (this.state.toDevRev) {
635
649
  this.state.toDevRev.attachmentsMetadata.lastProcessed += 1;
636
650
  }
637
651
  }
638
652
  }
639
- if (this.state.toDevRev) {
640
- console.log(`Finished processing attachments for artifact ID. Setting last processed to 0 and removing artifact ID from state.`);
641
- this.state.toDevRev.attachmentsMetadata.artifactIds.shift();
642
- this.state.toDevRev.attachmentsMetadata.lastProcessed = 0;
643
- }
653
+ console.log(`Finished processing all attachments for artifact ID: ${attachmentsMetadataArtifactId}.`);
654
+ this.state.toDevRev.attachmentsMetadata.artifactIds.shift();
655
+ this.state.toDevRev.attachmentsMetadata.lastProcessed = 0;
644
656
  }
645
657
  return;
646
658
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devrev/ts-adaas",
3
- "version": "1.2.6",
3
+ "version": "1.3.0",
4
4
  "description": "DevRev ADaaS (AirDrop-as-a-Service) Typescript SDK.",
5
5
  "type": "commonjs",
6
6
  "main": "./dist/index.js",
@@ -37,7 +37,7 @@
37
37
  "typescript": "^5.3.3"
38
38
  },
39
39
  "dependencies": {
40
- "@devrev/typescript-sdk": "^1.1.27",
40
+ "@devrev/typescript-sdk": "^1.1.54",
41
41
  "axios": "^1.7.9",
42
42
  "axios-retry": "^4.5.0",
43
43
  "form-data": "^4.0.1",