Unknown orderBy field returns 400 instead of handled 500 - #1281
Merged
Conversation
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Make mapped metadata properties case-insensitive * Extended reflection used to determine acceptable properties as it missed some readonly and [NotMapped], which would result in 500 * Use HashSet rather than dictionary as case cleaner * Move private fields above public methods * Reduce expression builder as nested properties are blocked
donaldgray
approved these changes
Aug 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this change?
Fixes #1280.
The
orderBy/orderByDescendingvalues on asset collection endpoints (space images,/allImages, batch assets) were not validated: the value was PascalCased and fed straight intoExpression.PropertyOrField, so a field name that is not a database-backed asset property threw at runtime and surfaced to the client as a handled 500 "Unexpected error" — for what is a client mistake.Now
AssetQueryXvalidates the resolved field name against a case-insensitively keyed set of orderableAssetproperties, built once by reflection: scalar columns and primitive-collection columns (manifests,deliveryChannels— these are array columns Postgres can order by), excluding collections of related entities (adjuncts,batchAssets, ...). An unknown or unorderable name now throwsBadRequestException, which the existing exception filter turns into a clean Hydra 400 with detailCannot order by field '{value}'.Everything that ordered successfully before still does, with unchanged semantics — the whitelist is deliberately permissive (any database-backed property) rather than a curated subset, so this PR only converts failures, it doesn't narrow the working surface. Case-insensitive matching (
orderBy=WIDTH) is preserved.One deliberate behaviour change beyond the 500→400: values shorter than 2 characters were previously silently ignored (results quietly ordered by
created); they now go through the same validation, soorderBy=xis a 400 too. The old length check existed only to guard the PascalCasing and hid client mistakes.Ordering of spaces (
/customers/{id}/spaces?orderBy=) is a separate hardcoded implementation that silently ignores unknown fields rather than erroring — not touched here.Integration tests cover: four invalid names → 400 with the expected detail (including
imageService, a Hydra-model property that is not a database column, andadjuncts, an entity-collection navigation); valid names → 200 (includingWIDTHfor case-insensitivity andmanifestsfor primitive-collection ordering).Breaking Changes
Caution
This PR introduces breaking changes.
orderBy/orderByDescendingfield now return 400 (previously a handled 500 "Unexpected error")/allImages, batch assetsorderByvalues shorter than 2 characters now return 400 (previously silently ignored, results ordered bycreated)🤖 Generated with Claude Code