Improve request implementation and return format

This commit is contained in:
avitex 2017-07-30 19:37:35 +10:00
parent 216a9dec8f
commit 8279a50c92
5 changed files with 97 additions and 65 deletions

View File

@ -1,6 +1,6 @@
language: elixir language: elixir
elixir: elixir:
- 1.4.0 - 1.5.0
notifications: notifications:
recipients: recipients:
- theavitex@gmail.com - theavitex@gmail.com

View File

@ -1,5 +1,5 @@
[![Build Status](https://travis-ci.org/avitex/elixir-vultr.svg)](https://travis-ci.org/avitex/elixir-vultr) [![Build Status](https://travis-ci.org/avitex/elixir-vultr.svg)](https://travis-ci.org/avitex/elixir-vultr)
[![Hex.pm](https://img.shields.io/hexpm/v/vultr.svg)](https://hexdocs.pm/vultr) [![Hex.pm](https://img.shields.io/hexpm/v/vultr.svg)](https://hex.pm/packages/vultr)
[![Hex Docs](https://img.shields.io/badge/hex-docs-blue.svg)](https://hexdocs.pm/vultr) [![Hex Docs](https://img.shields.io/badge/hex-docs-blue.svg)](https://hexdocs.pm/vultr)
# Vultr # Vultr
@ -13,7 +13,7 @@ Documentation hosted on [hexdocs](https://hexdocs.pm/vultr).
```elixir ```elixir
def deps do def deps do
[{:vultr, "~> 0.1.0"}] [{:vultr, "~> 0.2.0"}]
end end
``` ```
@ -24,20 +24,12 @@ Documentation hosted on [hexdocs](https://hexdocs.pm/vultr).
Vultr.app_list() Vultr.app_list()
# Example response # Example response
%{ {:ok, %{ "1" => %{"APPID" => "1", "deploy_name" => "LEMP on CentOS 6 x64", ... }, ... }}
body: %{ "1" => %{"APPID" => "1", "deploy_name" => "LEMP on CentOS 6 x64", ... }, ... },
headers: %{ "content-type" => "application/json", ... },
method: :get,
opts: [],
query: [],
status: 200,
url: "https://api.vultr.com/v1/app/list"
}
``` ```
#### Using authenticated methods #### Using authenticated methods
```elixir ```elixir
client = Vultr.client("<APIKEY>") client = Vultr.client("<APIKEY>")
Vultr.server_list(client, %{}) Vultr.server_list(client, [])
``` ```

View File

@ -1,6 +1,10 @@
defmodule Vultr.Request do defmodule Vultr.Request do
@moduledoc false
#@SPECIAL_PARAMS ["SUBID", "DCID", "RECORDID", "VPSPLANID", "APPID", "OSID", "ISOID", "SCRIPTID", "SNAPSHOTID", "SSHKEYID", "BACKUPID", "USERID"] @special_params [
:subid, :dcid, :recordid, :vpsplanid, :appid, :osid,
:isoid, :scriptid, :snapshotid, :sshkeyid, :backupid, :userid,
]
defmacro __using__(_) do defmacro __using__(_) do
quote do quote do
@ -15,55 +19,88 @@ defmodule Vultr.Request do
endpoint_required_access = Keyword.get(endpoint_opts, :required_access, nil) endpoint_required_access = Keyword.get(endpoint_opts, :required_access, nil)
endpoint_has_params = (length(endpoint_params) > 0) endpoint_has_params = (length(endpoint_params) > 0)
request_client_var = Macro.var(:client, nil) common_args = [__CALLER__.module, endpoint_method, endpoint_path]
request_params_var = Macro.var(:params, nil)
function_args = func = &__MODULE__.perform_request/5
func_name =
endpoint_path
|> String.replace("/", "_")
|> String.to_atom
func_doc = gen_doc(
endpoint_method, endpoint_path,
endpoint_description, endpoint_params,
endpoint_required_access, endpoint_requires_api_key
)
func_body =
cond do cond do
endpoint_requires_api_key && endpoint_has_params -> endpoint_requires_api_key && endpoint_has_params ->
[request_client_var, request_params_var] quote do
def unquote(func_name)(client, params \\ []) when is_list(params) do
unquote(func).(unquote_splicing(common_args), client, params)
end
end
endpoint_requires_api_key -> endpoint_requires_api_key ->
[request_client_var] quote do
def unquote(func_name)(client) do
unquote(func).(unquote_splicing(common_args), client, [])
end
end
endpoint_has_params -> endpoint_has_params ->
[request_params_var] quote do
def unquote(func_name)(params \\ []) when is_list(params) do
unquote(func).(unquote_splicing(common_args), nil, params)
end
end
true -> true ->
[] quote do
end def unquote(func_name)() do
unquote(func).(unquote_splicing(common_args), nil, [])
tesla_args = end
if endpoint_requires_api_key do end
[request_client_var]
else
[]
end
tesla_args =
if endpoint_method === :get && endpoint_has_params do
tesla_args ++ [quote do
unquote(endpoint_path) <> "?" <> URI.encode_query(unquote(request_params_var))
end]
else
tesla_args ++ [quote do
unquote(endpoint_path)
end]
end
tesla_args =
if endpoint_method !== :get && endpoint_has_params do
tesla_args ++ [request_params_var]
else
tesla_args
end end
quote do quote do
@doc unquote(documentation(endpoint_method, endpoint_path, endpoint_description, endpoint_params, endpoint_required_access, endpoint_requires_api_key)) @doc unquote(func_doc)
def unquote(function_name(endpoint_path))(unquote_splicing(function_args)) do unquote(func_body)
unquote(endpoint_method)(unquote_splicing(tesla_args))
end
end end
end end
defp function_name(path), do: path |> String.replace("/", "_") |> String.to_atom def perform_request(caller, method, url, client, params) do
opts = [url: url, method: method] ++ prepare_params(method, params)
resp = Tesla.perform_request(caller, client, opts)
case resp.status do
200 -> {:ok, resp.body}
400 -> {:error, :invalid_api_location, resp.body}
403 -> {:error, :invalid_api_key, resp.body}
405 -> {:error, :invalid_http_method, resp.body}
412 -> {:error, :bad_request, resp.body}
500 -> {:error, :server_error, resp.body}
503 -> {:error, :rate_limit, resp.body}
end
end
defp prepare_params(:get, nil), do: [query: []]
defp prepare_params(_, nil), do: [body: %{}]
defp prepare_params(:get, params), do: [query: capitalize_special_params(params)]
defp prepare_params(_, params), do: [body: Enum.into(capitalize_special_params(params), %{})]
defp capitalize_special_params(params) do
Enum.map(params, fn {k, v} ->
is_special_param =
Enum.any?(@special_params, fn special_param ->
special_param == k
end)
if is_special_param do
{String.upcase(k), v}
else
{k, v}
end
end)
end
defp normalize_param({name, opts}) do defp normalize_param({name, opts}) do
type = Keyword.fetch!(opts, :type) type = Keyword.fetch!(opts, :type)
@ -84,7 +121,7 @@ defmodule Vultr.Request do
################################################## ##################################################
# Documentation helpers # Documentation helpers
defp documentation(method, path, desc, params, required_access, api_key) do defp gen_doc(method, path, desc, params, required_access, api_key) do
doc = """ doc = """
#{desc} #{desc}
""" """
@ -130,14 +167,6 @@ defmodule Vultr.Request do
end end
end end
defp doc_api_key(nil), do: "No"
defp doc_api_key(atm), do: atom_to_word(atm)
defp doc_required_access(nil), do: "None"
defp doc_required_access(atm), do: Atom.to_string(atm)
defp atom_to_word(atm), do: atm |> Atom.to_string |> String.capitalize
defp doc_method(method) do defp doc_method(method) do
if !Enum.any?([:get, :post], fn supported -> method == supported end) do if !Enum.any?([:get, :post], fn supported -> method == supported end) do
raise ArgumentError, message: "Bad method" raise ArgumentError, message: "Bad method"
@ -145,4 +174,12 @@ defmodule Vultr.Request do
method |> Atom.to_string |> String.upcase() method |> Atom.to_string |> String.upcase()
end end
end
defp doc_api_key(nil), do: "No"
defp doc_api_key(atm), do: atom_to_word(atm)
defp doc_required_access(nil), do: "None"
defp doc_required_access(atm), do: Atom.to_string(atm)
defp atom_to_word(atm), do: atm |> Atom.to_string |> String.capitalize
end

View File

@ -8,6 +8,9 @@ defmodule Vultr do
adapter :ibrowse adapter :ibrowse
@doc """
Create a Vultr client with an API-Key for authenticated methods
"""
def client(api_key) do def client(api_key) do
Tesla.build_client [ Tesla.build_client [
{Tesla.Middleware.Headers, %{ "API-Key" => api_key }} {Tesla.Middleware.Headers, %{ "API-Key" => api_key }}
@ -1999,4 +2002,4 @@ defmodule Vultr do
], ],
}, },
] ]
end end

View File

@ -8,7 +8,7 @@ defmodule Vultr.Mixfile do
def project do def project do
[ [
app: :vultr, app: :vultr,
version: "0.1.0", version: "0.2.0",
elixir: "~> 1.3", elixir: "~> 1.3",
deps: deps(), deps: deps(),
description: @description, description: @description,
@ -25,7 +25,7 @@ defmodule Vultr.Mixfile do
[ [
{:inch_ex, "~> 0.5", only: :dev}, {:inch_ex, "~> 0.5", only: :dev},
{:ex_doc, "~> 0.14", only: :dev, runtime: false}, {:ex_doc, "~> 0.14", only: :dev, runtime: false},
{:tesla, "~> 0.7.0"}, {:tesla, "~> 0.7.1"},
{:ibrowse, "~> 4.2"}, {:ibrowse, "~> 4.2"},
{:poison, ">= 1.0.0"} {:poison, ">= 1.0.0"}
] ]