-
Notifications
You must be signed in to change notification settings - Fork 4.8k
Feat/update and delete meta templates #2163
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feat/update and delete meta templates #2163
Conversation
Reviewer's GuideThis PR adds support for updating and deleting WhatsApp Business templates by extending the TemplateService with dedicated edit/delete methods and HTTP helpers, wiring new API endpoints with validation and error handling in the router and controller, defining DTOs and JSONSchema validators, and includes minor formatting cleanup in the proxy utility. Sequence diagram for template edit API flowsequenceDiagram
actor User
participant "TemplateRouter"
participant "TemplateController"
participant "TemplateService"
participant "Meta API"
User->>"TemplateRouter": POST /template/edit
"TemplateRouter"->>"TemplateController": editTemplate(instance, data)
"TemplateController"->>"TemplateService": edit(instance, data)
"TemplateService"->>"Meta API": requestEditTemplate(templateId, payload)
"Meta API"-->>"TemplateService": response
"TemplateService"-->>"TemplateController": response
"TemplateController"-->>"TemplateRouter": response
"TemplateRouter"-->>User: HTTP 200 OK / error
Sequence diagram for template delete API flowsequenceDiagram
actor User
participant "TemplateRouter"
participant "TemplateController"
participant "TemplateService"
participant "Meta API"
participant "PrismaRepository"
User->>"TemplateRouter": DELETE /template/delete
"TemplateRouter"->>"TemplateController": deleteTemplate(instance, data)
"TemplateController"->>"TemplateService": delete(instance, data)
"TemplateService"->>"Meta API": requestDeleteTemplate({ name, hsm_id })
"Meta API"-->>"TemplateService": response
"TemplateService"->>"PrismaRepository": deleteMany({ where: ... })
"PrismaRepository"-->>"TemplateService": cleanup result
"TemplateService"-->>"TemplateController": response
"TemplateController"-->>"TemplateRouter": response
"TemplateRouter"-->>User: HTTP 200 OK / error
ER diagram for new and updated template DTOserDiagram
TEMPLATE {
string name
string language
string category
any components
string webhookUrl
}
TEMPLATE_EDIT {
string templateId
string category
boolean allowCategoryChange
number ttl
any components
}
TEMPLATE_DELETE {
string name
string hsmId
}
TEMPLATE ||--|| TEMPLATE_EDIT : "edit"
TEMPLATE ||--|| TEMPLATE_DELETE : "delete"
Class diagram for new and updated template DTOs and service methodsclassDiagram
class TemplateDto {
+string name
+string language
+string category
+any components
+string webhookUrl
}
class TemplateEditDto {
+string templateId
+string category
+boolean allowCategoryChange
+number ttl
+any components
}
class TemplateDeleteDto {
+string name
+string hsmId
}
class TemplateService {
+edit(instance: InstanceDto, data: TemplateEditDto)
+delete(instance: InstanceDto, data: TemplateDeleteDto)
-requestEditTemplate(templateId: string, data: any)
-requestDeleteTemplate(name: string, hsm_id: string)
}
TemplateService ..> TemplateEditDto
TemplateService ..> TemplateDeleteDto
Class diagram for TemplateController with new methodsclassDiagram
class TemplateController {
+editTemplate(instance: InstanceDto, data: TemplateEditDto)
+deleteTemplate(instance: InstanceDto, data: TemplateDeleteDto)
+findTemplate(instance: InstanceDto)
}
TemplateController ..> TemplateEditDto
TemplateController ..> TemplateDeleteDto
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey there - I've reviewed your changes - here's some feedback:
- Consolidate the instance lookup and token/businessId assignment in TemplateService into a shared private helper to reduce duplication and improve maintainability.
- Consider using PATCH instead of POST for the template edit endpoint to better align with RESTful update semantics.
- Replace console.error in the router error handlers with the project's logger to ensure consistent logging practices and severity levels.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Consolidate the instance lookup and token/businessId assignment in TemplateService into a shared private helper to reduce duplication and improve maintainability.
- Consider using PATCH instead of POST for the template edit endpoint to better align with RESTful update semantics.
- Replace console.error in the router error handlers with the project's logger to ensure consistent logging practices and severity levels.
## Individual Comments
### Comment 1
<location> `src/api/services/template.service.ts:107` </location>
<code_context>
+ if (typeof data.category === 'string') payload.category = data.category;
+ if (typeof data.allowCategoryChange === 'boolean') payload.allow_category_change = data.allowCategoryChange;
+ if (typeof data.ttl === 'number') payload.time_to_live = data.ttl;
+ if (data.components) payload.components = data.components;
+
+ const response = await this.requestEditTemplate(data.templateId, payload);
</code_context>
<issue_to_address>
**suggestion:** Check for empty array or object in components assignment.
The current logic excludes empty arrays or objects from assignment, which may be valid. Use a check like data.components !== undefined to allow empty values if appropriate.
```suggestion
if (data.components !== undefined) payload.components = data.components;
```
</issue_to_address>
### Comment 2
<location> `src/api/services/template.service.ts:145-152` </location>
<code_context>
+
+ try {
+ // Best-effort local cleanup of stored template metadata
+ await this.prismaRepository.template.deleteMany({
+ where: {
+ OR: [
+ { name: data.name, instanceId: getInstance.id },
+ data.hsmId ? { templateId: data.hsmId, instanceId: getInstance.id } : undefined,
+ ].filter(Boolean) as any,
+ },
+ });
</code_context>
<issue_to_address>
**suggestion:** Avoid using 'as any' in Prisma query filter.
Explicitly define the filter type or refactor the query to maintain type safety and prevent hidden type errors.
```suggestion
const orFilter: Array<{ name?: string; templateId?: string; instanceId: string }> = [
{ name: data.name, instanceId: getInstance.id },
];
if (data.hsmId) {
orFilter.push({ templateId: data.hsmId, instanceId: getInstance.id });
}
await this.prismaRepository.template.deleteMany({
where: {
OR: orFilter,
},
});
```
</issue_to_address>
### Comment 3
<location> `src/api/services/template.service.ts:154-156` </location>
<code_context>
+ },
+ });
+ } catch (err) {
+ this.logger.warn(
+ `Failed to cleanup local template records after delete: ${(err as Error)?.message || String(err)}`,
+ );
</code_context>
<issue_to_address>
**suggestion:** Consider including error stack in log for better diagnostics.
Including the stack trace will provide more context for debugging cleanup failures.
```suggestion
this.logger.warn(
`Failed to cleanup local template records after delete: ${(err as Error)?.message || String(err)}\nStack: ${(err as Error)?.stack || 'No stack trace available.'}`,
);
```
</issue_to_address>
### Comment 4
<location> `src/validate/templateEdit.schema.ts:31` </location>
<code_context>
+ category: { type: 'string', enum: ['AUTHENTICATION', 'MARKETING', 'UTILITY'] },
+ allowCategoryChange: { type: 'boolean' },
+ ttl: { type: 'number' },
+ components: { type: 'array' },
+ },
+ required: ['templateId'],
</code_context>
<issue_to_address>
**suggestion:** Specify item type for components array in schema.
Defining items: { type: 'object' } for the components array will ensure proper validation and prevent invalid data.
```suggestion
components: {
type: 'array',
items: { type: 'object' }
},
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| if (typeof data.category === 'string') payload.category = data.category; | ||
| if (typeof data.allowCategoryChange === 'boolean') payload.allow_category_change = data.allowCategoryChange; | ||
| if (typeof data.ttl === 'number') payload.time_to_live = data.ttl; | ||
| if (data.components) payload.components = data.components; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: Check for empty array or object in components assignment.
The current logic excludes empty arrays or objects from assignment, which may be valid. Use a check like data.components !== undefined to allow empty values if appropriate.
| if (data.components) payload.components = data.components; | |
| if (data.components !== undefined) payload.components = data.components; |
| await this.prismaRepository.template.deleteMany({ | ||
| where: { | ||
| OR: [ | ||
| { name: data.name, instanceId: getInstance.id }, | ||
| data.hsmId ? { templateId: data.hsmId, instanceId: getInstance.id } : undefined, | ||
| ].filter(Boolean) as any, | ||
| }, | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: Avoid using 'as any' in Prisma query filter.
Explicitly define the filter type or refactor the query to maintain type safety and prevent hidden type errors.
| await this.prismaRepository.template.deleteMany({ | |
| where: { | |
| OR: [ | |
| { name: data.name, instanceId: getInstance.id }, | |
| data.hsmId ? { templateId: data.hsmId, instanceId: getInstance.id } : undefined, | |
| ].filter(Boolean) as any, | |
| }, | |
| }); | |
| const orFilter: Array<{ name?: string; templateId?: string; instanceId: string }> = [ | |
| { name: data.name, instanceId: getInstance.id }, | |
| ]; | |
| if (data.hsmId) { | |
| orFilter.push({ templateId: data.hsmId, instanceId: getInstance.id }); | |
| } | |
| await this.prismaRepository.template.deleteMany({ | |
| where: { | |
| OR: orFilter, | |
| }, | |
| }); |
| category: { type: 'string', enum: ['AUTHENTICATION', 'MARKETING', 'UTILITY'] }, | ||
| allowCategoryChange: { type: 'boolean' }, | ||
| ttl: { type: 'number' }, | ||
| components: { type: 'array' }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: Specify item type for components array in schema.
Defining items: { type: 'object' } for the components array will ensure proper validation and prevent invalid data.
| components: { type: 'array' }, | |
| components: { | |
| type: 'array', | |
| items: { type: 'object' } | |
| }, |
| } else { | ||
| const { host, password, port, protocol: proto, username } = proxy | ||
| protocol = (proto || 'http').replace(':', '') | ||
| const { host, password, port, protocol: proto, username } = proxy; |
Check failure
Code scanning / CodeQL
Insecure randomness High
Math.random()
| } else { | ||
| const { host, password, port, protocol: proto, username } = proxy | ||
| protocol = (proto || 'http').replace(':', '') | ||
| const { host, password, port, protocol: proto, username } = proxy; |
Check failure
Code scanning / CodeQL
Insecure randomness High
Math.random()
| } else { | ||
| const { host, password, port, protocol: proto, username } = proxy | ||
| protocol = (proto || 'http').replace(':', '') | ||
| const { host, password, port, protocol: proto, username } = proxy; |
Check failure
Code scanning / CodeQL
Insecure randomness High
Math.random()
| } else { | ||
| const { host, password, port, protocol: proto, username } = proxy | ||
| protocol = (proto || 'http').replace(':', '') | ||
| const { host, password, port, protocol: proto, username } = proxy; |
Check failure
Code scanning / CodeQL
Insecure randomness High
Math.random()
| } else { | ||
| const { host, password, port, protocol: proto, username } = proxy | ||
| protocol = (proto || 'http').replace(':', '') | ||
| const { host, password, port, protocol: proto, username } = proxy; |
Check failure
Code scanning / CodeQL
Insecure randomness High
📋 Description
Adds Template management endpoints for WhatsApp Business templates.
🔗 Related Issue
Closes #(issue_number)
🧪 Type of Change
🧪 Testing
Manual checks:
/template/createwith validTemplateDtocreates template and stores metadata/template/editupdates template fields via Meta API/template/deletedeletes template by name or hsm_id and performs best-effort local cleanup/template/findretrieves templates for the instance✅ Checklist
📝 Additional Notes
WAMonitoringServicecreateMetaErrorResponseSummary by Sourcery
Add update and delete operations for WhatsApp Business message templates
New Features:
Enhancements:
Chores: