Skip to content

feat: drive the credly badge poll from the badge state table - #5139

Merged
ulemons merged 2 commits into
mainfrom
feat/issue-873-credly-poll-badge-state
Oct 28, 2026
Merged

ulemons merged 2 commits into
mainfrom
feat/issue-873-credly-poll-badge-state

Conversation

@ulemons

@ulemons ulemons commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

Refs linuxfoundation/lfx-cdp-insights-ops#873

What

Second PR for the badge state table: the poll reads and writes integration."credlyBadges".

  • New badge of a mapped template: issued-badge plus its state activity on the routing target, row stored with that segment and integration. A new pending badge is stored and emits nothing.
  • Known badge: same state as stored → nothing emitted, lastSeenAt moves (this is the Credly re-touch case: state_updated_at changes, state does not). Different state → only the state activity, on the stored segment and with the stored earner email and name, never the routing of today. A stored pending that gets accepted emits issued plus accepted.
  • Expiry leaves the poll: expired-badge will come from the daily expiry sync (next PR), which also picks up expiries already past.
  • Lock: taken before the emit for every template that gets a row, pending ones included, so a mapping with badges in the table is never movable. Stored rows lock against their stored target; a moved mapping shows up as the existing "moved during the run" warning.
  • Page handling: a badge listed twice on one page is processed once with its last version; rows are saved per target group right after its emit.
  • Shadow run: the table is neither read nor written, so a later real run does not mistake badges for already emitted.
  • Migration: badgeTemplateName and badgeTemplateUrl on the table, so the expired-badge can be built months later without reading Credly. recipientEmail stored normalised.
  • Run log: stateChanges, unchanged, pendingStored.

Code review triage

Applied: issued-badge not lost for badges first seen pending; lock also for pending and stored rows; first-sight member on state activities; per-group save after emit; page deduplicated; comment trimmed.

Not applied: comparing stateUpdatedAt as well (same state with a new timestamp is the re-touch to ignore; a full state cycle between two polls is accepted); keeping the immediate expired-badge in the poll (the expiry sync emits past expiries on its first run, emit is off in prod).

Verification

159 Credly tests, DAL ones on the CI database. Staging after merge: run with emit on, then a second run on the same window must emit no state activity and count everything as unchanged; the table must hold one row per emitted badge with the routing segment.

No reviewer requested.

A new badge is stored with the segment of its first sight and emits issued plus its state; a
known badge emits a state activity only when the stored state differs, on the stored segment
and with the stored earner; pending badges are stored silently. The poll no longer emits
expired-badge, the daily expiry sync will. A shadow run neither reads nor writes the table.

Refs linuxfoundation/lfx-cdp-insights-ops#873

Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
@ulemons ulemons self-assigned this Oct 28, 2026
Copilot AI balanced review requested due to automatic review settings October 28, 2026 10:07
@cursor

cursor Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

PR Summary

Medium Risk
Changes core Credly ingestion deduplication and activity emission semantics; incorrect state handling could miss or duplicate member activities, though behavior is heavily covered by expanded tests.

Overview
The Credly badge poll now reads and upserts integration."credlyBadges" on real emits so repeat runs do not re-emit the same activities. New mapped badges get issued/state activities on the routing target and a stored row; pending badges are stored and locked without emit until they leave pending. Known badges emit only on state changes, routed to the first-sight segment/integration and member identity; unchanged state (including Credly re-touches) updates lastSeenAt only.

Expired-badge activities are removed from the poll (deferred to a follow-up expiry sync). The migration adds badgeTemplateName and badgeTemplateUrl on the table for later expiry emission without calling Credly; upserts persist normalized recipient email and template metadata. Shadow runs skip table read/write; pages dedupe duplicate badge IDs to the last version. Run logs add stateChanges, unchanged, and pendingStored.

Reviewed by Cursor Bugbot for commit 9ce2ffa. Bugbot is set up for automated code reviews on this repo. Configure here.

Copilot AI 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.

🟡 Changes recommended

Invalid badge timestamps can reach the DAL bulk cast, aborting the target group and blocking watermark progress on retries because per-badge validation is incomplete.

1 open issue
What changed in this PR

Moves Credly badge polling onto the badge state table so CDP can suppress unchanged-state activities and preserve each badge’s original routing and recipient.

Changes:

  • Reads and persists badge state, handles pending-to-active transitions, and deduplicates badges within each page.
  • Stores template metadata for the planned expiry sync and removes expiry emission from the poll.
  • Adds state-aware polling tests and dependency wiring while keeping shadow runs isolated from persistence.
File Description
services/​libs/​data-access-layer/​src/​credly/​badges.ts Adds badge lookup and persists template metadata.
services/​libs/​data-access-layer/​src/​credly/​badges.test.ts Tests badge lookup and stored metadata.
services/​libs/​connectors/​src/​connectors/​credly/​syncs/​badgePoll.ts Implements state-aware emission, locking, and persistence.
services/​libs/​connectors/​src/​connectors/​credly/​syncs/​badgePoll.test.ts Covers state transitions, original recipients, deduplication, and shadow runs.
services/​libs/​connectors/​src/​connectors/​credly/​deps.ts Connects polling to badge-state database operations.
services/​libs/​connectors/​src/​connectors/​credly/​deps.test.ts Verifies badge persistence dependency wiring.
services/​apps/​connectors_worker/​src/​bin/​dry-run-credly-badge-poll.ts Adds no-op badge-state dependencies for dry runs.
backend/​src/​database/​migrations/​V1793181595__credly-badges-template.sql Adds template name and URL columns.

Review effort: Balanced

Comment thread services/libs/connectors/src/connectors/credly/syncs/badgePoll.ts

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 600422d. Configure here.

Comment thread services/libs/connectors/src/connectors/credly/syncs/badgePoll.ts
Pending and unchanged badges no longer go through the mapper, so the row dates are parsed inside
the per-badge guard: a malformed date skips that badge instead of failing the group save.

Refs linuxfoundation/lfx-cdp-insights-ops#873

Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
Copilot AI balanced review requested due to automatic review settings October 28, 2026 11:11

Copilot AI 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.

🟢 Approval recommended

No active review issues remain for the persisted badge-state polling changes.

No open issues 🎉

1 resolved since last review

Review effort: Balanced

@ulemons
ulemons merged commit c7bcd68 into main Oct 28, 2026
15 of 16 checks passed
@ulemons
ulemons deleted the feat/issue-873-credly-poll-badge-state branch October 28, 2026 11:41
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