Skip to content

feat(label): add reliable full label sync endpoint - #2660

Open
HulianBuligon wants to merge 6 commits into
evolution-foundation:developfrom
HulianBuligon:codex/fix-label-full-sync
Open

feat(label): add reliable full label sync endpoint#2660
HulianBuligon wants to merge 6 commits into
evolution-foundation:developfrom
HulianBuligon:codex/fix-label-full-sync

Conversation

@HulianBuligon

@HulianBuligon HulianBuligon commented Jul 27, 2026

Copy link
Copy Markdown

📋 Description

This is an updated successor to #2421, rebased onto the current develop branch. It preserves the five original commits and authorship from @josuecocoman, then adds an isolated correctness fix in 06349eba.

The original endpoint requested an incremental regular app-state sync. That refreshes label metadata, but it does not provide a complete projection from which stale Chat.labels associations can be removed.

This version:

  • resets the regular app-state sync version before resyncing, forcing a complete snapshot;
  • collects label_jid associations while the snapshot is processed;
  • awaits asynchronous label handlers and the event-processing queue;
  • transactionally replaces each instance's Chat.labels projection through Prisma;
  • preserves the existing projection if the sync returns no observable label state.

The endpoint and unsupported-channel stubs from #2421 remain unchanged.

🔗 Related Issue

Maintainers can merge this PR directly or use the isolated fix commit to update #2421.

🧪 Type of Change

  • 🐛 Bug fix (non-breaking change which fixes an issue)
  • ✨ New feature (non-breaking change which adds functionality)
  • 💥 Breaking change
  • 📚 Documentation update
  • 🔧 Refactoring
  • ⚡ Performance improvement
  • 🧹 Code cleanup
  • 🔒 Security fix

🧪 Testing

  • Manual testing completed
  • Functionality verified in development environment
  • No breaking changes introduced
  • Tested with different connection types
npm run lint:check
# ESLint: no issues

npx eslint tests/label-sync-snapshot.test.ts
# no issues

npm run build
# TypeScript and tsup build passed

npx tsx --test tests/label-sync-snapshot.test.ts
# 2 passed, 0 failed

The regression tests cover both full replacement of stale labels and the fail-closed path where no label state is observed.

✅ Checklist

  • My code follows the project's style guidelines
  • I have performed a self-review of my code
  • I have commented the non-obvious snapshot behavior
  • 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 covered scenarios with automated tests
  • Any dependent changes are included in this branch

📝 Additional Notes

The snapshot replacement uses Prisma operations supported by the repository's PostgreSQL and MySQL providers. No schema or migration change is required.

Summary by Sourcery

Add a full WhatsApp Baileys label resync endpoint that rebuilds chat label projections from a forced app-state snapshot while keeping other channels unchanged.

New Features:

  • Expose a new label sync HTTP endpoint that triggers a full label resynchronization for WhatsApp Baileys instances.

Bug Fixes:

  • Ensure chat label projections are fully refreshed by rebuilding labels from a complete app-state snapshot instead of relying on incremental syncs.
  • Avoid losing existing chat label projections when a forced sync returns no observable label state.

Enhancements:

  • Track label association changes during app-state resync and transactionally replace chat label mappings in the database.
  • Await label-related event processing during sync to ensure a consistent label state before returning results.
  • Extend TypeScript configuration to include test files and add tests covering label snapshot replacement and fail-closed behavior.

Tests:

  • Add regression tests validating that syncLabels replaces stale chat labels from the snapshot and preserves labels when no label state is observed.

josuecocoman and others added 6 commits July 27, 2026 14:25
Delegates to service.syncLabels() via waMonitor, following the
same pattern as fetchLabels and handleLabel.
New endpoint that forces WhatsApp to re-download labels via
Baileys resyncAppState(['regular'], true) before returning
updated labels from DB. Uses same dataValidate pattern as findLabels.
Uses Baileys client.resyncAppState(['regular'], true) to force
an incremental re-download of WhatsApp app state (labels).
Waits 3s for LABELS_EDIT/LABELS_ASSOCIATION event handlers to
process, then returns updated labels from DB via fetchLabels().

Key: isLatest=true means incremental (safe, no disconnect).
isLatest=false would download full snapshot and cause disconnection.
Throws BadRequestException as syncLabels is only available
for Baileys (WhatsApp Web) connections.
Throws BadRequestException as syncLabels is only available
for Baileys (WhatsApp Web) connections.
@sourcery-ai

