12 — API Mapping (Communication Module)
- 1. Announcements API (implemented —
announcements.controller.ts) - 2. Request/response contracts
- 3. Domain events (outbound —
EventBus) - 4. Threads & messages API (secondary surface)
- 5. RBAC — GAP
- 6. Gap list (affects client)
Exact endpoints from source. Announcement API is verbatim (
announcements.controller.ts); threads/messages listed perdocs/IMPLEMENTATION_PLAN.md:104-119. Nocommunication.*permissions exist inpermissions.constants.ts— flagged as gap. Response envelope per 00-shared/07.
1. Announcements API (implemented — announcements.controller.ts)
| # | Method | Path | Summary (from @ApiOperation) | Source |
|---|---|---|---|---|
| 1 | POST | /api/v1/announcements | Create an announcement (draft) | announcements.controller.ts:23-27 |
| 2 | GET | /api/v1/announcements | List announcements (filter by audience type) | :29-33 |
| 3 | POST | /api/v1/announcements/:id/publish | Publish and broadcast an announcement | :35-39 |
| 4 | POST | /api/v1/announcements/:id/read | Mark announcement as read | :41-45 |
| 5 | GET | /api/v1/announcements/:id/reads | Read receipts for an announcement | :47-51 |
Controller-wide: @ApiTags('communication'), @ApiBearerAuth(),
@UseGuards(JwtAuthGuard) (:16-19). No pagination, no query params beyond
?audience= (:31).
2. Request/response contracts
POST /announcements — body (create-announcement.dto.ts:29-49)
{
"title": "string ≥1",
"body": "string ≥1",
"audience": { "type": "all|role|grade|section|custom", "value": "string|string[]?" },
"attachments": ["string"] // optional
}
Response: AnnouncementDocument (announcement.schema.ts:31-53) with
published: false, createdBy from token (announcement.service.ts:31-37).
GET /announcements — query
?audience=<AudienceType> optional → filter['audience.type'] = audience
(announcement.service.ts:56-60). Returns array sorted createdAt: -1, tenant-scoped.
POST /announcements/:id/publish
Idempotent (:68-70); resolves audience → targetUserIds (:73-75,109-148);
sets published: true, publishedAt (:76-78). 404 if missing (:62-66).
POST /announcements/:id/read
$addToSet receipt {userId, readAt} — idempotent (announcement.repository.ts:20-33);
404 if missing (announcement.service.ts:100).
GET /announcements/:id/reads
→ readBy[] (announcement.service.ts:104-107) — {userId, readAt}[]
(announcement.schema.ts:23-29). Does not include targetUserIds.
3. Domain events (outbound — EventBus)
| Event | Emitted | Payload | Source |
|---|---|---|---|
AnnouncementCreated | create (draft!) | {announcementId, title, body, audienceType} | announcement.service.ts:39-51 |
AnnouncementPublished | publish | {announcementId, title, audienceType} | :80-91 |
Interface AnnouncementPublishedEvent (communication-events.ts:9-13).
Queue routing — GAP: event-queue-map.ts has no AnnouncementCreated /
AnnouncementPublished entries (it maps UserRegistered → emails,
HomeworkCreated → in-app, etc., event-queue-map.ts:6-43). Nothing consumes these
events into BullMQ today. Intended mapping (planned), following existing patterns:
AnnouncementPublished → in-app (+ emails for broadcast-all) — fan-out design pending
Notifications module (studylyon-blueprint/04-Modules/Notifications.md:34-44).
4. Threads & messages API (secondary surface)
| Method | Path | Source |
|---|---|---|
| POST | /api/v1/messages | send (1:1 or group) — IMPLEMENTATION_PLAN.md:107 |
| GET | /api/v1/messages | list my messages — :108 |
| GET | /api/v1/threads | list conversations — :109 |
| GET | /api/v1/threads/:id | thread + messages — :110 |
| POST | /api/v1/threads | create thread — :111 |
| PATCH | /api/v1/threads/:id/read | mark read — :112 |
| — | WebSocket | realtime via WsModule (planned) — :119 |
5. RBAC — GAP
No communication.* (or announcement.*) permissions exist in
permissions.constants.ts (grep: zero matches). Controller is JwtAuthGuard-only
(announcements.controller.ts:18). Closest reference: Notifications blueprint perms
notification.read / notification.send / notification.preference.manage
(studylyon-blueprint/04-Modules/Notifications.md:65-71). Client must not assume
compose/publish gating beyond auth; role gates are (planned).
6. Gap list (affects client)
| Gap | Implication |
|---|---|
No GET /announcements/:id | detail relies on carried list payload |
| No update / delete / archive routes | drafts cannot be edited; duplicates tolerated |
| No "mine" filter | client filters createdBy == me |
| No user-filter on list | client filters targetUserIds for "For me" |
| No pagination | unbounded list; (planned) when tenant grows |
| No priority/expiry fields | composer must not render them (08 §8) |
all audience ⇒ targetUserIds = [] | receipts counts must degrade (09 §3) |
No communication.* RBAC | authorship gating is client-side by createdBy for now |
| No event→queue map entries | downstream delivery (planned) |