Add RSVP creation endpoint with typed tokens and attendee count

Introduce typed token value objects (EventToken, OrganizerToken,
RsvpToken) and refactor all existing Event code to use them.

Add POST /events/{token}/rsvps endpoint that persists an RSVP and
returns an rsvpToken. Populate attendeeCount in GET /events/{token}
from a real count query instead of hardcoded 0.

Includes: OpenAPI spec, Liquibase migration (rsvps table with
ON DELETE CASCADE), domain model, hexagonal ports/adapters,
service layer, and full test coverage (unit + integration).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-08 11:49:49 +01:00
parent 4828d06aba
commit a625e34fe4
24 changed files with 688 additions and 39 deletions

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
<changeSet id="003-create-rsvps-table" author="fete">
<createTable tableName="rsvps">
<column name="id" type="bigserial" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="rsvp_token" type="uuid">
<constraints nullable="false" unique="true"/>
</column>
<column name="event_id" type="bigint">
<constraints nullable="false"
foreignKeyName="fk_rsvps_event_id"
references="events(id)"
deleteCascade="true"/>
</column>
<column name="name" type="varchar(100)">
<constraints nullable="false"/>
</column>
</createTable>
<createIndex tableName="rsvps" indexName="idx_rsvps_event_id">
<column name="event_id"/>
</createIndex>
<createIndex tableName="rsvps" indexName="idx_rsvps_rsvp_token">
<column name="rsvp_token"/>
</createIndex>
</changeSet>
</databaseChangeLog>

View File

@@ -8,5 +8,6 @@
<include file="db/changelog/000-baseline.xml"/>
<include file="db/changelog/001-create-events-table.xml"/>
<include file="db/changelog/002-add-timezone-column.xml"/>
<include file="db/changelog/003-create-rsvps-table.xml"/>
</databaseChangeLog>

View File

@@ -37,6 +37,52 @@ paths:
schema:
$ref: "#/components/schemas/ValidationProblemDetail"
/events/{token}/rsvps:
post:
operationId: createRsvp
summary: Submit an RSVP for an event
tags:
- events
parameters:
- name: token
in: path
required: true
schema:
type: string
format: uuid
description: Public event token
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/CreateRsvpRequest"
responses:
"201":
description: RSVP created successfully
content:
application/json:
schema:
$ref: "#/components/schemas/CreateRsvpResponse"
"400":
description: Validation failed (e.g. blank name)
content:
application/problem+json:
schema:
$ref: "#/components/schemas/ValidationProblemDetail"
"404":
description: Event not found
content:
application/problem+json:
schema:
$ref: "#/components/schemas/ProblemDetail"
"409":
description: Event has expired — RSVPs no longer accepted
content:
application/problem+json:
schema:
$ref: "#/components/schemas/ProblemDetail"
/events/{token}:
get:
operationId: getEvent
@@ -182,6 +228,34 @@ components:
description: Whether the event's expiry date has passed
example: false
CreateRsvpRequest:
type: object
required:
- name
properties:
name:
type: string
minLength: 1
maxLength: 100
description: Guest's display name
example: "Max Mustermann"
CreateRsvpResponse:
type: object
required:
- rsvpToken
- name
properties:
rsvpToken:
type: string
format: uuid
description: Token identifying this RSVP (store client-side for future updates)
example: "d4e5f6a7-b8c9-0123-4567-890abcdef012"
name:
type: string
description: Guest's display name as stored
example: "Max Mustermann"
ProblemDetail:
type: object
properties: