From c08fd0ea2a515e1e1ff0cccdc3bc30a845415512 Mon Sep 17 00:00:00 2001 From: Matthew Zember Date: Mon, 11 Aug 2025 11:05:39 -0400 Subject: [PATCH 1/3] remove bulk delete job. update to point at /entites and /relationships --- src/index.new.test.ts | 6 --- src/index.ts | 102 +++++++++++++----------------------------- 2 files changed, 31 insertions(+), 77 deletions(-) diff --git a/src/index.new.test.ts b/src/index.new.test.ts index 62b0713..c17206a 100644 --- a/src/index.new.test.ts +++ b/src/index.new.test.ts @@ -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'); }); @@ -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({ diff --git a/src/index.ts b/src/index.ts index 1f62e2c..85200c6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,9 +13,7 @@ import Logger, { createLogger } from 'bunyan-category'; import { networkRequest } from './networkRequest'; import { - Entity, EntityForSync, - Relationship, RelationshipForSync, IntegrationDefinition, ListIntegrationDefinitions, @@ -74,8 +72,10 @@ export class FetchError extends Error { 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; @@ -363,7 +363,6 @@ export class JupiterOneClient { */ startPage = 0, ) { - let cursor: string; let complete = false; let results: any[] = []; @@ -379,9 +378,9 @@ export class JupiterOneClient { query: j1ql, deferredResponse: 'FORCE', flags: { - variableResultSize: true + variableResultSize: true, }, - cursor + cursor, }, ...options, }); @@ -396,7 +395,8 @@ export class JupiterOneClient { do { 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.`, ); } @@ -408,27 +408,31 @@ export class JupiterOneClient { } 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; } @@ -866,57 +870,38 @@ export class JupiterOneClient { return validateSyncJobResponse(response); } - async uploadGraphObjectsForDeleteSyncJob(options: { + async uploadGraphObjectsForSyncJob(options: { syncJobId: string; - entities?: Entity[]; - relationships?: Relationship[]; + entities?: EntityForSync[]; + relationships?: RelationshipForSync[]; }): Promise { - 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 { syncJobId, entities, relationships } = options; const headers = this.headers; - const response = await makeFetchRequest( - this.apiUrl + - `/persister/synchronization/jobs/${options.syncJobId}/upload`, + const entitiesResponse = await makeFetchRequest( + this.apiUrl + `/persister/synchronization/jobs/${syncJobId}/entities`, { method: 'POST', headers, - body: JSON.stringify(upload), + body: JSON.stringify({ + entities, + }), }, ); - return validateSyncJobResponse(response); - } + validateSyncJobResponse(entitiesResponse); - async uploadGraphObjectsForSyncJob(options: { - syncJobId: string; - entities?: EntityForSync[]; - relationships?: RelationshipForSync[]; - }): Promise { - const { syncJobId, entities, relationships } = options; - const headers = this.headers; - const response = await makeFetchRequest( - this.apiUrl + `/persister/synchronization/jobs/${syncJobId}/upload`, + const relationshipsResponse = await makeFetchRequest( + this.apiUrl + + `/persister/synchronization/jobs/${syncJobId}/relationships`, { method: 'POST', headers, body: JSON.stringify({ - entities, relationships, }), }, ); - return validateSyncJobResponse(response); + + return validateSyncJobResponse(relationshipsResponse); } async finalizeSyncJob(options: { @@ -1031,29 +1016,4 @@ export class JupiterOneClient { finalizeResult, }; } - - async bulkDelete(data: { - entities?: Entity[]; - relationships?: Relationship[]; - }): Promise { - 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.'); - } - } } From 6ebb44b455fac7963b4daf12e8f107728b4feef4 Mon Sep 17 00:00:00 2001 From: Matthew Zember Date: Mon, 11 Aug 2025 11:10:46 -0400 Subject: [PATCH 2/3] conditionally return response --- src/index.ts | 52 +++++++++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/src/index.ts b/src/index.ts index 85200c6..093c240 100644 --- a/src/index.ts +++ b/src/index.ts @@ -877,31 +877,37 @@ export class JupiterOneClient { }): Promise { const { syncJobId, entities, relationships } = options; const headers = this.headers; - const entitiesResponse = await makeFetchRequest( - this.apiUrl + `/persister/synchronization/jobs/${syncJobId}/entities`, - { - method: 'POST', - headers, - body: JSON.stringify({ - entities, - }), - }, - ); - validateSyncJobResponse(entitiesResponse); + 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); + } - const relationshipsResponse = await makeFetchRequest( - this.apiUrl + - `/persister/synchronization/jobs/${syncJobId}/relationships`, - { - method: 'POST', - headers, - body: JSON.stringify({ - relationships, - }), - }, - ); + if (relationships.length) { + const relationshipsResponse = await makeFetchRequest( + this.apiUrl + + `/persister/synchronization/jobs/${syncJobId}/relationships`, + { + method: 'POST', + headers, + body: JSON.stringify({ + relationships, + }), + }, + ); - return validateSyncJobResponse(relationshipsResponse); + response = await validateSyncJobResponse(relationshipsResponse); + } + return response; } async finalizeSyncJob(options: { From 55eaa4407a165fac47b9b2434e107b7993266913 Mon Sep 17 00:00:00 2001 From: Matthew Zember Date: Mon, 11 Aug 2025 11:37:10 -0400 Subject: [PATCH 3/3] remove unused sync modes --- src/index.new.test.ts | 4 ++-- src/index.ts | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/index.new.test.ts b/src/index.new.test.ts index c17206a..83733ed 100644 --- a/src/index.new.test.ts +++ b/src/index.new.test.ts @@ -267,7 +267,7 @@ describe('Core Index Tests', () => { const argumentOne = { syncJobOptions: { scope: 'an_example_scope', - syncMode: SyncJobModes.CREATE_OR_UPDATE, + syncMode: 'DIFF', }, entities: [exampleEntity], }; @@ -281,7 +281,7 @@ describe('Core Index Tests', () => { const expectedArgument = { source: SyncJobSources.API, scope: 'an_example_scope', - syncMode: SyncJobModes.CREATE_OR_UPDATE, + syncMode: 'DIFF', }; expect(j1.startSyncJob).toHaveBeenCalledWith(expectedArgument); diff --git a/src/index.ts b/src/index.ts index 093c240..20f1edc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -252,7 +252,6 @@ export enum SyncJobSources { export enum SyncJobModes { DIFF = 'DIFF', - CREATE_OR_UPDATE = 'CREATE_OR_UPDATE', } export type SyncJobResponse = {