Skip to content

Commit 3cb8fea

Browse files
committed
docs: add public roadmap feature documentation
Document the public product roadmap feature covering Jira integration, API endpoints, caching strategy, data model, frontend components, and security measures (project isolation, privacy redaction). Signed-off-by: Asitha De Silva <asithade@gmail.com> Signed-off-by: Asitha de Silva <asithade@gmail.com>
1 parent 132844e commit 3cb8fea

File tree

1 file changed

+266
-0
lines changed

1 file changed

+266
-0
lines changed

docs/public-roadmap.md

Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
<!-- Copyright The Linux Foundation and each contributor to LFX. -->
2+
<!-- SPDX-License-Identifier: MIT -->
3+
4+
# Public Product Roadmap
5+
6+
The LFX Changelog includes a public-facing product roadmap that displays ideas from Jira in a kanban board layout. Roadmap data is **fetched from Jira and cached server-side** (no database persistence) with a 5-minute TTL, so displayed data may lag behind Jira by several minutes.
7+
8+
## Overview
9+
10+
| Component | Description |
11+
| ------------------ | -------------------------------------------------------------------------- |
12+
| **Data source** | Jira Cloud API --- project `LFX`, issue type `Idea` |
13+
| **Persistence** | None --- in-memory cache with 5-minute TTL, stale fallback on Jira failure |
14+
| **Columns** | Now, Next, Later (active); Done, Won't do (completed, opt-in) |
15+
| **Privacy** | Person names redacted to "First L." format before reaching the client |
16+
| **Authentication** | Public (no auth required) --- all endpoints under `/public/api/roadmap` |
17+
18+
## How It Works
19+
20+
### Kanban Board
21+
22+
Users visit `/roadmap` to see a kanban board with three active columns (Now, Next, Later). Each column contains idea cards showing the summary, team/goal tags, and impact/effort/votes indicators.
23+
24+
- **Team filtering** --- pill buttons at the top filter ideas by team (e.g., "PCC", "Insights")
25+
- **Show completed** --- a toggle reveals Done and Won't do columns
26+
- **Detail panel** --- clicking a card opens a slide-in panel with full details, description (ADF rendered), comments, people, and a link to the Jira issue
27+
28+
### Mobile Behavior
29+
30+
On mobile (< 768px), columns render as a single-column accordion. Column headers are tappable to collapse/expand, with the first column expanded by default.
31+
32+
## Jira Integration
33+
34+
### Data Flow
35+
36+
```text
37+
Jira Cloud API (JQL search)
38+
↓ paginated fetch (all pages)
39+
↓ field mapping (custom fields → RoadmapIdea)
40+
↓ privacy redaction (names → "First L.")
41+
↓ group by roadmapColumn
42+
Server in-memory cache (5min TTL)
43+
↓ HTTP Cache-Control headers
44+
Angular client (HttpClient)
45+
↓ reactive signals
46+
Kanban board UI
47+
```
48+
49+
### Caching Strategy
50+
51+
The server maintains two separate in-memory caches:
52+
53+
| Cache | Contents | TTL | Populated |
54+
| ------------------- | ------------------------ | ----- | ---------------- |
55+
| **Active cache** | Now, Next, Later columns | 5 min | On first request |
56+
| **Completed cache** | Done, Won't do columns | 5 min | Lazy (on demand) |
57+
58+
Both caches use a fetch-promise deduplication pattern --- concurrent requests share a single in-flight Jira API call. On Jira API failure, stale cache data is returned as a fallback.
59+
60+
HTTP cache headers are also set on responses:
61+
62+
| Endpoint | `max-age` | `stale-while-revalidate` |
63+
| ------------ | --------- | ------------------------ |
64+
| Board & idea | 300s | 60s |
65+
| Comments | 120s | 30s |
66+
67+
### Security
68+
69+
- **Project isolation** --- Jira keys are validated against the pattern `/^LFX-\d+$/` to prevent fetching data from other Jira projects
70+
- **Privacy redaction** --- person names (reporter, assignee, creator) are reduced to "First L." format before sending to the client
71+
- **No auth required** --- all roadmap endpoints are public, but only expose curated Jira data (no raw Jira fields)
72+
73+
## API Endpoints
74+
75+
All endpoints are public (no authentication required).
76+
77+
| Method | Path | Description |
78+
| ------ | --------------------------------------- | ----------------------------- |
79+
| GET | `/public/api/roadmap` | Fetch the kanban board |
80+
| GET | `/public/api/roadmap/:jiraKey` | Fetch a single idea's details |
81+
| GET | `/public/api/roadmap/:jiraKey/comments` | Fetch comments for an idea |
82+
83+
### Example Requests
84+
85+
```bash
86+
# Fetch the kanban board
87+
curl "https://changelog.lfx.dev/public/api/roadmap"
88+
89+
# Filter by team
90+
curl "https://changelog.lfx.dev/public/api/roadmap?team=PCC"
91+
92+
# Include completed columns
93+
curl "https://changelog.lfx.dev/public/api/roadmap?includeCompleted=true"
94+
95+
# Fetch a single idea
96+
curl "https://changelog.lfx.dev/public/api/roadmap/LFX-123"
97+
98+
# Fetch comments for an idea
99+
curl "https://changelog.lfx.dev/public/api/roadmap/LFX-123/comments"
100+
```
101+
102+
### GET `/public/api/roadmap`
103+
104+
Returns the kanban board grouped by column.
105+
106+
**Query parameters:**
107+
108+
| Parameter | Type | Default | Description |
109+
| ------------------ | --------- | ------- | --------------------------------------- |
110+
| `team` | `string?` | --- | Filter ideas by team name (e.g., "PCC") |
111+
| `includeCompleted` | `string?` | `false` | Include Done and Won't do columns |
112+
113+
**Response:**
114+
115+
```json
116+
{
117+
"success": true,
118+
"data": {
119+
"columns": {
120+
"Now": [{ "jiraKey": "LFX-123", "summary": "...", "teams": [...], ... }],
121+
"Next": [...],
122+
"Later": [...]
123+
},
124+
"teams": ["Insights", "PCC", "Security"],
125+
"lastFetchedAt": "2026-03-23T10:00:00.000Z"
126+
}
127+
}
128+
```
129+
130+
When `includeCompleted=true`, the `columns` object also includes `Done` and `Won't do` keys.
131+
132+
### GET `/public/api/roadmap/:jiraKey`
133+
134+
Returns a single idea with full details including description in ADF format.
135+
136+
**Path parameters:**
137+
138+
| Parameter | Type | Description |
139+
| --------- | -------- | -------------------------------- |
140+
| `jiraKey` | `string` | Jira issue key (e.g., `LFX-123`) |
141+
142+
**Response:**
143+
144+
```json
145+
{
146+
"success": true,
147+
"data": {
148+
"jiraKey": "LFX-123",
149+
"summary": "Feature title",
150+
"roadmapColumn": "Now",
151+
"teams": ["PCC"],
152+
"goals": ["Improve UX"],
153+
"category": "Enhancement",
154+
"value": 4,
155+
"effort": null,
156+
"impact": 5,
157+
"votes": 12,
158+
"status": "In Progress",
159+
"reporter": { "name": "John D.", "avatarUrl": "..." },
160+
"assignee": { "name": "Jane S.", "avatarUrl": "..." },
161+
"descriptionAdf": { ... },
162+
"jiraUrl": "https://...",
163+
"createdAt": "2026-01-15T...",
164+
"updatedAt": "2026-03-20T..."
165+
}
166+
}
167+
```
168+
169+
Returns `404` for non-existent keys or keys that don't match the `LFX-` prefix.
170+
171+
**Note:** This endpoint searches only the currently populated caches. Ideas in completed columns (Done, Won't do) are only available if the completed cache has been populated by a prior `GET /public/api/roadmap?includeCompleted=true` request. A direct lookup for a completed idea may return `404` if no one has requested completed columns since the last cache expiry.
172+
173+
### GET `/public/api/roadmap/:jiraKey/comments`
174+
175+
Returns Jira comments for an idea, ordered newest first (max 50).
176+
177+
**Response:**
178+
179+
```json
180+
{
181+
"success": true,
182+
"data": [
183+
{
184+
"author": { "name": "John D.", "avatarUrl": "..." },
185+
"bodyAdf": { ... },
186+
"createdAt": "2026-03-20T..."
187+
}
188+
]
189+
}
190+
```
191+
192+
Returns an empty array for invalid or non-existent keys (does not 404).
193+
194+
## Data Model
195+
196+
Roadmap data is not persisted in the database --- it is fetched from Jira and cached in memory. See the Zod schemas in `packages/shared/src/schemas/roadmap.schema.ts` for exact field definitions.
197+
198+
- **RoadmapIdea** --- a single idea from the Jira roadmap board. Each idea has a Jira key (`LFX-123`), summary, kanban column (Now/Next/Later/Done/Won't do), team and goal tags, optional scoring fields (value, effort, impact --- all nullable, 0--5 when present), vote count, Jira workflow status, people (reporter, creator, assignee --- all nullable and privacy-redacted), an optional ADF description, and a Jira URL.
199+
200+
- **RoadmapPerson** --- a privacy-redacted person reference with a name in "First L." format and an optional avatar URL.
201+
202+
- **RoadmapComment** --- a Jira comment with a redacted author, ADF body, and creation timestamp.
203+
204+
## Frontend
205+
206+
The roadmap UI is implemented as a lazy-loaded Angular component at `src/app/modules/public/roadmap/`, mounted at the `/roadmap` route.
207+
208+
```text
209+
RoadmapBoardComponent (main container)
210+
├── Team filter pills
211+
├── Show completed toggle
212+
├── RoadmapColumnComponent x 3--5 (one per active/completed column)
213+
│ └── RoadmapCardComponent x N (one per idea)
214+
└── RoadmapDetailPanelComponent (slide-in panel, opened on card click)
215+
```
216+
217+
- **Streaming updates** --- the board re-fetches automatically when filters (team, show completed) change
218+
- **Parallel detail loading** --- idea details and comments load independently with separate loading states
219+
- **ADF rendering** --- description and comments use `AdfToHtmlPipe` to render Atlassian Document Format as HTML
220+
221+
## Environment Variables
222+
223+
| Variable | Required | Description |
224+
| -------------------- | -------- | --------------------------------------- |
225+
| `ATLASSIAN_EMAIL` | Yes | Email for Jira Cloud API basic auth |
226+
| `ATLASSIAN_API_KEY` | Yes | API token for Jira Cloud API basic auth |
227+
| `ATLASSIAN_CLOUD_ID` | Yes | Jira Cloud instance ID |
228+
229+
## Architecture
230+
231+
```text
232+
packages/shared/src/
233+
├── schemas/
234+
│ └── roadmap.schema.ts # Zod schemas (RoadmapIdea, RoadmapComment, etc.)
235+
└── constants/
236+
└── roadmap.constant.ts # Jira field IDs, column definitions, cache TTLs
237+
238+
apps/lfx-changelog/src/server/
239+
├── controllers/
240+
│ └── roadmap.controller.ts # 3 route handlers (board, idea, comments)
241+
├── services/
242+
│ └── roadmap.service.ts # Jira API sync, caching, field mapping, privacy
243+
├── routes/
244+
│ └── public-roadmap.route.ts # Express routes with cache middleware
245+
└── swagger/paths/
246+
└── public-roadmap.path.ts # OpenAPI spec definitions
247+
248+
apps/lfx-changelog/src/app/
249+
├── shared/services/
250+
│ └── roadmap.service.ts # Angular HttpClient wrapper
251+
└── modules/public/roadmap/
252+
├── roadmap-board/ # Main board component (container)
253+
└── components/
254+
├── roadmap-card/ # Individual idea card
255+
├── roadmap-column/ # Column container (accordion on mobile)
256+
└── roadmap-detail-panel/ # Slide-in detail panel
257+
258+
apps/lfx-changelog/e2e/
259+
├── pages/
260+
│ └── roadmap-board.page.ts # Playwright page object
261+
└── specs/
262+
├── public/
263+
│ └── roadmap-board.spec.ts # UI tests (24 tests)
264+
└── api/
265+
└── public-roadmap.api.spec.ts # API contract tests (18 tests)
266+
```

0 commit comments

Comments
 (0)