fix: prevent sync from nullifying assigned_resource_id on tickets
- bulkUpsert now accepts preserveExistingOnNull column list, using COALESCE(EXCLUDED.col, table.col) so null incoming values never overwrite existing non-null DB values - bulkUpsertRecords passes resource ID columns as preserve-on-null for TICKETS and TASKS entities - getValidResourceIds now throws on DB error instead of returning empty set (which would nullify every resource reference) - Fix mimecast mailbox-remediate fetch handlers to check res.ok and content-type before calling res.json(), preventing JSON parse crash on 502 Bad Gateway responses
This commit is contained in:
parent
04a720f0d0
commit
0e8eb4871e
4 changed files with 40 additions and 26 deletions
|
|
@ -253,14 +253,16 @@ export class EntitySyncService {
|
|||
const validResourceIds = await this.getValidResourceIds();
|
||||
|
||||
// Filter tickets with invalid resource references
|
||||
let skippedResourceCount = 0;
|
||||
mappedRecords = mappedRecords.map(ticket => {
|
||||
// Set invalid resource IDs to null instead of filtering out the entire ticket
|
||||
// Set to null — bulkUpsert uses COALESCE for these columns so existing DB value is preserved
|
||||
if (ticket.assigned_resource_id && !validResourceIds.has(ticket.assigned_resource_id)) {
|
||||
entityLogger.debug(`Invalid assigned_resource_id, setting to null`, {
|
||||
entityLogger.debug(`Unknown assigned_resource_id, will preserve existing DB value via COALESCE`, {
|
||||
ticketId: ticket.id,
|
||||
invalidResourceId: ticket.assigned_resource_id,
|
||||
unknownResourceId: ticket.assigned_resource_id,
|
||||
});
|
||||
ticket.assigned_resource_id = null;
|
||||
skippedResourceCount++;
|
||||
}
|
||||
if (ticket.first_response_assigned_resource_id && !validResourceIds.has(ticket.first_response_assigned_resource_id)) {
|
||||
ticket.first_response_assigned_resource_id = null;
|
||||
|
|
@ -271,9 +273,8 @@ export class EntitySyncService {
|
|||
return ticket;
|
||||
});
|
||||
|
||||
const nullifiedCount = initialCount - mappedRecords.filter(t => t.assigned_resource_id).length;
|
||||
if (nullifiedCount > 0) {
|
||||
entityLogger.warn('Nullified invalid resource references', { nullifiedCount });
|
||||
if (skippedResourceCount > 0) {
|
||||
entityLogger.warn('Skipped unknown resource references (existing DB values preserved via COALESCE)', { skippedResourceCount });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -595,7 +596,7 @@ export class EntitySyncService {
|
|||
chunkLogger.info('Cached valid resource IDs', { cacheSize: this.cachedValidResourceIds.size });
|
||||
}
|
||||
|
||||
// Nullify invalid resource references
|
||||
// Null out unknown resource references — bulkUpsert uses COALESCE so existing DB value is preserved
|
||||
mappedRecords = mappedRecords.map(ticket => {
|
||||
if (ticket.assigned_resource_id && !this.cachedValidResourceIds!.has(ticket.assigned_resource_id)) {
|
||||
ticket.assigned_resource_id = null;
|
||||
|
|
@ -670,16 +671,9 @@ export class EntitySyncService {
|
|||
* @returns Set of valid resource IDs
|
||||
*/
|
||||
private async getValidResourceIds(): Promise<Set<number>> {
|
||||
try {
|
||||
const query = 'SELECT id FROM resources WHERE is_deleted = false';
|
||||
const result = await postgresClient.query<{ id: number }>(query);
|
||||
return new Set(result.rows.map(row => row.id));
|
||||
} catch (error) {
|
||||
const err = error instanceof Error ? error : new Error(String(error));
|
||||
this.logger.error('Failed to fetch valid resource IDs', {}, err);
|
||||
// Return empty set on error - will cause all resource IDs to be nullified
|
||||
return new Set();
|
||||
}
|
||||
const query = 'SELECT id FROM resources WHERE is_deleted = false';
|
||||
const result = await postgresClient.query<{ id: number }>(query);
|
||||
return new Set(result.rows.map(row => Number(row.id)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -690,7 +684,7 @@ export class EntitySyncService {
|
|||
try {
|
||||
const query = 'SELECT id FROM contacts WHERE is_deleted = false';
|
||||
const result = await postgresClient.query<{ id: number }>(query);
|
||||
return new Set(result.rows.map(row => row.id));
|
||||
return new Set(result.rows.map(row => Number(row.id)));
|
||||
} catch (error) {
|
||||
const err = error instanceof Error ? error : new Error(String(error));
|
||||
this.logger.error('Failed to fetch valid contact IDs', {}, err);
|
||||
|
|
@ -708,7 +702,7 @@ export class EntitySyncService {
|
|||
try {
|
||||
const query = 'SELECT id FROM projects WHERE is_deleted = false';
|
||||
const result = await postgresClient.query<{ id: number }>(query);
|
||||
return new Set(result.rows.map(row => row.id));
|
||||
return new Set(result.rows.map(row => Number(row.id)));
|
||||
} catch (error) {
|
||||
const err = error instanceof Error ? error : new Error(String(error));
|
||||
this.logger.error('Failed to fetch valid project IDs', {}, err);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue