diff --git a/docsfera.json b/docsfera.json index deb8c8f..ec823b4 100644 --- a/docsfera.json +++ b/docsfera.json @@ -8645,7 +8645,7 @@ "/platform/jobs/{jobId}": { "delete": { "operationId": "PlatformApiController_deleteJob", - "summary": "Delete a job and sync pipeline data", + "summary": "Delete a job and mark its table as deleted in DynamoDB", "parameters": [ { "name": "jobId", diff --git a/src/modules/platform-api/platform-api.controller.ts b/src/modules/platform-api/platform-api.controller.ts index 74caaf9..a83b7bd 100644 --- a/src/modules/platform-api/platform-api.controller.ts +++ b/src/modules/platform-api/platform-api.controller.ts @@ -963,74 +963,55 @@ export class PlatformApiController { } @Delete('jobs/:jobId') - @ApiOperation({ summary: 'Delete a job and sync pipeline data' }) + @ApiOperation({ summary: 'Delete a job and mark its table as deleted in DynamoDB' }) @RequireAllPermissions(PERMISSIONS_GROUPS.PIPELINE.permissions.DELETE) async deleteJob( @Param('jobId') jobId: string, @User() user: RequestUser, ) { - // Get job details before deletion to extract table_name and connector type const normalizedJobId = this.normalizeJobId(jobId); - let tableName: string | undefined; + const esPipelineId = this.extractPipelineIdFromJobId(jobId); - this.logger.info('[deleteJob] Start', { jobId }); + // Fetch job details and ES pipeline in parallel — both are reads with no mutual dependency + const [jobResult, esPipelineResult] = await Promise.allSettled([ + this.platformApiService.proxy('GET', `/jobs/${normalizedJobId}`, user), + this.elasticsearchService.getPipeline(user.customer_name, esPipelineId), + ]); - try { - const job = await this.platformApiService.proxy( - 'GET', - `/jobs/${normalizedJobId}`, - user, - ); - tableName = job?.source_config?.table_name; - this.logger.info('[deleteJob] Job fetched', { jobId, tableName }); - } catch (err) { - this.logger.warn('[deleteJob] Could not fetch job before deletion', { jobId, error: err.message }); + const tableName: string | undefined = jobResult.status === 'fulfilled' + ? jobResult.value?.source_config?.table_name + : undefined; + + if (jobResult.status === 'rejected') { + this.logger.warn('deleteJob: could not fetch job before deletion', { jobId, error: jobResult.reason?.message }); } - const result = await this.platformApiService.proxy( - 'DELETE', - `/jobs/${normalizedJobId}`, - user, - ); - this.logger.info('[deleteJob] Job deleted from platform-api', { jobId }); + // Main operation + const result = await this.platformApiService.proxy('DELETE', `/jobs/${normalizedJobId}`, user); - // Mark the table as deleted via in-factory (which has the correct DynamoDB credentials). - // config.tables in ES stores the DynamoDB input ID (legacy naming). - if (tableName) { - try { - const esPipelineId = this.extractPipelineIdFromJobId(jobId); - const esPipeline = await this.elasticsearchService.getPipeline( - user.customer_name, - esPipelineId, - ); - // config.tables holds the DynamoDB input ID (see syncJobInputToDynamoDB for context) - const dynamoInputId = esPipeline?.config?.tables; + // Sync deleted status to DynamoDB — follows syncJobInputToDynamoDB pattern + // config.tables in ES stores the DynamoDB input ID (legacy field naming) + const dynamoInputId: string | undefined = esPipelineResult.status === 'fulfilled' + ? esPipelineResult.value?.config?.tables + : undefined; - if (dynamoInputId) { - const info = { customer_id: user.customer_id, user_id: user.user_id, customer: user.customer_name }; + if (esPipelineResult.status === 'rejected') { + this.logger.warn('deleteJob: could not fetch ES pipeline for DynamoDB sync', { jobId, error: esPipelineResult.reason?.message }); + } - // Read current input to get full tables array (adjustInputPayload flattens the response) - const { input } = await this.inputsService.findOne({ id: dynamoInputId, info }); - const currentTables: any[] = input?.tables ?? []; - - const updatedTables = currentTables.map((t: any) => - t.name === tableName ? { ...t, status: 'deleted' } : t, - ); - - await this.inputsService.update(dynamoInputId, { tables: updatedTables }, info); - this.logger.info('[deleteJob] Marked table as deleted via in-factory', { jobId, tableName }); - } else { - this.logger.warn('[deleteJob] No input ID in ES pipeline, skipping DynamoDB update', { jobId, tableName }); - } - } catch (error) { - this.logger.error('[deleteJob] Failed to mark table as deleted via in-factory', { - jobId, - tableName, - error: error.message, - }); - } + if (tableName && dynamoInputId) { + await this.dynamoDBService.updateInputTable( + user.customer_id, + dynamoInputId, + tableName, + { status: 'deleted' }, + ).catch((error) => { + this.logger.error('Failed to sync job deletion to DynamoDB', { jobId, tableName, error: error.message }); + }); + } else if (!tableName) { + this.logger.warn('deleteJob: table name not resolved, skipping DynamoDB sync', { jobId }); } else { - this.logger.warn('[deleteJob] tableName not resolved, skipping DynamoDB update', { jobId }); + this.logger.warn('deleteJob: no input ID in ES pipeline, skipping DynamoDB sync', { jobId }); } return result;