Skip to content

Skip validation when decoding API responses#54

Merged
chiiya merged 1 commit into
masterfrom
fix/lenient-response-decoding
May 29, 2026
Merged

Skip validation when decoding API responses#54
chiiya merged 1 commit into
masterfrom
fix/lenient-response-decoding

Conversation

@chiiya

@chiiya chiiya commented May 29, 2026

Copy link
Copy Markdown
Owner

Problem

Fixes chiiya/laravel-passes#40.

Updating (or fetching) a pass decodes the API response back into component objects via BaseRepository. Component::__construct() runs Symfony validation on every construction — including during decode() — so a response that legitimately omits fields only required when creating a pass throws spurious validation errors:

Validation errors:
  - Chiiya\Passes\Google\Passes\LoyaltyClass->programName: The field is required.
  - Chiiya\Passes\Google\Passes\LoyaltyClass->issuerName: The field is required.

The reporter correctly traced it to BaseRepository::update(), which builds a LoyaltyObject from the response whose classReference is partial.

Root cause

Requiredness/validity is a send-time concern, but it was being enforced at parse-time. We don't control what Google returns, so decoding must be lenient.

Fix

Skip validation while decoding. Component::decode() toggles a guard that makes __construct() bypass validation, and restores the previous value in a finally (so nested decoding — e.g. classReference inside an object — is handled correctly). Validation still runs for objects you construct directly to send, so invalid outgoing data is still caught.

This resolves the whole class of "validation failed while decoding a response" bugs, not just the loyalty case.

Scope note

A response can also fail to decode if it omits a field whose property is non-nullable (e.g. LoyaltyClass::$programLogo), which would throw a TypeError rather than a validation error. In practice a real classReference includes the class's genuinely-required fields, so this isn't hit by #40. Making every decodable required field nullable would touch ~60 fields across ~38 files and is intentionally left out of this PR.

Tests

Added tests/Common/ComponentTest.php:

  • decoding a response with an unexpected constraint value does not throw
  • constructing the same object directly still throws ValidationException
  • decoding a LoyaltyObject with a nested partial classReference (the Bump actions/checkout from 4 to 5 #40 path) succeeds

Full suite passes (57 tests); just lint clean.

Component::__construct() ran Symfony validation on every construction, including
while decoding API responses via decode(). Responses may legitimately omit fields
that are only required when creating a pass (e.g. a partial classReference when
updating a loyalty object), so decoding threw spurious validation errors.

Skip validation during decode() (restored afterwards, so nested decoding is
handled). Validation still runs for objects constructed directly to be sent.

Fixes chiiya/laravel-passes#40
@chiiya chiiya merged commit a1f3b49 into master May 29, 2026
3 checks passed
@chiiya chiiya deleted the fix/lenient-response-decoding branch May 29, 2026 14:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

LoyaltyObject validation issue when updating loyalty passes

1 participant