Skip to content

fix(api): enum validation messages (#363) + booking-requests guardrail - #364

Merged
telivity-otaip merged 2 commits into
mainfrom
cursor/enum-validation-booking-requests-guard-4a4f
Sep 9, 2026
Merged

telivity-otaip merged 2 commits into
mainfrom
cursor/enum-validation-booking-requests-guard-4a4f

Conversation

@telivity-otaip

@telivity-otaip telivity-otaip commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

Charles’s #363 in full, plus one maintainer hygiene commit we could not push to his fork (telivity-otaip / cursor[bot] both 403 on modernit-com-au/haip even with “allow edits from maintainers”).

His work: @IsEnum([...])@IsIn([...]) so 400s name the allowed values. Guardrail spec on class-validator metadata.

Hygiene (1cc3595): eager-load packages/booking-requests/src/http/dto/** in that spec so a future @IsEnum there cannot sneak past.

Merge this and close #363 as superseded, or merge #363 as-is and ignore this. Full credit to @modernitconsultants .

modernitconsultants and others added 2 commits September 9, 2026 22:26
Post a folio charge with type "beverage" and the API answers

    type must be one of the following values:

with nothing after the colon. The caller is told they are wrong and never
told what right looks like.

class-validator's IsEnum is for TypeScript enum objects. It validates with
Object.keys(entity).map(k => entity[k]), which happens to read an array's
values, so the check works; but it builds its message from
Object.entries(entity).filter(([k]) => isNaN(parseInt(k))), which drops every
key of an array because array keys are all numeric. So the allowed-value list
comes out empty. IsIn is the decorator for a list of allowed values, and
interpolates it.

Every enum decorator in the API was affected: 88 across 64 files, covering
accounting, folio, payment, reservation, rate plan, housekeeping, media,
groups, waitlist and the booking-requests package.

Validation behaviour does not change. Both decorators compare the same value
against the same list.

apps/api/src/validation-messages.spec.ts reads class-validator's metadata
registry rather than the source, so it covers the copies PartialType and
OmitType generate, which no grep sees. It asserts a rendered message too, and
that is what caught the first version of the metadata assertion: it filtered
on `type`, which is 'customValidation' for every ValidateBy decorator, so it
matched nothing and passed with an @IsEnum deliberately planted.
Maintainer hygiene on #363: the metadata registry check only loaded
apps/api modules/**/dto, so a future @IsEnum([...]) in
packages/booking-requests could regress unnoticed. Eager-load those
HTTP DTOs too and assert ListBookingRequestsDto registers.

Co-authored-by: telivity-otaip <telivity-otaip@users.noreply.github.com>
@telivity-otaip
telivity-otaip merged commit f1db91b into main Sep 9, 2026
5 checks passed
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.

3 participants