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>
10 KiB
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
- T001 Add
<!-- OG_META_TAGS -->placeholder comment in<head>offrontend/index.html - T002 [P] Create
og-image.pngbrand image (1200x630) infrontend/public/og-image.png - T003 [P] Add
server.forward-headers-strategy=frameworktobackend/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
- T004 Write unit tests for
MetaTagRenderer(HTML escaping, meta-tag HTML output) inbackend/src/test/java/de/fete/adapter/in/web/MetaTagRendererTest.java— tests MUST fail (Red) - T005 Implement
MetaTagRendererutility that renders meta-tag key-value pairs into HTML<meta>strings with proper HTML escaping inbackend/src/main/java/de/fete/adapter/in/web/MetaTagRenderer.java - T006 Write integration tests for
SpaControllerbase functionality (serves index.html, replaces placeholder) inbackend/src/test/java/de/fete/adapter/in/web/SpaControllerTest.java— tests MUST fail (Red) - T007 Implement
SpaControllerthat cachesindex.htmltemplate at startup and replaces<!-- OG_META_TAGS -->placeholder before serving inbackend/src/main/java/de/fete/adapter/in/web/SpaController.java - T008 Remove
PathResourceResolverSPA fallback frombackend/src/main/java/de/fete/config/WebConfig.java(replaced bySpaController)
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
- T009 [P] [US1] Unit tests for
OpenGraphService.buildEventMeta()— full event data, no location, no description, long title truncation, special characters inbackend/src/test/java/de/fete/application/OpenGraphServiceTest.java - T010 [P] [US1] Integration tests for
SpaControllerevent routes — GET/events/{token}returns HTML with event-specific OG meta-tags, event not found falls back to generic inbackend/src/test/java/de/fete/adapter/in/web/SpaControllerTest.java
Implementation for User Story 1
- T011 [US1] Implement
OpenGraphService.buildEventMeta()— fetch event by token, composeog:title(truncated 70 chars),og:description(date + location + description, max 200 chars),og:url,og:type,og:site_name,og:imageinbackend/src/main/java/de/fete/application/service/OpenGraphService.java - T012 [US1] Wire
SpaControllerto callOpenGraphServicefor/events/{token}routes, inject event-specific meta-tags viaMetaTagRendererinbackend/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
- T014 [P] [US2] Unit tests for
OpenGraphService.buildGenericMeta()— verify generic title "fete", generic description, correct URL, image URL inbackend/src/test/java/de/fete/application/service/OpenGraphServiceTest.java - T015 [P] [US2] Integration tests for
SpaControllergeneric routes — GET/, GET/createreturn HTML with generic OG meta-tags inbackend/src/test/java/de/fete/adapter/in/web/SpaControllerTest.java
Implementation for User Story 2
- T016 [US2] Implement
OpenGraphService.buildGenericMeta()— title "fete", description "Privacy-focused event planning. Create and share events without accounts.", type "website", site_name "fete" inbackend/src/main/java/de/fete/application/service/OpenGraphService.java - T017 [US2] Wire
SpaControllerto callOpenGraphService.buildGenericMeta()for all non-event HTML routes inbackend/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
- T019 [P] [US3] Unit tests for Twitter Card meta-tag generation — verify
twitter:card= "summary",twitter:title,twitter:descriptionmatch OG values inbackend/src/test/java/de/fete/application/service/OpenGraphServiceTest.java - T020 [P] [US3] Integration tests for
SpaController— event and generic routes include Twitter Card meta-tags inbackend/src/test/java/de/fete/adapter/in/web/SpaControllerTest.java
Implementation for User Story 3
- T021 [US3] Extend
OpenGraphServiceto include Twitter Card meta-tags (twitter:card,twitter:title,twitter:description) alongside OG tags inbackend/src/main/java/de/fete/application/service/OpenGraphService.java - T022 [US3] Extend
MetaTagRendererto render<meta name="twitter:*">tags (usingnameattribute instead ofproperty) inbackend/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
- 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
- T025 [P] Verify
SpaControllerdoes not intercept static asset requests — SpaController only handles explicit routes, not wildcard - T026 Run full backend test suite (
cd backend && ./mvnw verify) and fix any regressions — 97 tests, 0 bugs - 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
# 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)
- Complete Phase 1: Setup (T001–T003)
- Complete Phase 2: Foundational (T004–T008)
- Complete Phase 3: User Story 1 (T009–T013)
- STOP and VALIDATE: Share an event link in a messenger, verify preview card
- Deploy/demo if ready
Incremental Delivery
- Setup + Foundational → SpaController serving index.html with placeholder replacement
- Add US1 → Event links show rich previews → Deploy (MVP!)
- Add US2 → Generic pages also show previews → Deploy
- Add US3 → Twitter/X cards work too → Deploy
- 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