Replace PathResourceResolver SPA fallback with SpaController that injects OG/Twitter meta-tags into cached index.html template. Event pages get event-specific tags (title, date, location), all other pages get generic fete branding. Includes og-image.png brand asset and forward-headers-strategy for proxy support. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
202 lines
10 KiB
Markdown
202 lines
10 KiB
Markdown
# Tasks: Link Preview (Open Graph Meta-Tags)
|
||
|
||
**Input**: Design documents from `/specs/012-link-preview/`
|
||
**Prerequisites**: plan.md, spec.md, research.md, data-model.md, contracts/
|
||
|
||
**Tests**: Included — constitution mandates TDD (Red → Green → Refactor).
|
||
|
||
**Organization**: Tasks grouped by user story for independent implementation and testing.
|
||
|
||
## Format: `[ID] [P?] [Story] Description`
|
||
|
||
- **[P]**: Can run in parallel (different files, no dependencies)
|
||
- **[Story]**: Which user story this task belongs to (e.g., US1, US2, US3)
|
||
- Include exact file paths in descriptions
|
||
|
||
---
|
||
|
||
## Phase 1: Setup
|
||
|
||
**Purpose**: Prepare frontend template and static assets for meta-tag injection
|
||
|
||
- [x] T001 Add `<!-- OG_META_TAGS -->` placeholder comment in `<head>` of `frontend/index.html`
|
||
- [x] T002 [P] Create `og-image.png` brand image (1200x630) in `frontend/public/og-image.png`
|
||
- [x] T003 [P] Add `server.forward-headers-strategy=framework` to `backend/src/main/resources/application.properties`
|
||
|
||
---
|
||
|
||
## Phase 2: Foundational (Blocking Prerequisites)
|
||
|
||
**Purpose**: Core infrastructure for HTML meta-tag injection that ALL user stories depend on
|
||
|
||
**⚠️ CRITICAL**: No user story work can begin until this phase is complete
|
||
|
||
- [x] T004 Write unit tests for `MetaTagRenderer` (HTML escaping, meta-tag HTML output) in `backend/src/test/java/de/fete/adapter/in/web/MetaTagRendererTest.java` — tests MUST fail (Red)
|
||
- [x] T005 Implement `MetaTagRenderer` utility that renders meta-tag key-value pairs into HTML `<meta>` strings with proper HTML escaping in `backend/src/main/java/de/fete/adapter/in/web/MetaTagRenderer.java`
|
||
- [x] T006 Write integration tests for `SpaController` base functionality (serves index.html, replaces placeholder) in `backend/src/test/java/de/fete/adapter/in/web/SpaControllerTest.java` — tests MUST fail (Red)
|
||
- [x] T007 Implement `SpaController` that caches `index.html` template at startup and replaces `<!-- OG_META_TAGS -->` placeholder before serving in `backend/src/main/java/de/fete/adapter/in/web/SpaController.java`
|
||
- [x] T008 Remove `PathResourceResolver` SPA fallback from `backend/src/main/java/de/fete/config/WebConfig.java` (replaced by `SpaController`)
|
||
|
||
**Checkpoint**: SPA still works (index.html served for all non-API/non-static routes), but now through `SpaController` with placeholder replacement ready
|
||
|
||
---
|
||
|
||
## Phase 3: User Story 1 — Event Link Preview in Messenger (Priority: P1) 🎯 MVP
|
||
|
||
**Goal**: Shared event links display rich preview cards with event title, date, location, and description in messengers
|
||
|
||
**Independent Test**: Share an event URL — messenger shows event title and formatted description with date/location
|
||
|
||
### Tests for User Story 1 ⚠️
|
||
|
||
> **NOTE: Write these tests FIRST, ensure they FAIL before implementation**
|
||
|
||
- [x] T009 [P] [US1] Unit tests for `OpenGraphService.buildEventMeta()` — full event data, no location, no description, long title truncation, special characters in `backend/src/test/java/de/fete/application/OpenGraphServiceTest.java`
|
||
- [x] T010 [P] [US1] Integration tests for `SpaController` event routes — GET `/events/{token}` returns HTML with event-specific OG meta-tags, event not found falls back to generic in `backend/src/test/java/de/fete/adapter/in/web/SpaControllerTest.java`
|
||
|
||
### Implementation for User Story 1
|
||
|
||
- [x] T011 [US1] Implement `OpenGraphService.buildEventMeta()` — fetch event by token, compose `og:title` (truncated 70 chars), `og:description` (date + location + description, max 200 chars), `og:url`, `og:type`, `og:site_name`, `og:image` in `backend/src/main/java/de/fete/application/service/OpenGraphService.java`
|
||
- [x] T012 [US1] Wire `SpaController` to call `OpenGraphService` for `/events/{token}` routes, inject event-specific meta-tags via `MetaTagRenderer` in `backend/src/main/java/de/fete/adapter/in/web/SpaController.java`
|
||
- [ ] T013 [US1] E2E test — deferred (requires running backend; covered by integration tests)
|
||
|
||
**Checkpoint**: Event links show rich OG preview cards in messengers. MVP complete.
|
||
|
||
---
|
||
|
||
## Phase 4: User Story 2 — Fallback Preview for Generic Pages (Priority: P2)
|
||
|
||
**Goal**: Non-event pages (homepage, event list, create) show a meaningful generic fete preview when shared
|
||
|
||
**Independent Test**: Share the homepage URL — messenger shows "fete" as title and generic app description
|
||
|
||
### Tests for User Story 2 ⚠️
|
||
|
||
> **NOTE: Write these tests FIRST, ensure they FAIL before implementation**
|
||
|
||
- [x] T014 [P] [US2] Unit tests for `OpenGraphService.buildGenericMeta()` — verify generic title "fete", generic description, correct URL, image URL in `backend/src/test/java/de/fete/application/service/OpenGraphServiceTest.java`
|
||
- [x] T015 [P] [US2] Integration tests for `SpaController` generic routes — GET `/`, GET `/create` return HTML with generic OG meta-tags in `backend/src/test/java/de/fete/adapter/in/web/SpaControllerTest.java`
|
||
|
||
### Implementation for User Story 2
|
||
|
||
- [x] T016 [US2] Implement `OpenGraphService.buildGenericMeta()` — title "fete", description "Privacy-focused event planning. Create and share events without accounts.", type "website", site_name "fete" in `backend/src/main/java/de/fete/application/service/OpenGraphService.java`
|
||
- [x] T017 [US2] Wire `SpaController` to call `OpenGraphService.buildGenericMeta()` for all non-event HTML routes in `backend/src/main/java/de/fete/adapter/in/web/SpaController.java`
|
||
- [ ] T018 [US2] E2E test — deferred (requires running backend; covered by integration tests)
|
||
|
||
**Checkpoint**: All shared fete links (event-specific and generic) show rich preview cards
|
||
|
||
---
|
||
|
||
## Phase 5: User Story 3 — Twitter/X Card Support (Priority: P3)
|
||
|
||
**Goal**: Links shared on Twitter/X also display rich preview cards via Twitter Card meta-tags
|
||
|
||
**Independent Test**: Verify HTML source contains `twitter:card`, `twitter:title`, `twitter:description` meta-tags
|
||
|
||
### Tests for User Story 3 ⚠️
|
||
|
||
> **NOTE: Write these tests FIRST, ensure they FAIL before implementation**
|
||
|
||
- [x] T019 [P] [US3] Unit tests for Twitter Card meta-tag generation — verify `twitter:card` = "summary", `twitter:title`, `twitter:description` match OG values in `backend/src/test/java/de/fete/application/service/OpenGraphServiceTest.java`
|
||
- [x] T020 [P] [US3] Integration tests for `SpaController` — event and generic routes include Twitter Card meta-tags in `backend/src/test/java/de/fete/adapter/in/web/SpaControllerTest.java`
|
||
|
||
### Implementation for User Story 3
|
||
|
||
- [x] T021 [US3] Extend `OpenGraphService` to include Twitter Card meta-tags (`twitter:card`, `twitter:title`, `twitter:description`) alongside OG tags in `backend/src/main/java/de/fete/application/service/OpenGraphService.java`
|
||
- [x] T022 [US3] Extend `MetaTagRenderer` to render `<meta name="twitter:*">` tags (using `name` attribute instead of `property`) in `backend/src/main/java/de/fete/adapter/in/web/MetaTagRenderer.java`
|
||
- [ ] T023 [US3] E2E test — fetch event page and homepage, verify Twitter Card meta-tags present alongside OG tags in `frontend/e2e/link-preview.spec.ts`
|
||
|
||
**Checkpoint**: All three user stories complete — OG tags, generic fallback, and Twitter Cards all working
|
||
|
||
---
|
||
|
||
## Phase 6: Polish & Cross-Cutting Concerns
|
||
|
||
**Purpose**: Edge cases, hardening, and final verification
|
||
|
||
- [x] T024 [P] Verify HTML escaping for special characters (quotes, ampersands, angle brackets) in meta-tag values across all routes — edge-case tests in MetaTagRendererTest.java
|
||
- [x] T025 [P] Verify `SpaController` does not intercept static asset requests — SpaController only handles explicit routes, not wildcard
|
||
- [x] T026 Run full backend test suite (`cd backend && ./mvnw verify`) and fix any regressions — 97 tests, 0 bugs
|
||
- [x] T027 Run full frontend test suite (`cd frontend && npm run test:unit`) — 133 tests passed
|
||
|
||
---
|
||
|
||
## Dependencies & Execution Order
|
||
|
||
### Phase Dependencies
|
||
|
||
- **Setup (Phase 1)**: No dependencies — can start immediately
|
||
- **Foundational (Phase 2)**: Depends on T001 (placeholder in index.html) — BLOCKS all user stories
|
||
- **US1 (Phase 3)**: Depends on Foundational phase completion
|
||
- **US2 (Phase 4)**: Depends on Foundational phase completion — can run in parallel with US1
|
||
- **US3 (Phase 5)**: Depends on US1 or US2 (extends their meta-tag output)
|
||
- **Polish (Phase 6)**: Depends on all user stories being complete
|
||
|
||
### User Story Dependencies
|
||
|
||
- **User Story 1 (P1)**: Can start after Foundational (Phase 2) — no dependencies on other stories
|
||
- **User Story 2 (P2)**: Can start after Foundational (Phase 2) — independent from US1
|
||
- **User Story 3 (P3)**: Depends on US1 or US2 — extends existing OG meta-tag output with Twitter tags
|
||
|
||
### Within Each User Story
|
||
|
||
- Tests MUST be written and FAIL before implementation (TDD Red phase)
|
||
- Service layer before controller wiring
|
||
- Unit tests before integration tests before E2E tests
|
||
- Story complete before moving to next priority
|
||
|
||
### Parallel Opportunities
|
||
|
||
- T001, T002, T003 can all run in parallel (Setup phase)
|
||
- T004 and T006 can run in parallel (Foundational tests — different files)
|
||
- T009, T010 can run in parallel (US1 tests — different files)
|
||
- T014, T015 can run in parallel (US2 tests — different files)
|
||
- T019, T020 can run in parallel (US3 tests — different files)
|
||
- US1 and US2 can be worked on in parallel after Foundational phase
|
||
|
||
---
|
||
|
||
## Parallel Example: User Story 1
|
||
|
||
```bash
|
||
# Launch US1 tests in parallel (Red phase):
|
||
Task: "Unit tests for OpenGraphService.buildEventMeta() in OpenGraphServiceTest.java"
|
||
Task: "Integration tests for SpaController event routes in SpaControllerTest.java"
|
||
|
||
# Then implement sequentially:
|
||
Task: "Implement OpenGraphService.buildEventMeta()"
|
||
Task: "Wire SpaController for /events/{token} routes"
|
||
Task: "E2E test for event page meta-tags"
|
||
```
|
||
|
||
---
|
||
|
||
## Implementation Strategy
|
||
|
||
### MVP First (User Story 1 Only)
|
||
|
||
1. Complete Phase 1: Setup (T001–T003)
|
||
2. Complete Phase 2: Foundational (T004–T008)
|
||
3. Complete Phase 3: User Story 1 (T009–T013)
|
||
4. **STOP and VALIDATE**: Share an event link in a messenger, verify preview card
|
||
5. Deploy/demo if ready
|
||
|
||
### Incremental Delivery
|
||
|
||
1. Setup + Foundational → SpaController serving index.html with placeholder replacement
|
||
2. Add US1 → Event links show rich previews → Deploy (MVP!)
|
||
3. Add US2 → Generic pages also show previews → Deploy
|
||
4. Add US3 → Twitter/X cards work too → Deploy
|
||
5. Polish → Edge cases hardened → Final release
|
||
|
||
---
|
||
|
||
## Notes
|
||
|
||
- [P] tasks = different files, no dependencies
|
||
- [Story] label maps task to specific user story for traceability
|
||
- Each user story is independently completable and testable
|
||
- TDD enforced: write tests first, verify they fail, then implement
|
||
- Commit after each task or logical group
|
||
- Stop at any checkpoint to validate story independently
|