Skip to content

Commit 509af79

Browse files
committed
Add --no-enum-sort flag to preserve spec-defined enum order
By default, enum variants are sorted alphabetically (existing behaviour). Passing --no-enum-sort preserves the order defined in the OpenAPI spec, which is useful when enum order is meaningful (e.g. status workflows).
1 parent 3b2a786 commit 509af79

8 files changed

Lines changed: 234 additions & 22 deletions

File tree

cli/src/Cli.elm

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ type alias CliOptions =
5151
, writeMergedTo : Maybe String
5252
, noElmFormat : Bool
5353
, keepGoing : Bool
54+
, noEnumSort : Bool
5455
}
5556

5657

@@ -186,6 +187,10 @@ program =
186187
(Cli.Option.flag "keep-going"
187188
|> Cli.Option.withDescription "If a route can't be generated, skip it instead of erroring out."
188189
)
190+
|> Cli.OptionsParser.with
191+
(Cli.Option.flag "no-enum-sort"
192+
|> Cli.Option.withDescription "Don't sort enum variants alphabetically, preserve the order from the spec."
193+
)
189194
)
190195

191196

@@ -388,6 +393,7 @@ parseCliOptions cliOptions =
388393
|> OpenApi.Config.withAutoConvertSwagger cliOptions.autoConvertSwagger
389394
|> OpenApi.Config.withNoElmFormat cliOptions.noElmFormat
390395
|> OpenApi.Config.withKeepGoing cliOptions.keepGoing
396+
|> OpenApi.Config.withNoEnumSort cliOptions.noEnumSort
391397
|> maybe OpenApi.Config.withSwaggerConversionUrl cliOptions.swaggerConversionUrl
392398
|> maybe OpenApi.Config.withSwaggerConversionCommand
393399
(cliOptions.swaggerConversionCommand

docs/USAGE.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@
2525
- `[--effect-types <effect types>]`: A list of which kind of APIs to generate. Each item should be of the form `package.type`. If `package` is omitted it defaults to `elm/http`. If `type` is omitted it defaults to `cmd,task`. The options for package are: `elm/http`, `dillonkearns/elm-pages`, `lamdera/program-test`. The options for type are: `cmd`, `cmdrisky`, `cmdrecord`, `task`, `taskrisky`, `taskrecord`.
2626
- `[--server <server>]`: The base URL for the OpenAPI server. If not specified this will be extracted from the OAS or default to root of the web application. You can pass in an object to define multiple servers, like `{"dev": "http://localhost", "prod": "https://example.com"}`. This will add a `server` parameter to functions and define a `Servers` module with your servers. You can pass in an empty object if you have fully dynamic servers.
2727
- `[--no-elm-format]`: Don't run elm-format on the outputs.
28+
- `[--no-enum-sort]`: Don't sort enum variants alphabetically, preserve the order from the spec.
2829
- `[--keep-going]`: If a route can't be generated, skip it instead of erroring out.
30+
2931
## Example outputs:
3032

3133
Assume we have an OAS file named `my-cool-company-oas.json` and it has a field `"title": "My Coool Company"` and we run the CLI like so

src/CliMonad.elm

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ module CliMonad exposing
44
, succeed, succeedWith, fail, fromResult
55
, map, map2, map3
66
, andThen, andThen2, andThen3, andThen4, combine, combineDict, combineMap, foldl
7-
, errorToWarning, getApiSpec, enumName, moduleToNamespace, getOrCache
7+
, errorToWarning, getApiSpec, getNoEnumSort, enumName, moduleToNamespace, getOrCache
88
, withPath, withWarning, withExtendedWarning, withRequiredPackage
99
, todo, todoWithDefault
1010
, withFormat
@@ -18,7 +18,7 @@ module CliMonad exposing
1818
@docs succeed, succeedWith, fail, fromResult
1919
@docs map, map2, map3
2020
@docs andThen, andThen2, andThen3, andThen4, combine, combineDict, combineMap, foldl
21-
@docs errorToWarning, getApiSpec, enumName, moduleToNamespace, getOrCache
21+
@docs errorToWarning, getApiSpec, getNoEnumSort, enumName, moduleToNamespace, getOrCache
2222
@docs withPath, withWarning, withExtendedWarning, withRequiredPackage
2323
@docs todo, todoWithDefault
2424
@docs withFormat
@@ -88,6 +88,7 @@ type alias Input =
8888
, formats : FastDict.Dict InternalFormatName InternalFormat
8989
, warnOnMissingEnums : Bool
9090
, keepGoing : Bool
91+
, noEnumSort : Bool
9192
}
9293

9394

