feat: implement standardized error handling for WhatsApp API responses

This commit is contained in:
ricael 2025-09-08 08:48:49 -03:00
parent c2085b59ea
commit 10a2c001ab
4 changed files with 136 additions and 35 deletions

View File

@ -3,6 +3,7 @@ import { NumberDto } from '@api/dto/chat.dto';
import { businessController } from '@api/server.module';
import { catalogSchema, collectionsSchema } from '@validate/validate.schema';
import { RequestHandler, Router } from 'express';
import { createMetaErrorResponse } from '@utils/errorResponse';
import { HttpStatus } from './index.router';
@ -11,27 +12,45 @@ export class BusinessRouter extends RouterBroker {
super();
this.router
.post(this.routerPath('getCatalog'), ...guards, async (req, res) => {
const response = await this.dataValidate<NumberDto>({
request: req,
schema: catalogSchema,
ClassRef: NumberDto,
execute: (instance, data) => businessController.fetchCatalog(instance, data),
});
try {
const response = await this.dataValidate<NumberDto>({
request: req,
schema: catalogSchema,
ClassRef: NumberDto,
execute: (instance, data) => businessController.fetchCatalog(instance, data),
});
return res.status(HttpStatus.OK).json(response);
return res.status(HttpStatus.OK).json(response);
} catch (error) {
// Log error for debugging
console.error('Business catalog error:', error);
// Use utility function to create standardized error response
const errorResponse = createMetaErrorResponse(error, 'business_catalog');
return res.status(errorResponse.status).json(errorResponse);
}
})
.post(this.routerPath('getCollections'), ...guards, async (req, res) => {
const response = await this.dataValidate<NumberDto>({
request: req,
schema: collectionsSchema,
ClassRef: NumberDto,
execute: (instance, data) => businessController.fetchCollections(instance, data),
});
try {
const response = await this.dataValidate<NumberDto>({
request: req,
schema: collectionsSchema,
ClassRef: NumberDto,
execute: (instance, data) => businessController.fetchCollections(instance, data),
});
return res.status(HttpStatus.OK).json(response);
return res.status(HttpStatus.OK).json(response);
} catch (error) {
// Log error for debugging
console.error('Business collections error:', error);
// Use utility function to create standardized error response
const errorResponse = createMetaErrorResponse(error, 'business_collections');
return res.status(errorResponse.status).json(errorResponse);
}
});
}
public readonly router: Router = Router();
}
}

View File

@ -5,6 +5,7 @@ import { templateController } from '@api/server.module';
import { ConfigService } from '@config/env.config';
import { instanceSchema, templateSchema } from '@validate/validate.schema';
import { RequestHandler, Router } from 'express';
import { createMetaErrorResponse } from '@utils/errorResponse';
import { HttpStatus } from './index.router';
@ -16,26 +17,44 @@ export class TemplateRouter extends RouterBroker {
super();
this.router
.post(this.routerPath('create'), ...guards, async (req, res) => {
const response = await this.dataValidate<TemplateDto>({
request: req,
schema: templateSchema,
ClassRef: TemplateDto,
execute: (instance, data) => templateController.createTemplate(instance, data),
});
try {
const response = await this.dataValidate<TemplateDto>({
request: req,
schema: templateSchema,
ClassRef: TemplateDto,
execute: (instance, data) => templateController.createTemplate(instance, data),
});
res.status(HttpStatus.CREATED).json(response);
res.status(HttpStatus.CREATED).json(response);
} catch (error) {
// Log error for debugging
console.error('Template creation error:', error);
// Use utility function to create standardized error response
const errorResponse = createMetaErrorResponse(error, 'template_creation');
res.status(errorResponse.status).json(errorResponse);
}
})
.get(this.routerPath('find'), ...guards, async (req, res) => {
const response = await this.dataValidate<InstanceDto>({
request: req,
schema: instanceSchema,
ClassRef: InstanceDto,
execute: (instance) => templateController.findTemplate(instance),
});
try {
const response = await this.dataValidate<InstanceDto>({
request: req,
schema: instanceSchema,
ClassRef: InstanceDto,
execute: (instance) => templateController.findTemplate(instance),
});
res.status(HttpStatus.OK).json(response);
res.status(HttpStatus.OK).json(response);
} catch (error) {
// Log error for debugging
console.error('Template find error:', error);
// Use utility function to create standardized error response
const errorResponse = createMetaErrorResponse(error, 'template_find');
res.status(errorResponse.status).json(errorResponse);
}
});
}
public readonly router: Router = Router();
}
}

View File

@ -60,6 +60,13 @@ export class TemplateService {
const response = await this.requestTemplate(postData, 'POST');
if (!response || response.error) {
// If there's an error from WhatsApp API, throw it with the real error data
if (response && response.error) {
// Create an error object that includes the template field for Meta errors
const metaError = new Error(response.error.message || 'WhatsApp API Error');
(metaError as any).template = response.error;
throw metaError;
}
throw new Error('Error to create template');
}
@ -75,8 +82,9 @@ export class TemplateService {
return template;
} catch (error) {
this.logger.error(error);
throw new Error('Error to create template');
this.logger.error('Error in create template: ' + error);
// Propagate the real error instead of "engolindo" it
throw error;
}
}
@ -86,6 +94,7 @@ export class TemplateService {
const version = this.configService.get<WaBusiness>('WA_BUSINESS').VERSION;
urlServer = `${urlServer}/${version}/${this.businessId}/message_templates`;
const headers = { 'Content-Type': 'application/json', Authorization: `Bearer ${this.token}` };
if (method === 'GET') {
const result = await axios.get(urlServer, { headers });
return result.data;
@ -94,8 +103,15 @@ export class TemplateService {
return result.data;
}
} catch (e) {
this.logger.error(e.response.data);
return e.response.data.error;
this.logger.error('WhatsApp API request error: ' + (e.response?.data || e.message));
// Return the complete error response from WhatsApp API
if (e.response?.data) {
return e.response.data;
}
// If no response data, throw connection error
throw new Error(`Connection error: ${e.message}`);
}
}
}
}

View File

@ -0,0 +1,47 @@
import { HttpStatus } from '@api/routes/index.router';
export interface MetaErrorResponse {
status: number;
error: string;
message: string;
details: {
whatsapp_error: string;
whatsapp_code: string | number;
error_user_title: string;
error_user_msg: string;
error_type: string;
error_subcode: number | null;
fbtrace_id: string | null;
context: string;
type: string;
};
timestamp: string;
}
/**
* Creates standardized error response for Meta/WhatsApp API errors
*/
export function createMetaErrorResponse(error: any, context: string): MetaErrorResponse {
// Extract Meta/WhatsApp specific error fields
const metaError = error.template || error;
const errorUserTitle = metaError.error_user_title || metaError.message || 'Unknown error';
const errorUserMsg = metaError.error_user_msg || metaError.message || 'Unknown error';
return {
status: HttpStatus.BAD_REQUEST,
error: 'Bad Request',
message: errorUserTitle,
details: {
whatsapp_error: errorUserMsg,
whatsapp_code: metaError.code || 'UNKNOWN_ERROR',
error_user_title: errorUserTitle,
error_user_msg: errorUserMsg,
error_type: metaError.type || 'UNKNOWN',
error_subcode: metaError.error_subcode || null,
fbtrace_id: metaError.fbtrace_id || null,
context,
type: 'whatsapp_api_error'
},
timestamp: new Date().toISOString()
};
}