feat(server): say what was wrong with a rejected filter - #7061
Open
geovannewashington wants to merge 1 commit into
Open
geovannewashington wants to merge 1 commit into
geovannewashington wants to merge 1 commit into
Conversation
ValidateFilters answered six causes with one sentinel and applyQuery answered seven with two strings, so a client could not tell a typo in a field name from a filter that was merely too big. The reason is written at the gateway rather than taken from the sentinel's own text: a message the mapping did not write is not one a client was promised. ErrFilterShapeInvalid and ErrFilterValueTooLarge exist because their causes were being described by a message that was false for them. JSON that parses but is not a filter was told it was not JSON, and a value of an acceptable type that was merely too long was told it had the wrong type: the same misdirection this commit removes elsewhere. Unmarshal now splits on json.SyntaxError, which is the only failure that means the bytes are not JSON. The echoed identifier is capped because the field name is client-supplied and only filter values are length-bounded. 64 runes is chosen to exceed every field any contract in either repo names, so no honest request is ever truncated. A cut name is marked with an ellipsis, because a silently shortened one reads as a real field the client never sent. The switch's trailing "is not valid" is the pre-existing wording for a sentinel the mapping does not know. Nothing forces a new ErrFilter* to be added here, so a future one loses its message with no compiler or test signal. Left as is rather than guarded, since the guard wants an in-package test and this package is tested from outside. Fixes: #6467
Code Review CompleteThe automated review ran but did not post an updated summary — this usually means no new issues were found since the previous review. If you've pushed changes and want a fresh pass, comment |
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
A rejected
filternow names its own reason in thefieldsmap the error envelope already carries. Nine distinct causes get nine distinct reasons where there used to be two strings. The envelope, the 400 status and the OpenAPI schema are unchanged, so a client that only reads the status code is unaffected.Companion PR: shellhub-io/cloud#2543 , which moves cloud's three list routes onto the same seam so they answer identically. Merge this one first.
Closes #6467
Why
ValidateFiltersreturned one sentinel for six causes andapplyQuerycollapsed seven into two strings, so a script author could not tell a typo in a field name from a filter that was merely too big. The server knew the difference at the moment it decided to refuse, and threw it away one call before anything needed it."filter": "is not valid""filter": "names an unknown field: nmae""filter": "is not valid""filter": "operator gt is not valid for name""filter": "is not valid""filter": "value exceeds the maximum size for name""filter": "cannot be decoded""filter": "is not valid base64"Changes
pkg/api/queryclassifies its own refusals instead of discarding the classification.FieldConstraints.Namesseparates "no such field" from "that operator is not allowed here", whichAllowsused to answer as one. Three sentinels are added:ErrFilterNotBase64,ErrFilterTooManyItems, andErrFilterShapeInvalidfor JSON that parses but is not a filter.ErrFilterValueTooLargesplits an oversized value of an acceptable type from one of the wrong type.ErrFilterOperatorInvalidwas declared but never returned by production code; it is now real. AFilterErrorcarrier holds the offending field and operator alongside the sentinel and wraps it, so every existingerrors.Ischeck keeps working.server/api/pkg/gatewaymaps sentinel to reason in one place, at the seam every list route crosses, so no route can drift from the wording. The echoed identifier is capped at 64 runes and marks a cut, since the field name is client-supplied and only filter values are otherwise length-bounded.Testing
Unit and HTTP-level tests pass:
pkg/api/query, the gateway package, and the fullservermodule. The gateway's rejection table went from 5 cases to 13, each asserting a distinctfieldsmap. The one pre-existing failure,TestInstallScriptRendersAValidShellScript, is unrelated (the script is not mounted in the container).Also exercised end to end against a running dev stack, since the reason strings are a client-facing contract and only a real request proves what a client receives.
Observed, all
400:Controls: a valid filter still returns
200, and?sort_by=nopestill returns400with{"sort_by": "nope"}unchanged.