40227-vm/backend/docs/user-progress.md
2026-06-10 18:27:19 +02:00

4.5 KiB

User Progress Backend

Purpose

user_progress stores per-user progress for narrow staff workflows, keyed by a typed progress_type and an item_id. Current supported types are sign_learned (sign-language items learned) and zone_checkin (zones-of-regulation check-ins). The backend owns tenant scope, user ownership, validation, and persistence (one row per user + type + item, upserted).

Slice Files (by layer)

  • Route: src/routes/user_progress.ts (thin wiring; GET /, POST /, DELETE /by-item).
  • Controller: src/api/controllers/user_progress.controller.ts (custom — not the CRUD factory).
  • Service (BLL): src/services/user_progress.ts.
  • Repository (DAL): queries run through db.user_progress inside the service (no separate db/api/user_progress.ts).
  • Model: src/db/models/user_progress.ts.
  • Shared used: db/with-transaction.ts (withTransaction); services/shared/access.ts (assertAuthenticatedTenantUser, getCampusId, getOrganizationIdOrGlobal, requireUserId); shared/constants/user-progress.ts (USER_PROGRESS_TYPE_VALUES, UserProgressType); shared/constants/pagination.ts (resolvePagination); shared/errors/validation.ts (ValidationError).

API

All routes require JWT authentication. Base path mounted at /api/user_progress.

  • GET /api/user_progress -> 200 { rows, count }. Required query progress_type (must be a valid type); optional item_id, plus limit / page (paginated via resolvePagination). Returns the current user's rows for that type, ordered by createdAt desc.
  • POST /api/user_progress -> 200. Request body wrapped as { data: <UserProgressInput> }. Creates or updates one progress item and returns the saved DTO.
  • DELETE /api/user_progress/by-item -> 200 { deletedCount }. Required query progress_type and item_id. Deletes the current user's row for that type + item.

Access Rules

  • All operations require an authenticated tenant user (assertAuthenticatedTenantUser).
  • No additional role gating: every operation is bound to the current user. List, upsert, and delete are all filtered by userId (requireUserId), so a user only ever reads, writes, or deletes their own progress. Frontend-provided names or roles are not trusted for ownership.

Tenant Scope

  • Organization is resolved via getOrganizationIdOrGlobal: global access users bypass the org filter; regular users are bound to their organization. All queries are still filtered by userId so each user only sees their own progress.
  • userId is required from the current user (requireUserId).
  • On upsert, campusId is set from getCampusId; updatedById from the current user, and createdById on create.

Data Contract

  • Mutation input (UserProgressInput): progress_type (must be one of USER_PROGRESS_TYPE_VALUES: sign_learned, zone_checkin) and item_id (non-empty string) are required. Optional: value, score, metadata. Invalid input raises ValidationError.
  • On save, value is persisted only if a string (else null), score only if a number (else null), and metadata defaults to null when absent; item_id is trimmed.
  • DTO fields: id, progress_type, item_id, value, score, metadata, organizationId, campusId, userId, createdAt, updatedAt.
  • Model columns: progress_type (ENUM over USER_PROGRESS_TYPE_VALUES, not null), item_id (TEXT, not null), value (TEXT, nullable), score (INTEGER, nullable), metadata (JSONB, nullable), importHash (unique). Tenant/audit columns: organizationId (not null), userId (not null), createdById (not null), campusId (nullable), updatedById (nullable). The model is paranoid (soft delete via deletedAt) and uses freezeTableName.
  • Associations: belongsTo organizations (organization), campuses (campus), users (user, createdBy, updatedBy).

Behavior / Notes

  • upsert runs inside withTransaction: it looks up the existing row by organizationId + userId + progress_type + item_id, updating it if present, otherwise creating it.
  • list validates progress_type and is paginated with shared defaults.
  • removeByItem is a hard destroy call (no transaction wrapper) returning the number of rows deleted.

Tests

None yet (no user_progress unit/e2e test in src/).

  • Frontend: frontend/docs/user-progress-integration.md, frontend/docs/sign-language-integration.md, frontend/docs/zones-of-regulation-integration.md.
  • Related slices: safety-quiz-results.md, personality-quiz-results.md, walkthrough-checkins.md.