@@ -130,6 +131,7 @@ run :
130131
, formats : List OpenApi.Config.Format
131132
, warnOnMissingEnums : Bool
132133
, keepGoing : Bool
134+
, noEnumSort : Bool
133135
}
134136
-> CliMonad (List Declaration)
135137
->
@@ -153,6 +155,7 @@ run oneOfDeclarations input (CliMonad x) =
153155
|> FastDict.fromList
154156
, warnOnMissingEnums = input.warnOnMissingEnums
155157
, keepGoing = input.keepGoing
158+
, noEnumSort = input.noEnumSort
156159
}
157160

158161
res : Result Message ( List Declaration, Output, Cache )
@@ -575,6 +578,11 @@ getApiSpec =
575578
CliMonad (\input cache -> Ok ( input.openApi, emptyOutput, cache ))
576579

577580

581+
getNoEnumSort : CliMonad Bool
582+
getNoEnumSort =
583+
CliMonad (\input cache -> Ok ( input.noEnumSort, emptyOutput, cache ))
584+
585+
578586
{-| If the user has chosen to keep going in the face of errors, this will convert an error into a warning. Otherwise this returns the input
579587
-}
580588
errorToWarning : CliMonad a -> CliMonad (Maybe a)
@@ -664,7 +672,14 @@ enumName : List Common.UnsafeName -> CliMonad (Maybe Common.UnsafeName)
664672
enumName variants =
665673
CliMonad
666674
(\input cache ->
667-
case FastDict.get (List.map Common.unwrapUnsafe variants) input.enums of
675+
case
676+
FastDict.get
677+
(variants
678+
|> List.map Common.unwrapUnsafe
679+
|> List.sort
680+
)
681+
input.enums
682+
of
668683
Just { name } ->
669684
Ok ( Just name, emptyOutput, cache )
670685

src/Common.elm

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,6 @@ unwrapUnsafe (UnsafeName name) =
584584
enum : ( String, List String ) -> Type
585585
enum variants =
586586
variants
587-
|> NonEmpty.sort
588587
|> NonEmpty.map UnsafeName
589588
|> Enum
590589

src/OpenApi/Config.elm

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
module OpenApi.Config exposing
22
( Config, EffectType(..), effectTypeToPackage, Format, Input, Path(..), Server(..)
33
, init, inputFrom, pathFromString
4-
, withAutoConvertSwagger, AutoConvertSwagger(..), withEffectTypes, withFormat, withFormats, withGenerateTodos, withInput, withSwaggerConversionCommand, withSwaggerConversionUrl, withNoElmFormat, withKeepGoing
4+
, withAutoConvertSwagger, AutoConvertSwagger(..), withEffectTypes, withFormat, withFormats, withGenerateTodos, withInput, withSwaggerConversionCommand, withSwaggerConversionUrl, withNoElmFormat, withKeepGoing, withNoEnumSort
55
, withOutputModuleName, withOverrides, withServer, withWriteMergedTo, withWarnOnMissingEnums
6-
, autoConvertSwagger, inputs, outputDirectory, swaggerConversionCommand, swaggerConversionUrl, noElmFormat, keepGoing
6+
, autoConvertSwagger, inputs, outputDirectory, swaggerConversionCommand, swaggerConversionUrl, noElmFormat, keepGoing, noEnumSort
77
, oasPath, overrides, writeMergedTo
88
, toGenerationConfig, Generate, pathToString
99
, defaultFormats
@@ -20,13 +20,13 @@ module OpenApi.Config exposing
2020
# Creation
2121
2222
@docs init, inputFrom, pathFromString
23-
@docs withAutoConvertSwagger, AutoConvertSwagger, withEffectTypes, withFormat, withFormats, withGenerateTodos, withInput, withSwaggerConversionCommand, withSwaggerConversionUrl, withNoElmFormat, withKeepGoing
23+
@docs withAutoConvertSwagger, AutoConvertSwagger, withEffectTypes, withFormat, withFormats, withGenerateTodos, withInput, withSwaggerConversionCommand, withSwaggerConversionUrl, withNoElmFormat, withKeepGoing, withNoEnumSort
2424
@docs withOutputModuleName, withOverrides, withServer, withWriteMergedTo, withWarnOnMissingEnums
2525
2626
2727
# Config properties
2828
29-
@docs autoConvertSwagger, inputs, outputDirectory, swaggerConversionCommand, swaggerConversionUrl, noElmFormat, keepGoing
29+
@docs autoConvertSwagger, inputs, outputDirectory, swaggerConversionCommand, swaggerConversionUrl, noElmFormat, keepGoing, noEnumSort
3030
3131
3232
# Input properties
@@ -84,6 +84,7 @@ type Config
8484
, dynamicFormats : List { format : String, basicType : Common.BasicType } -> List Format
8585
, noElmFormat : Bool
8686
, keepGoing : Bool
87+
, noEnumSort : Bool
8788
}
8889

8990

@@ -253,6 +254,7 @@ init initialOutputDirectory =
253254
, dynamicFormats = \_ -> []
254255
, noElmFormat = False
255256
, keepGoing = False
257+
, noEnumSort = False
256258
}
257259
|> Config
258260

