Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple servers #102

Merged
merged 4 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 31 additions & 3 deletions src/Cli.elm
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ type alias CliOptions =
, swaggerConversionUrl : String
, swaggerConversionCommand : Maybe String
, swaggerConversionCommandArgs : List String
, server : Maybe String
, server : OpenApi.Generate.Server
}


Expand Down Expand Up @@ -71,7 +71,9 @@ program =
|> Cli.OptionsParser.with
(Cli.Option.keywordArgList "swagger-conversion-command-args")
|> Cli.OptionsParser.with
(Cli.Option.optionalKeywordArg "server")
(Cli.Option.optionalKeywordArg "server"
|> Cli.Option.validateMap serverValidation
)
|> Cli.OptionsParser.withDoc """
options:
Expand All @@ -91,6 +93,13 @@ program =
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.
--auto-convert-swagger If passed in, and a Swagger doc is encountered,
will attempt to convert it to an Open API file.
If not passed in, and a Swagger doc is encountered,
Expand Down Expand Up @@ -148,6 +157,25 @@ effectTypeValidation effectType =
Err <| "Unexpected effect type: " ++ effectType


serverValidation : Maybe String -> Result String OpenApi.Generate.Server
serverValidation server =
case Maybe.withDefault "" server of
"" ->
Ok OpenApi.Generate.Default

input ->
case Json.Decode.decodeString (Json.Decode.dict Json.Decode.string) input of
Ok servers ->
Ok <| OpenApi.Generate.Multiple servers

Err _ ->
if String.startsWith "{" input then
Err <| "Invalid JSON: " ++ input

else
Ok <| OpenApi.Generate.Single input


run : Pages.Script.Script
run =
Pages.Script.withCliOptions program
Expand Down Expand Up @@ -354,7 +382,7 @@ generateFileFromOpenApiSpec :
{ outputModuleName : Maybe String
, generateTodos : Maybe String
, effectTypes : List OpenApi.Generate.EffectType
, server : Maybe String
, server : OpenApi.Generate.Server
}
-> OpenApi.OpenApi
-> BackendTask.BackendTask FatalError.FatalError ( List Elm.File, List CliMonad.Message )
Expand Down
4 changes: 4 additions & 0 deletions src/Common.elm
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type Module
| Types
| Api
| Common
| Servers


moduleToNamespace : List String -> Module -> List String
Expand All @@ -36,6 +37,9 @@ moduleToNamespace namespace module_ =
Api ->
namespace ++ [ "Api" ]

Servers ->
namespace ++ [ "Servers" ]

Common ->
[ "OpenApi", "Common" ]

Expand Down
2 changes: 1 addition & 1 deletion src/JsonSchema/Generate.elm
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ schemaToDeclarations namespace name schema =
, Elm.alias typeName ann
|> Elm.exposeWith
{ exposeConstructor = False
, group = Nothing
, group = Just "Aliases"
}
)
|> CliMonad.succeed
Expand Down
173 changes: 118 additions & 55 deletions src/OpenApi/Generate.elm
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module OpenApi.Generate exposing
( ContentSchema(..)
, EffectType(..)
, Mime
, Server(..)
, files
, sanitizeModuleName
)
Expand Down Expand Up @@ -72,6 +73,12 @@ type ContentSchema
| BytesContent Mime


type Server
= Default
| Single String
| Multiple (Dict.Dict String String)


type alias AuthorizationInfo =
{ headers : Elm.Expression -> List ( Elm.Expression, Elm.Expression )
, params : List ( String, Elm.Annotation.Annotation )
Expand All @@ -83,7 +90,7 @@ files :
{ namespace : List String
, generateTodos : Bool
, effectTypes : List EffectType
, server : Maybe String
, server : Server
}
-> OpenApi.OpenApi
-> Result CliMonad.Message ( List Elm.File, List CliMonad.Message )
Expand Down Expand Up @@ -140,17 +147,36 @@ files { namespace, generateTodos, effectTypes, server } apiSpec =
, customHttpError
|> Elm.exposeWith
{ exposeConstructor = True
, group = Nothing
, group = Just "Http"
}
)
, ( Common.Common
, nullableType
|> Elm.exposeWith
{ exposeConstructor = True
, group = Nothing
, group = Just "Types"
}
)
]
++ (case server of
Multiple servers ->
servers
|> Dict.toList
|> List.map
(\( key, value ) ->
( Common.Servers
, Elm.string value
|> Elm.declaration key
|> Elm.exposeWith
{ exposeConstructor = True
, group = Just "Servers"
}
)
)

_ ->
[]
)
in
( allDecls
|> List.Extra.gatherEqualsBy Tuple.first
Expand Down Expand Up @@ -223,7 +249,7 @@ formatModuleDocs =
)


pathDeclarations : Maybe String -> List EffectType -> List String -> CliMonad (List ( Common.Module, Elm.Declaration ))
pathDeclarations : Server -> List EffectType -> List String -> CliMonad (List ( Common.Module, Elm.Declaration ))
pathDeclarations server effectTypes namespace =
CliMonad.fromApiSpec OpenApi.paths
|> CliMonad.andThen
Expand Down Expand Up @@ -318,7 +344,7 @@ unitDeclarations namespace name =
, Elm.alias typeName Elm.Annotation.unit
|> Elm.exposeWith
{ exposeConstructor = False
, group = Nothing
, group = Just "Aliases"
}
)
|> CliMonad.succeed
Expand Down Expand Up @@ -399,7 +425,7 @@ requestBodyToDeclarations namespace name reference =
|> CliMonad.withPath name


toRequestFunctions : Maybe String -> List EffectType -> List String -> String -> String -> OpenApi.Operation.Operation -> CliMonad (List ( Common.Module, Elm.Declaration ))
toRequestFunctions : Server -> List EffectType -> List String -> String -> String -> OpenApi.Operation.Operation -> CliMonad (List ( Common.Module, Elm.Declaration ))
toRequestFunctions server effectTypes namespace method pathUrl operation =
let
functionName : String
Expand Down Expand Up @@ -612,26 +638,36 @@ toRequestFunctions server effectTypes namespace method pathUrl operation =
documentation : AuthorizationInfo -> String
documentation { scopes } =
let
descriptionDoc : String
summaryDoc : Maybe String
summaryDoc =
OpenApi.Operation.summary operation

descriptionDoc : Maybe String
descriptionDoc =
OpenApi.Operation.description operation
|> Maybe.withDefault ""
in
if List.isEmpty scopes then
descriptionDoc

else
([ descriptionDoc
, ""
, "This operations requires the following scopes:"
]
++ List.map
(\scope ->
" - `" ++ scope ++ "`"
scopesDoc : Maybe String
scopesDoc =
if List.isEmpty scopes then
Nothing

else
("This operations requires the following scopes:"
:: List.map
(\scope ->
" - `" ++ scope ++ "`"
)
scopes
)
scopes
)
|> String.join "\n"
|> String.join "\n"
|> Just
in
[ summaryDoc
, descriptionDoc
, scopesDoc
]
|> List.filterMap identity
|> String.join "\n\n"
in
CliMonad.andThen4
(\contentSchema auth successAnnotation replaced ->
Expand All @@ -647,7 +683,10 @@ toRequestFunctions server effectTypes namespace method pathUrl operation =
|> Elm.withDocumentation (documentation auth)
|> Elm.exposeWith
{ exposeConstructor = False
, group = Nothing
, group =
operation
|> operationToGroup
|> Just
}
)
)
Expand All @@ -666,6 +705,7 @@ toRequestFunctions server effectTypes namespace method pathUrl operation =
, errorTypeAnnotation = errorTypeAnnotation
, authorizationInfo = auth
, bodyParams = params
, server = server
}
)
)
Expand All @@ -679,7 +719,17 @@ toRequestFunctions server effectTypes namespace method pathUrl operation =
|> CliMonad.withPath pathUrl


replacedUrl : Maybe String -> List String -> String -> OpenApi.Operation.Operation -> CliMonad (Elm.Expression -> Elm.Expression)
operationToGroup : OpenApi.Operation.Operation -> String
operationToGroup operation =
case OpenApi.Operation.tags operation of
[ tag ] ->
tag

_ ->
"Operations"


replacedUrl : Server -> List String -> String -> OpenApi.Operation.Operation -> CliMonad (Elm.Expression -> Elm.Expression)
replacedUrl server namespace pathUrl operation =
let
params : List (OpenApi.Reference.ReferenceOr OpenApi.Parameter.Parameter)
Expand Down Expand Up @@ -750,35 +800,40 @@ replacedUrl server namespace pathUrl operation =
OpenApi.servers
|> CliMonad.fromApiSpec
|> CliMonad.map
(\servers ->
\config ->
let
initialUrl : String
initialUrl =
case server of
Just cliServer ->
if String.startsWith "/" pathUrl then
cliServer ++ pathUrl

else
cliServer ++ "/" ++ pathUrl

Nothing ->
case servers of
[] ->
pathUrl

firstServer :: _ ->
if String.startsWith "/" pathUrl then
OpenApi.Server.url firstServer ++ pathUrl
(\servers config ->
let
initialUrl : Elm.Expression
initialUrl =
let
appendPath : String -> Elm.Expression
appendPath resolvedServer =
if String.startsWith "/" pathUrl then
Elm.string <| resolvedServer ++ pathUrl

else
OpenApi.Server.url firstServer ++ "/" ++ pathUrl
in
List.foldl
(\( replacement, _ ) -> replacement config)
(Elm.string initialUrl)
replacements
else
Elm.string <| resolvedServer ++ "/" ++ pathUrl
in
case server of
Single cliServer ->
cliServer |> appendPath

Default ->
case servers of
[] ->
"" |> appendPath

firstServer :: _ ->
OpenApi.Server.url firstServer |> appendPath

Multiple _ ->
Elm.Op.append
(Elm.get "server" config)
(appendPath "")
in
List.foldl
(\( replacement, _ ) -> replacement config)
initialUrl
replacements
)

else
Expand Down Expand Up @@ -1121,12 +1176,20 @@ toConfigParamAnnotation :
, errorTypeAnnotation : Elm.Annotation.Annotation
, authorizationInfo : AuthorizationInfo
, bodyParams : List ( String, Elm.Annotation.Annotation )
, server : Server
}
-> CliMonad ({ requireToMsg : Bool } -> Elm.Annotation.Annotation)
toConfigParamAnnotation namespace options =
CliMonad.map
(\urlParams { requireToMsg } ->
(options.authorizationInfo.params
((case options.server of
Multiple _ ->
[ ( "server", Elm.Annotation.string ) ]

_ ->
[]
)
++ options.authorizationInfo.params
++ (if requireToMsg then
[ ( "toMsg"
, Elm.Annotation.function
Expand Down Expand Up @@ -1619,7 +1682,7 @@ operationToTypesExpectAndResolver namespace functionName operation =
Elm.alias errorName Elm.Annotation.unit
|> Elm.exposeWith
{ exposeConstructor = True
, group = Nothing
, group = Just "Errors"
}

else
Expand All @@ -1629,7 +1692,7 @@ operationToTypesExpectAndResolver namespace functionName operation =
|> Elm.customType errorName
|> Elm.exposeWith
{ exposeConstructor = True
, group = Nothing
, group = Just "Errors"
}
, Elm.Annotation.named (Common.moduleToNamespace namespace Common.Types) errorName
)
Expand Down
Loading