Skip to content

feat(badges): config, freezes and earned-log endpoints for the SAKHI badges module - #341

Open
vaibhav45sktech wants to merge 2 commits into
PSMRI:mainfrom
vaibhav45sktech:feature/badges-api
Open

feat(badges): config, freezes and earned-log endpoints for the SAKHI badges module#341
vaibhav45sktech wants to merge 2 commits into
PSMRI:mainfrom
vaibhav45sktech:feature/badges-api

Conversation

@vaibhav45sktech

Copy link
Copy Markdown
Collaborator

Server side of the badges module in the SAKHI app (FLW-Mobile-App#625). The app works without these — it falls back to compiled defaults and keeps every award locally — so this is additive and nothing regresses if it is deployed later than the app.

Endpoints

All under /badges, and all taking the ASHA from the JwtToken header rather than from the body.

Method Path Returns
GET /badges/config tuning map: milestones, grace, per-badge enable flags, master kill switch
GET /badges/freezes streak-freeze windows, global plus the caller's own
GET /badges/earned the caller's award log, for restore after a reinstall
POST /badges/earned idempotent upload of new awards

BadgeEarnedPushDTO still has a userId field for wire compatibility, but it is never read: the owner comes from the JWT, so a caller cannot write awards onto another user's record.

Tables

Three, all in db_iemr: badge_config, badge_streak_freeze, badge_earned. BadgeConfigSeeder populates the tuning defaults on first start and is a no-op when rows already exist, so the module is configurable without a release of the app.

Award key

(user_id, badge_id, level) is not unique for every badge. Quarterly badges are re-earned each quarter at the same level and per-case badges once per beneficiary, so two genuine awards collided: the second was skipped as a duplicate on upload and could not be told apart on restore. The unique key is (user_id, badge_id, level, award_key).

award_key is a quarter key such as 2026-Q3, or an opaque digest for per-case awards, and empty for streak and cumulative badges. It is never a beneficiary identifier — the device hashes those before they are sent, so the column can separate two awards without the server learning who either was about. A missing key is stored as empty rather than rejected, so a client predating the field behaves exactly as it did before.

Notes for reviewers

  • No changes outside the badge files. Nothing existing was touched.
  • The tuning values live in badge_config rather than in code so milestones can be adjusted per rollout without shipping an app build.
  • Verified against a local run of this branch with the app pointed at it: all four calls return the shapes the app's BadgeApiService expects, the upload is idempotent on re-send, and an unauthenticated or tampered token is rejected.
  • 8 badge tests pass. The rest of the suite does not compile on this branch's base (ChildCare, Couple and MaternalHealth tests are stale against their current service signatures) and is untouched here, so the badge tests were run on their own.

GET /badges/{config,freezes,earned} and POST /badges/earned; the ASHA is
taken from the JWT, re-uploads are idempotent, defaults seeded on boot.
(user_id, badge_id, level) is not unique for every badge. Quarterly badges are
re-earned each quarter at the same level, and per-case badges once per
beneficiary, so two genuine awards collided on the old constraint: the second
was silently skipped as a duplicate on upload and could not be told apart on
restore.

badge_earned gains award_key and the unique key becomes
(user_id, badge_id, level, award_key). The key is a quarter key such as
"2026-Q3", or an opaque digest for per-case awards, and empty for streak and
cumulative badges, which are earned once per level.

It is never a beneficiary identifier. The device hashes those before they are
sent, so this column can separate two awards without the server learning who
either was about (Badge LLD §4).

A missing key is stored as empty rather than rejected. That is what a streak or
cumulative badge sends, and also what a client predating the field sends for
everything, so such a client keeps behaving exactly as it did before.

Verified: 8 badge tests pass, including two new ones covering two awards of the
same level under different keys, and a null key from an older client. The rest
of the suite does not compile on this branch's base and was excluded from the
run, unchanged.
@coderabbitai

coderabbitai Bot commented Sep 11, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 8de995fe-4c2c-4549-b82b-3552696e1d1e


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@vaibhav45sktech

Copy link
Copy Markdown
Collaborator Author

Heads-up on the red build_and_check_coverage: it is inherited, not caused by this PR.

The job fails at test compilation, in MaternalHealthServiceImplTest, ChildCareControllerTest and ChildCareServiceImplTest — stale call signatures against saveANCVisit, saveHBNCDetails and findRecordByActivityIdCreatedDateBenId. None of those files are touched here.

This branch is main (be69bb13) plus two commits, and the only test files it changes are BadgeControllerTest and BadgeServiceTest. Checking out be69bb13 on its own and running mvn test-compile produces the same 200 compilation errors with no badge code present at all, so main is currently red for this job independently of this change.

The 8 badge tests pass when compiled and run on their own. I have deliberately not touched the unrelated stale tests here, since fixing them is a separate change and would bury the badge review — happy to raise that as its own PR if it is wanted.

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.

1 participant