sourcery-ai Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Implements a new full label sync endpoint for Baileys WhatsApp channels that forces a complete app-state resync, snapshots label associations, and transactionally refreshes Chat.labels projections, while wiring the endpoint through controller/router layers, adding unsupported stubs for other channels, and covering behavior with targeted tests.

Sequence diagram for the new full label sync endpoint

sequenceDiagram
  actor Client
  participant LabelRouter
  participant LabelController
  participant BaileysService as BaileysStartupService
  participant Prisma

  Client->>LabelRouter: GET syncLabels
  LabelRouter->>LabelController: syncLabels(instance)
  LabelController->>BaileysService: syncLabels()

  BaileysService->>BaileysService: authState.state.keys.set(app-state-sync-version)
  BaileysService->>BaileysService: client.resyncAppState(regular)
  BaileysService->>BaileysService: eventProcessingQueue (await)

  loop During resync
    BaileysService->>BaileysService: collectLabelAssociationSnapshot(data)
  end

  alt [collector.observedLabelState]
    BaileysService->>Prisma: replaceChatLabelsFromSnapshot(instanceId, labelsByChatId)
    Prisma-->>BaileysService: transaction committed
  else [no label state observed]
    BaileysService->>BaileysService: logger.warn(keep existing projection)
  end

  BaileysService->>BaileysService: setTimeout(3000)
  BaileysService->>Prisma: fetchLabels()
  Prisma-->>BaileysService: LabelDto[]

  BaileysService-->>LabelController: LabelDto[]
  LabelController-->>LabelRouter: LabelDto[]
  LabelRouter-->>Client: 200 OK (LabelDto[])
Loading

File-Level Changes

Change Details Files
Add a full label sync workflow in the Baileys WhatsApp service that forces a full app-state sync, collects label associations, and transactionally replaces Chat.labels based on the snapshot.
  • Introduce an optional labelAssociationSnapshotCollector to track observed label state and per-chat label sets during sync processing.
  • Update LABELS_EDIT and LABELS_ASSOCIATION handlers to mark label state as observed and feed label_jid association changes into the snapshot collector, ensuring they await asynchronous processing in the event loop.
  • Add a syncLabels method that resets the regular app-state-sync version, triggers a Baileys resync, waits for the internal eventProcessingQueue, conditionally replaces chat labels via Prisma inside a transaction, and finally returns the refreshed labels after a short delay.
  • Implement helper methods to collect association deltas into a Map<chatId, Set> and to clear/rebuild Chat.labels via Prisma updateMany and per-chat upsert operations.
src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts
Expose the label sync capability as an HTTP endpoint and extend the label controller to call into the instance-level sync implementation.
  • Register a new GET /label/syncLabels route (reusing existing guards and validation pipeline) that invokes labelController.syncLabels on the resolved instance.
  • Add a syncLabels method to LabelController that dispatches to the selected waMonitor instance’s syncLabels service method.
src/api/routes/label.router.ts
src/api/controllers/label.controller.ts
Define syncLabels stubs for non-Baileys channels that explicitly reject unsupported usage.
  • Add a syncLabels method to EvolutionStartupService that throws BadRequestException for unsupported channels.
  • Add a syncLabels method to BusinessStartupService that throws BadRequestException for unsupported channels.
src/api/integrations/channel/evolution/evolution.channel.service.ts
src/api/integrations/channel/meta/whatsapp.business.service.ts
Add regression tests for the label sync snapshot behavior and ensure test files are compiled by TypeScript.
  • Create label-sync-snapshot.test.ts to verify that syncLabels forces a full app-state snapshot, clears stale labels, rebuilds per-chat labels from the collector, and cleans up its internal collector state.
  • Add a complementary test that asserts no transactional label replacement occurs (and a warning is logged) when the forced sync yields no label state mutations.
  • Introduce a helper to make setTimeout fire immediately in tests for deterministic behavior and add tests/**/*.ts to tsconfig.json include list so tests compile.
tests/label-sync-snapshot.test.ts
tsconfig.json

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@HulianBuligon
HulianBuligon marked this pull request as ready for review July 27, 2026 17:42

@sourcery-ai sourcery-ai Bot left a comment

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.

Sorry @HulianBuligon, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

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.

2 participants