Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions src/index.new.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,6 @@ describe('Core Index Tests', () => {
test('startSyncJob', () => {
expect(j1).toHaveProperty('startSyncJob');
});
test('uploadGraphObjectsForDeleteSyncJob', () => {
expect(j1).toHaveProperty('uploadGraphObjectsForDeleteSyncJob');
});
test('uploadGraphObjectsForSyncJob', () => {
expect(j1).toHaveProperty('uploadGraphObjectsForSyncJob');
});
Expand All @@ -103,9 +100,6 @@ describe('Core Index Tests', () => {
test('bulkUpload', () => {
expect(j1).toHaveProperty('bulkUpload');
});
test('bulkDelete', () => {
expect(j1).toHaveProperty('bulkDelete');
});

test('--api-base-url properly sets URLs', () => {
const jupiterOneCustomURLClient = new JupiterOneClient({
Expand Down
128 changes: 47 additions & 81 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@
import { networkRequest } from './networkRequest';

import {
Entity,
EntityForSync,
Relationship,
RelationshipForSync,
IntegrationDefinition,
ListIntegrationDefinitions,
Expand Down Expand Up @@ -74,8 +72,10 @@
nameForLogging?: string;
}) {
super(
`JupiterOne API error. Response not OK (requestName=${options.nameForLogging || '(none)'
}, status=${options.response.status}, url=${options.url}, method=${options.method
`JupiterOne API error. Response not OK (requestName=${
options.nameForLogging || '(none)'
}, status=${options.response.status}, url=${options.url}, method=${
options.method
}). Response: ${options.responseBody}`,
);
this.httpStatusCode = options.response.status;
Expand Down Expand Up @@ -111,7 +111,7 @@
{
maxAttempts: 5,
delay: 1000,
handleError(err, context, options) {

Check warning on line 114 in src/index.ts

View workflow job for this annotation

GitHub Actions / test

'options' is defined but never used

Check warning on line 114 in src/index.ts

View workflow job for this annotation

GitHub Actions / test

'options' is defined but never used
const possibleFetchError = err as Partial<FetchError>;
const { httpStatusCode } = possibleFetchError;
if (httpStatusCode !== undefined) {
Expand Down Expand Up @@ -163,7 +163,7 @@

export interface JupiterOneEntity {
entity: JupiterOneEntityMetadata;
properties: any;

Check warning on line 166 in src/index.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected any. Specify a different type

Check warning on line 166 in src/index.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected any. Specify a different type
}

export interface QueryResult {
Expand Down Expand Up @@ -283,7 +283,7 @@
};

export class JupiterOneClient {
graphClient: ApolloClient<any>;

Check warning on line 286 in src/index.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected any. Specify a different type

Check warning on line 286 in src/index.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected any. Specify a different type
headers?: Record<string, string>;
account: string;
accessToken: string;
Expand Down Expand Up @@ -356,21 +356,20 @@
* this limits the looping to stop after at least {stopAfter} results are found
* @deprecated This property is no longer supported.
*/
stopAfter = Number.MAX_SAFE_INTEGER,

Check warning on line 359 in src/index.ts

View workflow job for this annotation

GitHub Actions / test

'stopAfter' is assigned a value but never used

Check warning on line 359 in src/index.ts

View workflow job for this annotation

GitHub Actions / test

'stopAfter' is assigned a value but never used
/** same as above, this gives more fine-grained control over the starting point of the query,
* since this method controls the `SKIP` clause in the query
* @deprecated This property is no longer supported.
*/
startPage = 0,

Check warning on line 364 in src/index.ts

View workflow job for this annotation

GitHub Actions / test

'startPage' is assigned a value but never used

Check warning on line 364 in src/index.ts

View workflow job for this annotation

GitHub Actions / test

'startPage' is assigned a value but never used
) {

let cursor: string;
let complete = false;
let results: any[] = [];

Check warning on line 368 in src/index.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected any. Specify a different type

Check warning on line 368 in src/index.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected any. Specify a different type

const limitCheck = j1ql.match(/limit (\d+)/i);

let progress: any;

Check warning on line 372 in src/index.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected any. Specify a different type

Check warning on line 372 in src/index.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected any. Specify a different type

do {
const res = await this.graphClient.query({
Expand All @@ -379,9 +378,9 @@
query: j1ql,
deferredResponse: 'FORCE',
flags: {
variableResultSize: true
variableResultSize: true,
},
cursor
cursor,
},
...options,
});
Expand All @@ -391,12 +390,13 @@

const deferredUrl = res.data.queryV1.url;
let status = JobStatus.IN_PROGRESS;
let statusFile: any;

Check warning on line 393 in src/index.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected any. Specify a different type
const startTimeInMs = Date.now();
do {

Check warning on line 395 in src/index.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected any. Specify a different type
if (Date.now() - startTimeInMs > QUERY_RESULTS_TIMEOUT) {
throw new Error(
`Exceeded request timeout of ${QUERY_RESULTS_TIMEOUT / 1000
`Exceeded request timeout of ${
QUERY_RESULTS_TIMEOUT / 1000
} seconds.`,
);
}
Expand All @@ -408,27 +408,31 @@
} while (status === JobStatus.IN_PROGRESS);

if (status === JobStatus.FAILED) {
throw new Error(`JupiterOne returned error(s) for query: '${statusFile.error}'`);
throw new Error(
`JupiterOne returned error(s) for query: '${statusFile.error}'`,
);
}

const result = statusFile.data;

if (showProgress && !limitCheck) {
if (results.length === 0) {
progress = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);
progress = new cliProgress.SingleBar(
{},
cliProgress.Presets.shades_classic,
);
progress.start(Number(statusFile.totalCount), 0);
}
progress.update(results.length);
}

if (result) {
results = results.concat(result)
results = results.concat(result);
}

if (status === JobStatus.COMPLETED && (cursor == null || limitCheck)) {
complete = true;
}

} while (complete === false);
return results;
}
Expand Down Expand Up @@ -866,57 +870,44 @@
return validateSyncJobResponse(response);
}

async uploadGraphObjectsForDeleteSyncJob(options: {
syncJobId: string;
entities?: Entity[];
relationships?: Relationship[];
}): Promise<SyncJobResponse> {
const upload: GraphObjectDeletionPayload = {
deleteEntities: [],
deleteRelationships: [],
};
for (const e of options.entities || []) {
upload.deleteEntities.push({ _id: e?.['_id'] });
}

for (const r of options.relationships || []) {
upload.deleteRelationships.push({ _id: r?.['_id'] });
}

this.logger.trace(upload, 'Full upload of deletion sync job');
this.logger.info('uploading deletion sync job');
const headers = this.headers;
const response = await makeFetchRequest(
this.apiUrl +
`/persister/synchronization/jobs/${options.syncJobId}/upload`,
{
method: 'POST',
headers,
body: JSON.stringify(upload),
},
);
return validateSyncJobResponse(response);
}

async uploadGraphObjectsForSyncJob(options: {
syncJobId: string;
entities?: EntityForSync[];
relationships?: RelationshipForSync[];
}): Promise<SyncJobResponse> {
const { syncJobId, entities, relationships } = options;
const headers = this.headers;
const response = await makeFetchRequest(
this.apiUrl + `/persister/synchronization/jobs/${syncJobId}/upload`,
{
method: 'POST',
headers,
body: JSON.stringify({
entities,
relationships,
}),
},
);
return validateSyncJobResponse(response);
let response: SyncJobResponse | undefined = undefined;
if (entities.length) {
const entitiesResponse = await makeFetchRequest(
this.apiUrl + `/persister/synchronization/jobs/${syncJobId}/entities`,
{
method: 'POST',
headers,
body: JSON.stringify({
entities,
}),
},
);
response = await validateSyncJobResponse(entitiesResponse);
}

if (relationships.length) {
const relationshipsResponse = await makeFetchRequest(
this.apiUrl +
`/persister/synchronization/jobs/${syncJobId}/relationships`,
{
method: 'POST',
headers,
body: JSON.stringify({
relationships,
}),
},
);

response = await validateSyncJobResponse(relationshipsResponse);
}
return response;
}

async finalizeSyncJob(options: {
Expand Down Expand Up @@ -1031,29 +1022,4 @@
finalizeResult,
};
}

async bulkDelete(data: {
entities?: Entity[];
relationships?: Relationship[];
}): Promise<SyncJobResult | undefined> {
if (data.entities || data.relationships) {
const { job: syncJob } = await this.startSyncJob({
source: SyncJobSources.API,
syncMode: SyncJobModes.CREATE_OR_UPDATE,
});
const syncJobId = syncJob.id;
await this.uploadGraphObjectsForDeleteSyncJob({
syncJobId,
entities: data.entities,
relationships: data.relationships,
});
const finalizeResult = await this.finalizeSyncJob({ syncJobId });
return {
syncJobId,
finalizeResult,
};
} else {
this.logger.info('No entities or relationships to upload.');
}
}
}
Loading