diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ElmClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ElmClientCodegen.java index 11652306daab..705a6346f81e 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ElmClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ElmClientCodegen.java @@ -64,7 +64,7 @@ public class ElmClientCodegen extends DefaultCodegen implements CodegenConfig { featureSet = getFeatureSet().modify() .includeDocumentationFeatures(DocumentationFeature.Readme) .wireFormatFeatures(EnumSet.of(WireFormatFeature.JSON)) - .securityFeatures(EnumSet.noneOf(SecurityFeature.class)) + .securityFeatures(EnumSet.of(SecurityFeature.BearerToken)) .excludeGlobalFeatures( GlobalFeature.XMLStructureDefinitions, GlobalFeature.Callbacks, diff --git a/modules/openapi-generator/src/main/resources/elm/Api.mustache b/modules/openapi-generator/src/main/resources/elm/Api.mustache index 987428ec0724..2890e9deac97 100644 --- a/modules/openapi-generator/src/main/resources/elm/Api.mustache +++ b/modules/openapi-generator/src/main/resources/elm/Api.mustache @@ -2,10 +2,13 @@ module Api exposing ( Request , request , send + , sendWithCustomError + , task , map , withBasePath , withTimeout , withTracker + , withBearerToken , withHeader , withHeaders ) @@ -13,6 +16,7 @@ module Api exposing import Http import Json.Decode import Json.Encode +import Task import Url.Builder @@ -46,18 +50,35 @@ request method path pathParams queryParams headerParams body decoder = send : (Result Http.Error a -> msg) -> Request a -> Cmd msg -send toMsg (Request req) = +send toMsg req = + sendWithCustomError identity toMsg req + + +sendWithCustomError : (Http.Error -> e) -> (Result e a -> msg) -> Request a -> Cmd msg +sendWithCustomError mapError toMsg (Request req) = Http.request { method = req.method , headers = req.headers , url = Url.Builder.crossOrigin req.basePath req.pathParams req.queryParams , body = req.body - , expect = expectJson toMsg req.decoder + , expect = expectJson mapError toMsg req.decoder , timeout = req.timeout , tracker = req.tracker } +task : Request a -> Task.Task Http.Error a +task (Request req) = + Http.task + { method = req.method + , headers = req.headers + , url = Url.Builder.crossOrigin req.basePath req.pathParams req.queryParams + , body = req.body + , resolver = jsonResolver req.decoder + , timeout = req.timeout + } + + map : (a -> b) -> Request a -> Request b map fn (Request req) = Request @@ -87,6 +108,11 @@ withTracker tracker (Request req) = Request { req | tracker = Just tracker } +withBearerToken : String -> Request a -> Request a +withBearerToken token (Request req) = + Request { req | headers = Http.header "Authorization" ("Bearer " ++ token) :: req.headers } + + withHeader : String -> String -> Request a -> Request a withHeader key value (Request req) = Request { req | headers = req.headers ++ [ Http.header key value ] } @@ -121,36 +147,44 @@ queries = List.filterMap (\(key, value) -> Maybe.map (Url.Builder.string key) value) -expectJson : (Result Http.Error a -> msg) -> Json.Decode.Decoder a -> Http.Expect msg -expectJson toMsg decoder = - Http.expectStringResponse toMsg <| - \response -> - case response of - Http.BadUrl_ url -> - Err (Http.BadUrl url) +expectJson : (Http.Error -> e) -> (Result e a -> msg) -> Json.Decode.Decoder a -> Http.Expect msg +expectJson mapError toMsg decoder = + Http.expectStringResponse toMsg (Result.mapError mapError << decodeResponse decoder) - Http.Timeout_ -> - Err Http.Timeout - Http.NetworkError_ -> - Err Http.NetworkError +jsonResolver : Json.Decode.Decoder a -> Http.Resolver Http.Error a +jsonResolver decoder = + Http.stringResolver (decodeResponse decoder) - Http.BadStatus_ metadata _ -> - Err (Http.BadStatus metadata.statusCode) - Http.GoodStatus_ _ body -> - if String.isEmpty body then - -- we might 'expect' no body if the return type is `()` - case Json.Decode.decodeString decoder "{}" of - Ok value -> - Ok value +decodeResponse : Json.Decode.Decoder a -> Http.Response String -> Result Http.Error a +decodeResponse decoder response = + case response of + Http.BadUrl_ url -> + Err (Http.BadUrl url) - Err _ -> - decodeBody decoder body + Http.Timeout_ -> + Err Http.Timeout - else + Http.NetworkError_ -> + Err Http.NetworkError + + Http.BadStatus_ metadata _ -> + Err (Http.BadStatus metadata.statusCode) + + Http.GoodStatus_ _ body -> + if String.isEmpty body then + -- we might 'expect' no body if the return type is `()` + case Json.Decode.decodeString decoder "{}" of + Ok value -> + Ok value + + Err _ -> decodeBody decoder body + else + decodeBody decoder body + decodeBody : Json.Decode.Decoder a -> String -> Result Http.Error a decodeBody decoder body = diff --git a/modules/openapi-generator/src/main/resources/elm/operation.mustache b/modules/openapi-generator/src/main/resources/elm/operation.mustache index a5da2f70f1ee..ed872dddc569 100644 --- a/modules/openapi-generator/src/main/resources/elm/operation.mustache +++ b/modules/openapi-generator/src/main/resources/elm/operation.mustache @@ -33,8 +33,8 @@ import Uuid exposing (Uuid){{/includeUuid}} {-| {{{notes}}} -} {{/notes}} -{{operationId}} : {{#allParams}}{{^required}}Maybe {{/required}}{{#isListContainer}}List {{/isListContainer}}{{#datatypeWithEnum}}{{.}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{#isModel}}Api.Data.{{/isModel}}{{dataType}}{{/datatypeWithEnum}} -> {{/allParams}}Api.Request {{^responses}}(){{/responses}}{{#responses}}{{#-first}}{{^dataType}}(){{/dataType}}{{#isMapContainer}}(Dict.Dict String {{/isMapContainer}}{{#isListContainer}}(List {{/isListContainer}}{{^primitiveType}}{{^isUuid}}Api.Data.{{/isUuid}}{{/primitiveType}}{{#items}}{{#isModel}}Api.Data.{{/isModel}}{{/items}}{{dataType}}{{#isListContainer}}){{/isListContainer}}{{#isMapContainer}}){{/isMapContainer}}{{/-first}}{{/responses}} -{{operationId}}{{#allParams}} {{>paramName}}{{/allParams}} = +{{operationId}} : {{#allParams}}{{^required}}Maybe {{/required}}{{#isListContainer}}List {{/isListContainer}}{{#datatypeWithEnum}}{{.}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{#isModel}}Api.Data.{{/isModel}}{{dataType}}{{/datatypeWithEnum}} -> {{/allParams}}{{#authMethods}}{{#isBasicBearer}}String -> {{/isBasicBearer}}{{/authMethods}}Api.Request {{^responses}}(){{/responses}}{{#responses}}{{#isDefault}}{{^dataType}}(){{/dataType}}{{#isMapContainer}}(Dict.Dict String {{/isMapContainer}}{{#isListContainer}}(List {{/isListContainer}}{{^primitiveType}}{{^isUuid}}Api.Data.{{/isUuid}}{{/primitiveType}}{{#items}}{{#isModel}}Api.Data.{{/isModel}}{{/items}}{{dataType}}{{#isListContainer}}){{/isListContainer}}{{#isMapContainer}}){{/isMapContainer}}{{/isDefault}}{{/responses}} +{{operationId}}{{#allParams}} {{>paramName}}{{/allParams}}{{#authMethods}}{{#isBasicBearer}} auth_token{{/isBasicBearer}}{{/authMethods}} = Api.request "{{httpMethod}}" "{{path}}" @@ -42,5 +42,6 @@ import Uuid exposing (Uuid){{/includeUuid}} [{{#queryParams}} ( "{{baseName}}", {{#required}}Just <| {{/required}}{{^required}}Maybe.map {{/required}}{{>paramToString}} {{>paramName}} ){{#-last}} {{/-last}}{{^-last}},{{/-last}}{{/queryParams}}] [{{#headerParams}} ( "{{baseName}}", {{#required}}Just <| {{/required}}{{^required}}Maybe.map {{/required}}{{>paramToString}} {{>paramName}} ){{#-last}} {{/-last}}{{^-last}},{{/-last}}{{/headerParams}}] {{#bodyParam}}({{#required}}Just ({{/required}}{{^required}}Maybe.map {{/required}}{{#isModel}}Api.Data.{{/isModel}}{{>recordFieldValueEncoder}} {{>paramName}}{{#required}}){{/required}}){{/bodyParam}}{{^bodyParam}}Nothing{{/bodyParam}} - {{^responses}}(Json.Decode.succeed ()){{/responses}}{{#responses}}{{#isDefault}}{{^dataType}}(Json.Decode.succeed ()){{/dataType}}{{#dataType}}{{>recordFieldValueDecoder}}{{/dataType}}{{/isDefault}}{{/responses}} + {{^responses}}(Json.Decode.succeed ()){{/responses}}{{#responses}}{{#isDefault}}{{^dataType}}(Json.Decode.succeed ()){{/dataType}}{{#dataType}}{{>recordFieldValueDecoder}}{{/dataType}}{{/isDefault}}{{/responses}}{{#authMethods}}{{#isBasicBearer}} + |> Api.withBearerToken auth_token{{/isBasicBearer}}{{/authMethods}} {{/operation}}{{/operations}} \ No newline at end of file diff --git a/modules/openapi-generator/src/test/resources/3_0/elm.yaml b/modules/openapi-generator/src/test/resources/3_0/elm.yaml index 22f74690fe17..71023941dcef 100644 --- a/modules/openapi-generator/src/test/resources/3_0/elm.yaml +++ b/modules/openapi-generator/src/test/resources/3_0/elm.yaml @@ -97,6 +97,16 @@ paths: responses: "200": description: Default response + /secured: + post: + summary: Secured endpoint + security: + - BearerAuth: [] + responses: + "200": + description: Authenticated + "401": + description: Unauthenticated /uuid: get: parameters: @@ -278,3 +288,8 @@ components: type: string in_the_middle: type: string + securitySchemes: + BearerAuth: + type: http + scheme: bearer + bearerFormat: JWT diff --git a/samples/openapi3/client/elm/src/Api.elm b/samples/openapi3/client/elm/src/Api.elm index fc8618efa663..842a33ca3aa6 100644 --- a/samples/openapi3/client/elm/src/Api.elm +++ b/samples/openapi3/client/elm/src/Api.elm @@ -2,10 +2,13 @@ module Api exposing ( Request , request , send + , sendWithCustomError + , task , map , withBasePath , withTimeout , withTracker + , withBearerToken , withHeader , withHeaders ) @@ -13,6 +16,7 @@ module Api exposing import Http import Json.Decode import Json.Encode +import Task import Url.Builder @@ -46,18 +50,35 @@ request method path pathParams queryParams headerParams body decoder = send : (Result Http.Error a -> msg) -> Request a -> Cmd msg -send toMsg (Request req) = +send toMsg req = + sendWithCustomError identity toMsg req + + +sendWithCustomError : (Http.Error -> e) -> (Result e a -> msg) -> Request a -> Cmd msg +sendWithCustomError mapError toMsg (Request req) = Http.request { method = req.method , headers = req.headers , url = Url.Builder.crossOrigin req.basePath req.pathParams req.queryParams , body = req.body - , expect = expectJson toMsg req.decoder + , expect = expectJson mapError toMsg req.decoder , timeout = req.timeout , tracker = req.tracker } +task : Request a -> Task.Task Http.Error a +task (Request req) = + Http.task + { method = req.method + , headers = req.headers + , url = Url.Builder.crossOrigin req.basePath req.pathParams req.queryParams + , body = req.body + , resolver = jsonResolver req.decoder + , timeout = req.timeout + } + + map : (a -> b) -> Request a -> Request b map fn (Request req) = Request @@ -87,6 +108,11 @@ withTracker tracker (Request req) = Request { req | tracker = Just tracker } +withBearerToken : String -> Request a -> Request a +withBearerToken token (Request req) = + Request { req | headers = Http.header "Authorization" ("Bearer " ++ token) :: req.headers } + + withHeader : String -> String -> Request a -> Request a withHeader key value (Request req) = Request { req | headers = req.headers ++ [ Http.header key value ] } @@ -121,36 +147,44 @@ queries = List.filterMap (\(key, value) -> Maybe.map (Url.Builder.string key) value) -expectJson : (Result Http.Error a -> msg) -> Json.Decode.Decoder a -> Http.Expect msg -expectJson toMsg decoder = - Http.expectStringResponse toMsg <| - \response -> - case response of - Http.BadUrl_ url -> - Err (Http.BadUrl url) +expectJson : (Http.Error -> e) -> (Result e a -> msg) -> Json.Decode.Decoder a -> Http.Expect msg +expectJson mapError toMsg decoder = + Http.expectStringResponse toMsg (Result.mapError mapError << decodeResponse decoder) - Http.Timeout_ -> - Err Http.Timeout - Http.NetworkError_ -> - Err Http.NetworkError +jsonResolver : Json.Decode.Decoder a -> Http.Resolver Http.Error a +jsonResolver decoder = + Http.stringResolver (decodeResponse decoder) - Http.BadStatus_ metadata _ -> - Err (Http.BadStatus metadata.statusCode) - Http.GoodStatus_ _ body -> - if String.isEmpty body then - -- we might 'expect' no body if the return type is `()` - case Json.Decode.decodeString decoder "{}" of - Ok value -> - Ok value +decodeResponse : Json.Decode.Decoder a -> Http.Response String -> Result Http.Error a +decodeResponse decoder response = + case response of + Http.BadUrl_ url -> + Err (Http.BadUrl url) - Err _ -> - decodeBody decoder body + Http.Timeout_ -> + Err Http.Timeout - else + Http.NetworkError_ -> + Err Http.NetworkError + + Http.BadStatus_ metadata _ -> + Err (Http.BadStatus metadata.statusCode) + + Http.GoodStatus_ _ body -> + if String.isEmpty body then + -- we might 'expect' no body if the return type is `()` + case Json.Decode.decodeString decoder "{}" of + Ok value -> + Ok value + + Err _ -> decodeBody decoder body + else + decodeBody decoder body + decodeBody : Json.Decode.Decoder a -> String -> Result Http.Error a decodeBody decoder body = diff --git a/samples/openapi3/client/elm/src/Api/Request/Default.elm b/samples/openapi3/client/elm/src/Api/Request/Default.elm index 5dcdf717a7ba..23e998284eb5 100644 --- a/samples/openapi3/client/elm/src/Api/Request/Default.elm +++ b/samples/openapi3/client/elm/src/Api/Request/Default.elm @@ -18,6 +18,7 @@ module Api.Request.Default exposing , maybeGet , pathStringIntegerEnumerationGet, Enumeration(..), enumerationVariants , queryGet, Enum(..), enumVariants + , securedPost , uuidGet ) @@ -165,6 +166,20 @@ queryGet string_query int_query enum_query = +securedPost : String -> Api.Request () +securedPost auth_token = + Api.request + "POST" + "/secured" + [] + [] + [] + Nothing + (Json.Decode.succeed ()) + |> Api.withBearerToken auth_token + + + uuidGet : Maybe Uuid -> Api.Request Uuid uuidGet value_query = Api.request