Commit Graph

3 Commits

Author SHA1 Message Date
0cbdad70cd fix(api): accept submitter details on public draft PUT and submit POST
S3a PR 1 frontend sends public_submitter_name and public_submitter_email
on draft saves (PUT) and final submit (POST /submit), but the matching
SavePublicDraftRequest and SubmitPublicSubmissionRequest did not whitelist
these fields — Laravel's validated() silently stripped them, preventing
mid-form name/email updates from persisting.

Align both form requests with StartPublicDraftRequest to accept the same
submitter fields with identical rules (string, max:150 / email, max:255,
nullable). Controller copies present keys onto the submission model and
saves when dirty, matching standard Laravel update() semantics — missing
keys leave prior values untouched.

Closes the backend gap identified in PR 1 smoke test.
2026-04-23 16:36:31 +02:00
6ba921442c fix(form-builder): explicit OrganisationScope bypass on every public-form query
Five models that the public form endpoints touch carry a global
OrganisationScope: FormSchema, Event, TimeSlot, FestivalSection,
PersonTag. The initial S2c implementation relied on the scope no-opping
because /public/forms/* has no `{organisation}` route parameter and
OrganisationScope::resolveOrganisationId returns null in that case.

That's accidentally-correct. Any middleware that sets an implicit org
context later (route model binding for platform admin, impersonation,
default-org fallback on an authed Sanctum session) would start
filtering public schema resolution by the wrong org.

- PublicFormTokenResolver: both FormSchema::query() calls now pass
  withoutGlobalScope(OrganisationScope::class). public_token is
  globally unique so this is safe.
- PublicFormController::timeSlots() / sections() / festivalEventIds():
  Event, TimeSlot, FestivalSection queries all explicit now, including
  the eager-loaded event relation on time-slots.
- PublicFormController::ownerEvent(): narrowed from
  Event::withoutGlobalScopes() to withoutGlobalScope(OrganisationScope)
  so future scopes (soft-delete, archived) aren't accidentally
  stripped.
- PublicFormSchemaResource::availableTagsByCategory: same narrowing on
  the PersonTag query.

PublicFormCrossOrgScopeTest pins the expectation — 4 cases hit every
public endpoint under a stashed foreign-org route parameter and assert
the owner-org data still surfaces. Verified the tests fail when the
fix is reverted (all 4 return `SCHEMA_NOT_FOUND` with the bypass
absent).

Full suite 893 → 897 green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 23:16:22 +02:00
9b1bf0e13d test(form-builder): public form API — 36 new tests covering S2c deliverables
Eight new feature test files under tests/Feature/Api/V1/Public/FormBuilder/.
Full suite 857 → 893 green.

- PublicFormSchemaResourceTest (3) — TAG_PICKER available_tags grouped
  by category, tag_categories filter, version + opened_at top-level.
- PublicFormTimeSlotsTest (4) — volunteer-only filter, festival
  children included, 410 TOKEN_EXPIRED on rotated-past-grace, 404
  SCHEMA_NOT_FOUND on unknown token.
- PublicFormSectionsTest (2) — show_in_registration + type=standard
  filter, dedup-by-name across festival children.
- PublicFormDraftLifecycleTest (8) — POST creates draft (201),
  idempotent replay returns 200 w/ same id, idempotency_key required,
  PUT partial update increments auto_save_count, submit happy path,
  409 SUBMISSION_ALREADY_SUBMITTED on re-submit, schema_drift flagged
  when organiser edits mid-draft, 404 when submission_id belongs to
  another schema.
- PublicFormValidationTest (6) — EMAIL format, NUMBER type, SELECT
  option list, NUMBER min/max from validation_rules, required-at-submit
  enforcement, draft-save tolerates missing required.
- PublicFormSubmissionResourceTest (3) — no PII echo
  (public_submitter_name/email/ip suppressed), admin metadata
  (review_status/schema_snapshot/reviewed_by) absent, identity_match
  shape with Dutch message on pending.
- PublicFormErrorEnvelopeTest (5) — SCHEMA_NOT_FOUND, TOKEN_EXPIRED,
  SCHEMA_UNPUBLISHED codes; 422 VALIDATION_FAILED carries errors; 429
  RATE_LIMITED carries Retry-After header.
- IdentityMatchOnSubmitTest (5) — event_registration triggers
  matched/none/pending per person state; non-event_registration purpose
  does not trigger; public-subject submissions write pending.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 23:03:28 +02:00