Add toEncoding to preserve field order - #1
Merged
Merged
Conversation
encode/toEncoding of an OpenApi document re-sorted object keys alphabetically. Schema and the eight Referenced instances had no toEncoding and fell back to the default `toEncoding = toEncoding . toJSON`, which routes through Value and cannot represent key order. The fallback is transitive, so the whole sub-tree below them was alphabetized as well. Schema.toEncoding existed in swagger2 but was dropped in 11f8f97 ("Merge ParamSchema into Schema"), where the merged Schema started needing inline aeson options and no encoding counterpart of sopSwaggerGenericToJSONWithOpts existed. Data/OpenApi/Internal/AesonUtils.hs: * Add sopSwaggerGenericToEncodingWithOpts, the encoding counterpart of sopSwaggerGenericToJSONWithOpts. * Add sopSwaggerGenericToSeries, the body of sopSwaggerGenericToEncoding without the final `pairs`, so an instance can splice a record's fields into an enclosing object. * Splice sub-objects (saoSubObject) on the encoding path rather than through toJSON/Value. The caller supplies the sub-object's members as a Series via sopSwaggerGenericToEncodingWithSub, because the generic traversal only has ToJSON for them and an Encoding is an opaque Builder that cannot be taken apart. A declared sub-object with no Series is now a loud error instead of silently dropped fields. * Handle Newtype in the three generic traversals. They matched only ADT, so a newtype record hit the "unsupported type" error. * Replace the dead saoAdditionalPairs option with saoRequiredFields, which forces a field to be emitted even when it equals its default. saoAdditionalPairs existed for swagger2's constant "swagger": "2.0" field, which is a real record field in openapi3, and nothing had set it since. Drops pairsToSeries and parseAdditionalField with it. Data/OpenApi/Internal.hs: * Restore Schema.toEncoding and the eight Referenced instances, the latter via a new referencedToEncoding. * Add toEncoding to every remaining ToJSON in the model so nothing falls back to the Value path: the hand-written instances (SecuritySchemeType, OpenApiItems, SecurityDefinitions, AdditionalProperties, ExpressionOrValue, Callback, Reference, MimeList, OpenApiSpecVersion) and the jsonPrefix-derived records and enums. * Move ApiKeyParams and the four OAuth2*Flow records to the generics-sop family. Their fields are spliced into an enclosing object, which needs a Series, and deriving it keeps the field names in the records instead of in string literals. * Emit the required-but-empty OpenApi "paths" and OAuth2Flow "scopes" objects via saoRequiredFields, which removes the special case from both toJSON and toEncoding. * Add the Series helpers the sub-object splices need: openApiItemsSeries, securitySchemeTypeSeries and responsesSeries, reused by the corresponding toEncoding instances so each has a single definition. * Drop the stale `saoSubObject ?~ "paramSchema"` from Schema's HasSwaggerAesonOptions. No field has produced that name since the ParamSchema merge. It cannot be corrected to "items" either: FromJSON Schema shares these options and would then try to parse the whole schema object as OpenApiItems. test/SpecCommon.hs: * (<=>) now also asserts `decode (toEncoding x) == Right (toJSON x)`, so every curated example checks that the two encoders agree. Two behavior changes beyond key order: * An empty "paths" is emitted in its declared position rather than appended last. * parseJSON failure messages for ApiKeyParams and the four OAuth2*Flow records now read "Swagger Record Object", like the other generics-sop types, instead of naming the constructor. Successful parses and all serialized output are unchanged.
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.
encode/toEncoding of an OpenApi document re-sorted object keys alphabetically. Schema and the eight Referenced instances had no toEncoding and fell back to the default
toEncoding = toEncoding . toJSON, which routes through Value and cannot represent key order. The fallback is transitive, so the whole sub-tree below them was alphabetized as well.Schema.toEncoding existed in swagger2 but was dropped in 11f8f97 ("Merge ParamSchema into Schema"), where the merged Schema started needing inline aeson options and no encoding counterpart of sopSwaggerGenericToJSONWithOpts existed.
Data/OpenApi/Internal/AesonUtils.hs:
pairs, so an instance can splice a record's fields into an enclosing object.Data/OpenApi/Internal.hs:
saoSubObject ?~ "paramSchema"from Schema's HasSwaggerAesonOptions. No field has produced that name since the ParamSchema merge. It cannot be corrected to "items" either: FromJSON Schema shares these options and would then try to parse the whole schema object as OpenApiItems.test/SpecCommon.hs:
decode (toEncoding x) == Right (toJSON x), so every curated example checks that the two encoders agree.Two behavior changes beyond key order: