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

[Elixir] Improve Elixir client #6550

Merged
merged 5 commits into from
Sep 28, 2017
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

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
# {{#modulized}}{{appName}}{{/modulized}}
# {{moduleName}}

**TODO: Add description**
{{appDescription}}

## Installation

If [available in Hex](https://hex.pm/docs/publish), the package can be installed
by adding `{{#underscored}}{{appName}}{{/underscored}}` to your list of dependencies in `mix.exs`:
by adding `{{#underscored}}{{packageName}}{{/underscored}}` to your list of dependencies in `mix.exs`:

```elixir
def deps do
[{:{{#underscored}}{{appName}}{{/underscored}}, "~> 0.1.0"}]
[{:{{#underscored}}{{packageName}}{{/underscored}}, "~> 0.1.0"}]
end
```

Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
be found at [https://hexdocs.pm/{{#underscored}}{{appName}}{{/underscored}}](https://hexdocs.pm/{{#underscored}}{{appName}}{{/underscored}}).
be found at [https://hexdocs.pm/{{#underscored}}{{packageName}}{{/underscored}}](https://hexdocs.pm/{{#underscored}}{{packageName}}{{/underscored}}).
68 changes: 46 additions & 22 deletions modules/swagger-codegen/src/main/resources/elixir/api.mustache
Original file line number Diff line number Diff line change
@@ -1,35 +1,59 @@
defmodule {{#modulized}}{{appName}}{{/modulized}}.Api.{{classname}} do
{{>licenseInfo}}
defmodule {{moduleName}}.Api.{{classname}} do
@moduledoc """
Documentation for {{#modulized}}{{appName}}{{/modulized}}.Api.{{classname}}.
API calls for all endpoints tagged `{{baseName}}`.
"""

use Tesla
alias {{moduleName}}.Connection
import {{moduleName}}.RequestBuilder

plug Tesla.Middleware.BaseUrl, "{{{basePath}}}"
plug Tesla.Middleware.JSON
{{#operations}}
{{#operation}}
{{#operation}}

@doc """
{{#summary}}
{{summary}}
{{^notes.isEmpty}}

{{/summary}}
{{#notes}}
{{notes}}
{{/notes.isEmpty}}
"""
def {{#underscored}}{{operationId}}{{/underscored}}({{#allParams}}{{^-first}}, {{/-first}}{{#underscored}}{{paramName}}{{/underscored}}{{/allParams}}) do
method = [method: :{{#underscored}}{{httpMethod}}{{/underscored}}]
url = [url: "{{replacedPathName}}"]
query_params = [{{^queryParams.isEmpty}}query: [{{#queryParams}}{{^-first}}, {{/-first}}{:"{{baseName}}", {{#underscored}}{{paramName}}{{/underscored}}}{{/queryParams}}]{{/queryParams.isEmpty}}]
header_params = [{{^headerParams.isEmpty}}header: [{{#headerParams}}{{^-first}}, {{/-first}}{:"{{baseName}}", {{#underscored}}{{paramName}}{{/underscored}}}{{/headerParams}}]{{/headerParams.isEmpty}}]
body_params = [{{^bodyParams.isEmpty}}body: {{#bodyParams}}{{#underscored}}{{paramName}}{{/underscored}}{{/bodyParams}}{{/bodyParams.isEmpty}}]
form_params = [{{^formParams.isEmpty}}body: Enum.map_join([{{#formParams}}{{^-first}}, {{/-first}}{:"{{baseName}}", {{#underscored}}{{paramName}}{{/underscored}}}{{/formParams}}], "&", &("#{elem(&1, 0)}=#{elem(&1, 1)}")){{/formParams.isEmpty}}]
params = query_params ++ header_params ++ body_params ++ form_params
opts = []
options = method ++ url ++ params ++ opts
{{/notes}}

## Parameters

request(options)
- connection ({{moduleName}}.Connection): Connection to server
{{#allParams}}{{#required}} - {{#underscored}}{{paramName}}{{/underscored}} ({{dataType}}): {{description}}
{{/required}}{{/allParams}} - opts (KeywordList): [optional] Optional parameters
{{#allParams}}{{^required}} - {{#underscored}}:{{paramName}}{{/underscored}} ({{dataType}}): {{description}}
{{/required}}{{/allParams}}
## Returns

{:ok, {{#isListContainer}}[%{{returnBaseType}}{}, ...]{{/isListContainer}}{{#isMapContainer}}%{}{{/isMapContainer}}{{^returnType}}%{}{{/returnType}}{{#returnSimpleType}}%{{#returnType}}{{#isMapContainer}}{{/isMapContainer}}{{moduleName}}.Model.{{{returnType}}}{{/returnType}}{}{{/returnSimpleType}}} on success
{:error, info} on failure
"""
{{typespec}}
def {{#underscored}}{{operationId}}{{/underscored}}(connection, {{#allParams}}{{#required}}{{#underscored}}{{paramName}}{{/underscored}}, {{/required}}{{/allParams}}{{^hasOptionalParams}}_{{/hasOptionalParams}}opts \\ []) do
{{#hasOptionalParams}}
optional_params = %{
{{#allParams}}{{^required}}{{^isPathParam}}:"{{baseName}}" => {{#isBodyParam}}:body{{/isBodyParam}}{{#isFormParam}}:form{{/isFormParam}}{{#isQueryParam}}:query{{/isQueryParam}}{{#isHeaderParam}}:headers{{/isHeaderParam}}{{/isPathParam}}{{#hasMore}},
{{/hasMore}}{{/required}}{{/allParams}}
}
{{/hasOptionalParams}}
%{}
|> method(:{{#underscored}}{{httpMethod}}{{/underscored}})
|> url("{{replacedPathName}}")
{{#allParams}}
{{#required}}
{{^isPathParam}} |> add_param({{#isBodyParam}}:body{{/isBodyParam}}{{#isFormParam}}{{#isMultipart}}{{#isFile}}:file{{/isFile}}{{^isFile}}:form{{/isFile}}{{/isMultipart}}{{^isMultipart}}:form{{/isMultipart}}{{/isFormParam}}{{#isQueryParam}}:query{{/isQueryParam}}{{#isHeaderParam}}:headers{{/isHeaderParam}}, :"{{baseName}}", {{#underscored}}{{paramName}}{{/underscored}})
{{/isPathParam}}
{{/required}}
{{/allParams}}
{{#hasOptionalParams}}
|> add_optional_params(optional_params, opts)
{{/hasOptionalParams}}
|> Enum.into([])
|> (&Connection.request(connection, &1)).()
|> decode({{decodedStruct}})
end
{{/operation}}
{{/operation}}
{{/operations}}
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
{{>licenseInfo}}
defmodule {{moduleName}}.Connection do
@moduledoc """
Handle Tesla connections for {{moduleName}}.
"""

use Tesla

# Add any middleware here (authentication)
plug Tesla.Middleware.BaseUrl, "{{{basePath}}}"
plug Tesla.Middleware.Headers, %{"User-Agent" => "Elixir"}
plug Tesla.Middleware.EncodeJson

{{#hasAuthMethods}}
{{#authMethods}}
{{#isOAuth}}
@scopes [
{{#scopes}}
"{{scope}}"{{#hasMore}},{{/hasMore}} {{#description}}# {{description}}{{/description}}
{{/scopes}}
]

@doc """
Configure a client connection using a provided OAuth2 token as a Bearer token

## Parameters

- token (String): Bearer token

## Returns

Tesla.Env.client
"""
@spec new(String.t) :: Tesla.Env.client
def new(token) when is_binary(token) do
Tesla.build_client([
{Tesla.Middleware.Headers, %{"Authorization" => "Bearer #{token}"}}
])
end

@doc """
Configure a client connection using a function which yields a Bearer token.

## Parameters

- token_fetcher (function arity of 1): Callback which provides an OAuth2 token
given a list of scopes

## Returns

Tesla.Env.client
"""
@spec new(((list(String.t)) -> String.t)) :: Tesla.Env.client
def new(token_fetcher) when is_function(token_fetcher) do
token_fetcher.(@scopes)
|> new
end
{{/isOAuth}}
{{#isBasic}}
@doc """
Configure an client connection using Basic authentication.

## Parameters

- username (String): Username used for authentication
- password (String): Password used for authentication

# Returns

Tesla.Env.client
"""
@spec new(String.t, String.t) :: Tesla.Env.client
def new(username, password) do
Tesla.build_client([
{Tesla.Middleware.BasicAuth, %{username: username, password: password}}
])
end
{{/isBasic}}
{{/authMethods}}
{{/hasAuthMethods}}
@doc """
Configure an authless client connection

# Returns

Tesla.Env.client
"""
@spec new() :: Tesla.Env.client
def new do
Tesla.build_client([])
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{{>licenseInfo}}
defmodule {{moduleName}}.Deserializer do
@moduledoc """
Helper functions for deserializing responses into models
"""

@doc """
Update the provided model with a deserialization of a nested value
"""
@spec deserialize(struct(), :atom, :atom, struct(), keyword()) :: struct()
def deserialize(model, field, :list, mod, options) do
model
|> Map.update!(field, &(Poison.Decode.decode(&1, Keyword.merge(options, [as: [struct(mod)]]))))
end
def deserialize(model, field, :struct, mod, options) do
model
|> Map.update!(field, &(Poison.Decode.decode(&1, Keyword.merge(options, [as: struct(mod)]))))
end
def deserialize(model, field, :map, mod, options) do
model
|> Map.update!(field, &(Map.new(&1, fn {key, val} -> {key, Poison.Decode.decode(val, Keyword.merge(options, [as: struct(mod)]))} end)))
end
def deserialize(model, field, :date, _, _options) do
case DateTime.from_iso8601(Map.get(model, field)) do
{:ok, datetime} ->
Map.put(model, field, datetime)
_ ->
model
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# The directory Mix will write compiled artifacts to.
/_build

# If you run "mix test --cover", coverage assets end up here.
/cover

# The directory Mix downloads your dependencies sources to.
/deps

# Where 3rd-party dependencies like ExDoc output generated docs.
/doc

# Ignore .fetch files in case you like to edit your project deps locally.
/.fetch

# If the VM crashes, it generates a dump, let's ignore it too.
erl_crash.dump

# Also ignore archive artifacts (built via "mix archive.build").
*.ez
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{{#licenseHeader}}{{licenseHeader}}

{{/licenseHeader}}
# NOTE: This class is auto generated by the swagger code generator program.
# https://github.com/swagger-api/swagger-codegen.git
# Do not edit the class manually.
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
defmodule {{#modulized}}{{appName}}{{/modulized}}.Mixfile do
defmodule {{moduleName}}.Mixfile do
use Mix.Project

def project do
[app: :{{#underscored}}{{appName}}{{/underscored}},
[app: :{{#underscored}}{{packageName}}{{/underscored}},
version: "0.1.0",
elixir: "~> {{supportedElixirVersion}}",
build_embedded: Mix.env == :prod,
Expand Down
32 changes: 32 additions & 0 deletions modules/swagger-codegen/src/main/resources/elixir/model.mustache
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{{>licenseInfo}}
{{#models}}{{#model}}defmodule {{moduleName}}.Model.{{classname}} do
@moduledoc """
{{description}}
"""

@derive [Poison.Encoder]
defstruct [
{{#vars}}:"{{baseName}}"{{#hasMore}},
{{/hasMore}}{{/vars}}
]
end

defimpl Poison.Decoder, for: {{moduleName}}.Model.{{classname}} do
{{#hasComplexVars}}
import {{moduleName}}.Deserializer
def decode(value, options) do
value
{{#vars}}
{{^isPrimitiveType}}
{{#datatype}}|> deserialize(:"{{baseName}}", {{#isListContainer}}:list, {{moduleName}}.Model.{{items.datatype}}{{/isListContainer}}{{#isMapContainer}}:map, {{moduleName}}.Model.{{items.datatype}}{{/isMapContainer}}{{#isDate}}:date, nil{{/isDate}}{{#isDateTime}}:date, nil{{/isDateTime}}{{^isDate}}{{^isDateTime}}{{^isMapContainer}}{{^isListContainer}}:struct, {{moduleName}}.Model.{{datatype}}{{/isListContainer}}{{/isMapContainer}}{{/isDateTime}}{{/isDate}}, options)
{{/datatype}}
{{/isPrimitiveType}}
{{/vars}}
{{/hasComplexVars}}
{{^hasComplexVars}}
def decode(value, _options) do
value
{{/hasComplexVars}}
end
end
{{/model}}{{/models}}
Loading