From 10a2c001abd3cfac97ae6f6cae332d8544139e72 Mon Sep 17 00:00:00 2001 From: ricael Date: Mon, 8 Sep 2025 08:48:49 -0300 Subject: [PATCH] feat: implement standardized error handling for WhatsApp API responses --- src/api/routes/business.router.ts | 49 +++++++++++++++++++--------- src/api/routes/template.router.ts | 49 +++++++++++++++++++--------- src/api/services/template.service.ts | 26 ++++++++++++--- src/utils/errorResponse.ts | 47 ++++++++++++++++++++++++++ 4 files changed, 136 insertions(+), 35 deletions(-) create mode 100644 src/utils/errorResponse.ts diff --git a/src/api/routes/business.router.ts b/src/api/routes/business.router.ts index 1e510a4f..8a56bba4 100644 --- a/src/api/routes/business.router.ts +++ b/src/api/routes/business.router.ts @@ -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({ - request: req, - schema: catalogSchema, - ClassRef: NumberDto, - execute: (instance, data) => businessController.fetchCatalog(instance, data), - }); + try { + const response = await this.dataValidate({ + 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({ - request: req, - schema: collectionsSchema, - ClassRef: NumberDto, - execute: (instance, data) => businessController.fetchCollections(instance, data), - }); + try { + const response = await this.dataValidate({ + 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(); -} +} \ No newline at end of file diff --git a/src/api/routes/template.router.ts b/src/api/routes/template.router.ts index b77b7d83..9a956e94 100644 --- a/src/api/routes/template.router.ts +++ b/src/api/routes/template.router.ts @@ -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({ - request: req, - schema: templateSchema, - ClassRef: TemplateDto, - execute: (instance, data) => templateController.createTemplate(instance, data), - }); + try { + const response = await this.dataValidate({ + 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({ - request: req, - schema: instanceSchema, - ClassRef: InstanceDto, - execute: (instance) => templateController.findTemplate(instance), - }); + try { + const response = await this.dataValidate({ + 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(); -} +} \ No newline at end of file diff --git a/src/api/services/template.service.ts b/src/api/services/template.service.ts index 949f71c7..8cbdc486 100644 --- a/src/api/services/template.service.ts +++ b/src/api/services/template.service.ts @@ -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('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}`); } } -} +} \ No newline at end of file diff --git a/src/utils/errorResponse.ts b/src/utils/errorResponse.ts new file mode 100644 index 00000000..66b61e40 --- /dev/null +++ b/src/utils/errorResponse.ts @@ -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() + }; +} \ No newline at end of file