From 10a2c001abd3cfac97ae6f6cae332d8544139e72 Mon Sep 17 00:00:00 2001 From: ricael Date: Mon, 8 Sep 2025 08:48:49 -0300 Subject: [PATCH 1/5] 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 From 79438c94459601c873b3a3684a61fd8e70516a9c Mon Sep 17 00:00:00 2001 From: ricael Date: Mon, 8 Sep 2025 09:11:45 -0300 Subject: [PATCH 2/5] refactor: lint fix --- src/api/routes/business.router.ts | 8 ++++---- src/api/routes/template.router.ts | 8 ++++---- src/api/services/template.service.ts | 8 ++++---- src/utils/errorResponse.ts | 8 ++++---- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/api/routes/business.router.ts b/src/api/routes/business.router.ts index 8a56bba4..faca7b33 100644 --- a/src/api/routes/business.router.ts +++ b/src/api/routes/business.router.ts @@ -1,9 +1,9 @@ import { RouterBroker } from '@api/abstract/abstract.router'; import { NumberDto } from '@api/dto/chat.dto'; import { businessController } from '@api/server.module'; +import { createMetaErrorResponse } from '@utils/errorResponse'; import { catalogSchema, collectionsSchema } from '@validate/validate.schema'; import { RequestHandler, Router } from 'express'; -import { createMetaErrorResponse } from '@utils/errorResponse'; import { HttpStatus } from './index.router'; @@ -24,7 +24,7 @@ export class BusinessRouter extends RouterBroker { } 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); @@ -44,7 +44,7 @@ export class BusinessRouter extends RouterBroker { } 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); @@ -53,4 +53,4 @@ export class BusinessRouter extends RouterBroker { } 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 9a956e94..39d7b128 100644 --- a/src/api/routes/template.router.ts +++ b/src/api/routes/template.router.ts @@ -3,9 +3,9 @@ import { InstanceDto } from '@api/dto/instance.dto'; import { TemplateDto } from '@api/dto/template.dto'; import { templateController } from '@api/server.module'; import { ConfigService } from '@config/env.config'; +import { createMetaErrorResponse } from '@utils/errorResponse'; import { instanceSchema, templateSchema } from '@validate/validate.schema'; import { RequestHandler, Router } from 'express'; -import { createMetaErrorResponse } from '@utils/errorResponse'; import { HttpStatus } from './index.router'; @@ -29,7 +29,7 @@ export class TemplateRouter extends RouterBroker { } 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); @@ -48,7 +48,7 @@ export class TemplateRouter extends RouterBroker { } 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); @@ -57,4 +57,4 @@ export class TemplateRouter extends RouterBroker { } 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 8cbdc486..8cc12831 100644 --- a/src/api/services/template.service.ts +++ b/src/api/services/template.service.ts @@ -94,7 +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; @@ -104,14 +104,14 @@ export class TemplateService { } } catch (e) { 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 index 66b61e40..ee36ed22 100644 --- a/src/utils/errorResponse.ts +++ b/src/utils/errorResponse.ts @@ -26,7 +26,7 @@ export function createMetaErrorResponse(error: any, context: string): MetaErrorR 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', @@ -40,8 +40,8 @@ export function createMetaErrorResponse(error: any, context: string): MetaErrorR error_subcode: metaError.error_subcode || null, fbtrace_id: metaError.fbtrace_id || null, context, - type: 'whatsapp_api_error' + type: 'whatsapp_api_error', }, - timestamp: new Date().toISOString() + timestamp: new Date().toISOString(), }; -} \ No newline at end of file +} From 16c0a8033f5a78de62626a12dba6429ec9e2016f Mon Sep 17 00:00:00 2001 From: ricael Date: Mon, 8 Sep 2025 14:36:09 -0300 Subject: [PATCH 3/5] add stringify --- src/api/services/template.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/services/template.service.ts b/src/api/services/template.service.ts index 8cc12831..d129f602 100644 --- a/src/api/services/template.service.ts +++ b/src/api/services/template.service.ts @@ -103,7 +103,7 @@ export class TemplateService { return result.data; } } catch (e) { - this.logger.error('WhatsApp API request error: ' + (e.response?.data || e.message)); + this.logger.error('WhatsApp API request error: ' + ( e.response?.data ? JSON.stringify(e.response?.data) : e.message)); // Return the complete error response from WhatsApp API if (e.response?.data) { From 05fcdd9ffc3a661b6dba75dfc5e823100fe7348d Mon Sep 17 00:00:00 2001 From: ricael Date: Mon, 8 Sep 2025 14:45:33 -0300 Subject: [PATCH 4/5] =?UTF-8?q?refactor:=20melhora=20na=20formata=C3=A7?= =?UTF-8?q?=C3=A3o=20do=20log=20de=20erro=20da=20API=20do=20WhatsApp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/services/template.service.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/api/services/template.service.ts b/src/api/services/template.service.ts index d129f602..8c36feab 100644 --- a/src/api/services/template.service.ts +++ b/src/api/services/template.service.ts @@ -103,7 +103,9 @@ export class TemplateService { return result.data; } } catch (e) { - this.logger.error('WhatsApp API request error: ' + ( e.response?.data ? JSON.stringify(e.response?.data) : e.message)); + this.logger.error( + 'WhatsApp API request error: ' + (e.response?.data ? JSON.stringify(e.response?.data) : e.message), + ); // Return the complete error response from WhatsApp API if (e.response?.data) { From 4378c33f42acbdeafcb67ec8ec701bf6a734fa66 Mon Sep 17 00:00:00 2001 From: ricael Date: Wed, 17 Sep 2025 08:51:16 -0300 Subject: [PATCH 5/5] Merge branch 'develop' into main_