@@ -575,6 +577,14 @@ withKeepGoing newKeepGoing (Config config) =
575577
Config { config | keepGoing = newKeepGoing }
576578

577579

580+
{-| When `True`, enum variants are kept in the order they appear in the OpenAPI spec
581+
instead of being sorted alphabetically. Defaults to `False`.
582+
-}
583+
withNoEnumSort : Bool -> Config -> Config
584+
withNoEnumSort newNoEnumSort (Config config) =
585+
Config { config | noEnumSort = newNoEnumSort }
586+
587+
578588

579589
-------------
580590
-- Getters --
@@ -623,6 +633,13 @@ keepGoing (Config config) =
623633
config.keepGoing
624634

625635

636+
{-| Whether enum variants should be kept in spec-defined order instead of sorted alphabetically.
637+
-}
638+
noEnumSort : Config -> Bool
639+
noEnumSort (Config config) =
640+
config.noEnumSort
641+
642+
626643
{-| -}
627644
oasPath : Input -> Path
628645
oasPath (Input input) =
@@ -656,6 +673,7 @@ type alias Generate =
656673
, formats : List Format
657674
, warnOnMissingEnums : Bool
658675
, keepGoing : Bool
676+
, noEnumSort : Bool
659677
}
660678

661679

@@ -690,6 +708,7 @@ toGenerationConfig formatsInput (Config config) augmentedInputs =
690708
, warnOnMissingEnums = input.warnOnMissingEnums
691709
, formats = config.staticFormats ++ config.dynamicFormats formatsInput
692710
, keepGoing = config.keepGoing
711+
, noEnumSort = config.noEnumSort
693712
}
694713
, spec
695714
)

src/OpenApi/Generate.elm

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ files :
126126
, warnings : List Message
127127
, requiredPackages : FastSet.Set String
128128
}
129-
files { namespace, generateTodos, effectTypes, server, formats, warnOnMissingEnums, keepGoing } apiSpec =
129+
files { namespace, generateTodos, effectTypes, server, formats, warnOnMissingEnums, keepGoing, noEnumSort } apiSpec =
130130
case extractEnums apiSpec of
131131
Err e ->
132132
Err e
@@ -156,6 +156,7 @@ files { namespace, generateTodos, effectTypes, server, formats, warnOnMissingEnu
156156
, formats = formats
157157
, warnOnMissingEnums = warnOnMissingEnums
158158
, keepGoing = keepGoing
159+
, noEnumSort = noEnumSort
159160
}
160161
|> Result.map
161162
(\{ declarations, warnings, requiredPackages } ->

src/SchemaUtils.elm

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,23 @@ schemaToType seen schema =
185185
}
186186
)
187187

188+
enumType : NonEmpty String -> CliMonad { type_ : Common.Type, documentation : Maybe String }
189+
enumType decodedEnums =
190+
CliMonad.getNoEnumSort
191+
|> CliMonad.map
192+
(\noSort ->
193+
{ type_ =
194+
Common.enum
195+
(if noSort then
196+
decodedEnums
197+
198+
else
199+
NonEmpty.sort decodedEnums
200+
)
201+
, documentation = subSchema.description
202+
}
203+
)
204+
188205
singleTypeToType : Json.Schema.Definitions.SingleType -> CliMonad { type_ : Common.Type, documentation : Maybe String }
189206
singleTypeToType singleType =
190207
let
@@ -380,10 +397,7 @@ schemaToType seen schema =
380397
singleTypeToType Json.Schema.Definitions.StringType
381398

382399
Ok (Just { decodedEnums, hasNull }) ->
383-
CliMonad.succeed
384-
{ type_ = Common.enum decodedEnums
385-
, documentation = subSchema.description
386-
}
400+
enumType decodedEnums
387401
|> (if hasNull then
388402
nullable
389403

@@ -466,10 +480,7 @@ schemaToType seen schema =
466480
CliMonad.succeed { type_ = Common.Value, documentation = subSchema.description }
467481

468482
Ok (Just { decodedEnums, hasNull }) ->
469-
CliMonad.succeed
470-
{ type_ = Common.enum decodedEnums
471-
, documentation = subSchema.description
472-
}
483+
enumType decodedEnums
473484
|> (if hasNull then
474485
nullable
475486

@@ -486,10 +497,7 @@ schemaToType seen schema =
486497
nullable (singleTypeToType Json.Schema.Definitions.StringType)
487498

488499
Ok (Just { decodedEnums }) ->
489-
CliMonad.succeed
490-
{ type_ = Common.enum decodedEnums
491-
, documentation = subSchema.description
492-
}
500+
enumType decodedEnums
493501
|> nullable
494502

495503
Err e ->

0 commit comments

Comments
 (0)