Feat/update and delete meta templates by ricaelchiquetti · Pull Request #2163 · EvolutionAPI/evolution-api · GitHub
Skip to content

Feat/update and delete meta templates#2163

Merged
DavidsonGomes merged 3 commits intoEvolutionAPI:developfrom
ricaelchiquetti:feat/update_delete_meta_templates
Nov 7, 2025
Merged

Feat/update and delete meta templates#2163
DavidsonGomes merged 3 commits intoEvolutionAPI:developfrom
ricaelchiquetti:feat/update_delete_meta_templates

Conversation

@ricaelchiquetti
Copy link
Copy Markdown
Contributor

@ricaelchiquetti ricaelchiquetti commented Oct 30, 2025

📋 Description

Adds Template management endpoints for WhatsApp Business templates.

🔗 Related Issue

Closes #(issue_number)

🧪 Type of Change

  • 🐛 Bug fix (non-breaking change which fixes an issue)
  • [X ] ✨ New feature (non-breaking change which adds functionality)
  • 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • 📚 Documentation update
  • 🔧 Refactoring (no functional changes)
  • ⚡ Performance improvement
  • 🧹 Code cleanup
  • 🔒 Security fix

🧪 Testing

  • Manual testing completed
  • Functionality verified in development environment
  • No breaking changes introduced
  • Tested with different connection types (if applicable)

Manual checks:

  • POST /template/create with valid TemplateDto creates template and stores metadata
  • POST /template/edit updates template fields via Meta API
  • DELETE /template/delete deletes template by name or hsm_id and performs best-effort local cleanup
  • GET /template/find retrieves templates for the instance
  • Validation via JSONSchema7 blocks invalid payloads
  • Error responses are standardized and include Meta error payload when applicable

✅ Checklist

  • My code follows the project's style guidelines
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have manually tested my changes thoroughly
  • I have verified the changes work with different scenarios
  • Any dependent changes have been merged and published

📝 Additional Notes

  • Multi-tenant: All operations are scoped by instance through WAMonitoringService
  • Validation: Uses JSONSchema7 and DTO classes following Evolution patterns
  • Error handling: Service propagates Meta API errors with structured payloads; routes use createMetaErrorResponse
  • Database: On delete, performs best-effort cleanup of local template metadata via Prisma

Summary by Sourcery

Add update and delete operations for WhatsApp Business message templates

New Features:

  • Implement TemplateService.edit to update templates via Meta API
  • Implement TemplateService.delete to remove templates and perform local metadata cleanup
  • Expose POST /template/edit and DELETE /template/delete routes with guards and error handling

Enhancements:

  • Introduce TemplateEditDto and TemplateDeleteDto with JSONSchema7 validation
  • Wrap and propagate Meta API errors into standardized responses

Chores:

  • Apply formatting and semicolon tweaks in makeProxyAgentUndici

@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented Oct 30, 2025

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a 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>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
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;
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: 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.

Suggested change
if (data.components) payload.components = data.components;
if (data.components !== undefined) payload.components = data.components;

Comment on lines +145 to +152
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,
},
});
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: 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.

Suggested change
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' },
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: Specify item type for components array in schema.

Defining items: { type: 'object' } for the components array will ensure proper validation and prevent invalid data.

Suggested change
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

This uses a cryptographically insecure random number generated at
Math.random()
in a security context.
} 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

This uses a cryptographically insecure random number generated at
Math.random()
in a security context.
} 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

This uses a cryptographically insecure random number generated at
Math.random()
in a security context.
} 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

This uses a cryptographically insecure random number generated at
Math.random()
in a security context.

Check failure

Code scanning / CodeQL

Insecure randomness High

This uses a cryptographically insecure random number generated at
Math.random()
in a security context.
@DavidsonGomes DavidsonGomes merged commit 1e3a235 into EvolutionAPI:develop Nov 7, 2025
4 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants