feat(contact): add contact save endpoint for Baileys provider (#2711) - #2712
feat(contact): add contact save endpoint for Baileys provider (#2711)#2712sohampirale wants to merge 2 commits into
Conversation
…ion-foundation#2711) - Add SaveContactDto for typed contact payload - Add saveContactSchema with validation for number and name - Add ContactController delegating to active Baileys instance - Add ContactRouter mounting POST /contact/save/:instanceName - Register ContactRouter in index.router.ts and server.module.ts - Implement saveContact in BaileysStartupService using chatModify contact app-state sync
Reviewer's GuideIntroduces POST /contact/save/:instanceName for validating and saving contacts through Baileys App-State Sync, with configurable device persistence, local Prisma mirroring, dependency wiring, and standardized error handling. Sequence diagram for saving a contact through BaileyssequenceDiagram
participant Client
participant ContactRouter
participant ContactController
participant BaileysStartupService
participant WhatsApp
participant Prisma
Client->>ContactRouter: POST /contact/save/:instanceName
ContactRouter->>ContactRouter: dataValidate
ContactRouter->>ContactController: saveContact(instance, data)
ContactController->>BaileysStartupService: saveContact(data)
BaileysStartupService->>WhatsApp: chatModify(contact, jid)
opt CONTACTS persistence enabled
BaileysStartupService->>Prisma: contact.upsert
end
BaileysStartupService-->>ContactController: saved contact response
ContactController-->>ContactRouter: response
ContactRouter-->>Client: 201 Created
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 3 issues
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="src/validate/contact.schema.ts" line_range="27-28" />
<code_context>
+ $id: v4(),
+ type: 'object',
+ properties: {
+ number: { type: 'string' },
+ name: { type: 'string' },
+ firstName: { type: 'string' },
+ saveOnDevice: { type: 'boolean' },
+ },
+ required: ['number', 'name'],
+ ...isNotEmpty('number', 'name'),
+};
</code_context>
<issue_to_address>
**issue (bug_risk):** The non-empty checks for `number` and `name` are skipped whenever the request includes `firstName` or `saveOnDevice`. `isNotEmpty` uses `propertyNames` with an enum containing only `number` and `name`, so the `if` condition becomes false for those valid optional fields and payloads such as `{ "number": "", "name": "", "saveOnDevice": true }` pass validation.
**Triggers:** When a client supplies either optional field along with an empty required field.
**Suggested fix:** Validate each property directly with `minLength: 1`, or change the conditional schema so optional properties do not disable the required-field checks.
```suggestion
number: { type: 'string', minLength: 1 },
name: { type: 'string', minLength: 1 },
```
</issue_to_address>
### Comment 2
<location path="src/validate/contact.schema.ts" line_range="27-32" />
<code_context>
+ $id: v4(),
+ type: 'object',
+ properties: {
+ number: { type: 'string' },
+ name: { type: 'string' },
+ firstName: { type: 'string' },
+ saveOnDevice: { type: 'boolean' },
+ },
+ required: ['number', 'name'],
+ ...isNotEmpty('number', 'name'),
+};
</code_context>
<issue_to_address>
**issue (bug_risk):** The schema accepts any non-empty string as `number`, including values such as `abc` or whitespace. `createJid` strips non-digits and produces `@s.whatsapp.net`, so `chatModify` is called with a malformed JID instead of rejecting the request as invalid.
**Triggers:** When `number` is non-numeric or contains no digits.
**Suggested fix:** Require a valid WhatsApp number/JID pattern before calling `createJid`, and reject values that normalize to an empty number.
</issue_to_address>
### Comment 3
<location path="src/api/controllers/contact.controller.ts" line_range="8-9" />
<code_context>
+export class ContactController {
+ constructor(private readonly waMonitor: WAMonitoringService) {}
+
+ public async saveContact({ instanceName }: InstanceDto, data: SaveContactDto) {
+ return await this.waMonitor.waInstances[instanceName].saveContact(data);
+ }
+}
</code_context>
<issue_to_address>
**issue (bug_risk):** The globally mounted route calls `saveContact` on whatever object is registered for `instanceName`, but only `BaileysStartupService` implements this method. Calling the endpoint for an Evolution or WhatsApp Business instance dereferences a missing method and returns an internal error instead of a provider-specific unsupported-operation response.
**Triggers:** When `POST /contact/save/:instanceName` targets a non-Baileys instance.
**Suggested fix:** Check the active instance/provider before dispatching and return a clear unsupported-provider error, or expose the route only for Baileys instances.
</issue_to_address>Sourcery assessment
Needs a human reviewer. 3 findings to address first, and if the implementation is wrong, it can create or update an incorrect contact in the WhatsApp/device address book and optionally persist the wrong name in the local contact record. Reverting the code does not remove those entries, but the bounded damage can be repaired by correcting or deleting the affected contact.
Blocking findings: src/validate/contact.schema.ts:28, src/validate/contact.schema.ts:32, src/api/controllers/contact.controller.ts:9
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
…provider checks - Add numeric regex pattern and length constraints to number in saveContactSchema - Replace conditional isNotEmpty with direct minLength constraints - Add unsupported-provider guard in ContactController with BadRequestException
📋 Description
Feat : #2711
This PR introduces a dedicated API endpoint for programmatically saving contacts to the connected WhatsApp account using Baileys' multi-device App-State Sync (collection
critical_unblock_low).Unlike
POST /message/sendContact(which only sends a vCard message inside a chat), this endpoint updates WhatsApp's synchronized contact state so that the contact's name is recognized across all linked WhatsApp devices and the local database mirror.Key Changes:
SaveContactDtoandsaveContactSchemawith JSON Schema validation fornumber,name, optionalfirstName, andsaveOnDevice.ContactControllerandContactRouterexposingPOST /contact/save/:instanceName.ContactRouterandContactControllerinindex.router.tsandserver.module.ts.saveContact()toBaileysStartupServiceutilizing Baileys'chatModify()withContactAction(saveOnPrimaryAddressbook) and updating the local Prisma contact mirror.🔗 Related Issue
Closes #2711
🧪 Type of Change
🧪 Testing
Manual testing completed against a live QR-paired WhatsApp session.
Functionality verified in development environment:
POST /contact/save/:instanceNamewith test contact payload (number+name)./chat/findContactsimmediately reflects the updated contact withisSaved: true.Full TypeScript type-check passed (
npx tsc --noEmitwith 0 errors).ESLint & Prettier checks passed with 0 warnings/errors.
No breaking changes introduced to existing endpoints.
📸 Example Usage
Request:
Response (201 Created):
{ "saved": true, "number": "5511999999999", "name": "Jane Doe", "firstName": "Jane", "saveOnDevice": true }✅ Checklist
📝 Additional Notes
I am a new contributor to the Evolution API repository. I would appreciate any guidance, feedback, or suggestions from the maintainers if any adjustments are needed to better align with the project's standards and conventions. Thank you!
Summary by Sourcery
Enable programmatic contact saving for Baileys-backed WhatsApp instances.
New Features:
POST /contact/save/:instanceNameendpoint for saving contacts to connected WhatsApp accounts.Enhancements:
Summary by Sourcery
Enable programmatic contact saving for Baileys-backed WhatsApp instances.
New Features:
POST /contact/save/:instanceNameendpoint for saving contacts on connected WhatsApp Baileys instances.Enhancements: