Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 34 additions & 15 deletions src/api/routes/business.router.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { RouterBroker } from '@api/abstract/abstract.router';

Check failure on line 1 in src/api/routes/business.router.ts

View workflow job for this annotation

GitHub Actions / check-lint-and-build

Run autofix to sort these imports!
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';

Expand All @@ -11,27 +12,45 @@
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);

Check failure on line 27 in src/api/routes/business.router.ts

View workflow job for this annotation

GitHub Actions / check-lint-and-build

Delete `··········`
// 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);

Check failure on line 47 in src/api/routes/business.router.ts

View workflow job for this annotation

GitHub Actions / check-lint-and-build

Delete `··········`
// 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();
}
}

Check failure on line 56 in src/api/routes/business.router.ts

View workflow job for this annotation

GitHub Actions / check-lint-and-build

Insert `⏎`
49 changes: 34 additions & 15 deletions src/api/routes/template.router.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { RouterBroker } from '@api/abstract/abstract.router';

Check failure on line 1 in src/api/routes/template.router.ts

View workflow job for this annotation

GitHub Actions / check-lint-and-build

Run autofix to sort these imports!
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 { instanceSchema, templateSchema } from '@validate/validate.schema';
import { RequestHandler, Router } from 'express';
import { createMetaErrorResponse } from '@utils/errorResponse';

import { HttpStatus } from './index.router';

Expand All @@ -16,26 +17,44 @@
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);

Check failure on line 32 in src/api/routes/template.router.ts

View workflow job for this annotation

GitHub Actions / check-lint-and-build

Delete `··········`
// 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);

Check failure on line 51 in src/api/routes/template.router.ts

View workflow job for this annotation

GitHub Actions / check-lint-and-build

Delete `··········`
// 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();
}
}

Check failure on line 60 in src/api/routes/template.router.ts

View workflow job for this annotation

GitHub Actions / check-lint-and-build

Insert `⏎`
26 changes: 21 additions & 5 deletions src/api/services/template.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@
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');
}

Expand All @@ -75,8 +82,9 @@

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;
}
}

Expand All @@ -86,6 +94,7 @@
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}` };

Check failure on line 97 in src/api/services/template.service.ts

View workflow job for this annotation

GitHub Actions / check-lint-and-build

Delete `······`
if (method === 'GET') {
const result = await axios.get(urlServer, { headers });
return result.data;
Expand All @@ -94,8 +103,15 @@
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));

Check failure on line 107 in src/api/services/template.service.ts

View workflow job for this annotation

GitHub Actions / check-lint-and-build

Delete `······`
// 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}`);
}
}
}
}
47 changes: 47 additions & 0 deletions src/utils/errorResponse.ts
Original file line number Diff line number Diff line change
@@ -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 {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: The error response always uses BAD_REQUEST status, which may not fit all error scenarios.

Allow the status code to be set dynamically based on the error type, so responses can accurately reflect server or authorization errors.

Suggested implementation:

/**
 * Creates standardized error response for Meta/WhatsApp API errors
 * @param error - The error object from Meta/WhatsApp API
 * @param context - Context string for the error
 * @param status - Optional HTTP status code (defaults to BAD_REQUEST)
 * @param errorString - Optional error string (defaults to 'Bad Request')
 */
export function createMetaErrorResponse(
  error: any,
  context: string,
  status: number = HttpStatus.BAD_REQUEST,
  errorString: string = 'Bad Request'
): 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: status,
    error: errorString,
    message: errorUserTitle,
    details: {
      whatsapp_error: errorUserMsg,
      whatsapp_code: metaError.code || 'UNKNOWN_ERROR',

You will need to update all usages of createMetaErrorResponse to optionally pass the status and errorString parameters where appropriate, e.g.:

createMetaErrorResponse(error, context, HttpStatus.UNAUTHORIZED, 'Unauthorized')

// 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()
};
}