From 8279a50c92a69a7a5ed55e05c8cfbce6f5e8a7b8 Mon Sep 17 00:00:00 2001 From: avitex Date: Sun, 30 Jul 2017 19:37:35 +1000 Subject: [PATCH] Improve request implementation and return format --- .travis.yml | 2 +- README.md | 18 ++----- lib/request.ex | 133 +++++++++++++++++++++++++++++++------------------ lib/vultr.ex | 5 +- mix.exs | 4 +- 5 files changed, 97 insertions(+), 65 deletions(-) diff --git a/.travis.yml b/.travis.yml index 64c5a1a..6b08972 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: elixir elixir: - - 1.4.0 + - 1.5.0 notifications: recipients: - theavitex@gmail.com diff --git a/README.md b/README.md index 2ebe586..a9702b6 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ [![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) # Vultr @@ -13,7 +13,7 @@ Documentation hosted on [hexdocs](https://hexdocs.pm/vultr). ```elixir def deps do - [{:vultr, "~> 0.1.0"}] + [{:vultr, "~> 0.2.0"}] end ``` @@ -24,20 +24,12 @@ Documentation hosted on [hexdocs](https://hexdocs.pm/vultr). Vultr.app_list() # Example response - %{ - 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" - } + {:ok, %{ "1" => %{"APPID" => "1", "deploy_name" => "LEMP on CentOS 6 x64", ... }, ... }} ``` #### Using authenticated methods ```elixir client = Vultr.client("") - Vultr.server_list(client, %{}) - ``` \ No newline at end of file + Vultr.server_list(client, []) + ``` diff --git a/lib/request.ex b/lib/request.ex index 8b4ba2a..e731cc2 100644 --- a/lib/request.ex +++ b/lib/request.ex @@ -1,6 +1,10 @@ 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 quote do @@ -15,55 +19,88 @@ defmodule Vultr.Request do endpoint_required_access = Keyword.get(endpoint_opts, :required_access, nil) endpoint_has_params = (length(endpoint_params) > 0) - request_client_var = Macro.var(:client, nil) - request_params_var = Macro.var(:params, nil) + common_args = [__CALLER__.module, endpoint_method, endpoint_path] - 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 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 -> - [request_client_var] + quote do + def unquote(func_name)(client) do + unquote(func).(unquote_splicing(common_args), client, []) + end + end 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 -> - [] - end - - tesla_args = - if endpoint_requires_api_key do - [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 + quote do + def unquote(func_name)() do + unquote(func).(unquote_splicing(common_args), nil, []) + end + end end quote do - @doc unquote(documentation(endpoint_method, endpoint_path, endpoint_description, endpoint_params, endpoint_required_access, endpoint_requires_api_key)) - def unquote(function_name(endpoint_path))(unquote_splicing(function_args)) do - unquote(endpoint_method)(unquote_splicing(tesla_args)) - end + @doc unquote(func_doc) + unquote(func_body) 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 type = Keyword.fetch!(opts, :type) @@ -84,7 +121,7 @@ defmodule Vultr.Request do ################################################## # 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 = """ #{desc} """ @@ -130,14 +167,6 @@ defmodule Vultr.Request do 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 if !Enum.any?([:get, :post], fn supported -> method == supported end) do raise ArgumentError, message: "Bad method" @@ -145,4 +174,12 @@ defmodule Vultr.Request do method |> Atom.to_string |> String.upcase() end -end \ No newline at end of file + + 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 diff --git a/lib/vultr.ex b/lib/vultr.ex index de0678a..fa3db60 100644 --- a/lib/vultr.ex +++ b/lib/vultr.ex @@ -8,6 +8,9 @@ defmodule Vultr do adapter :ibrowse + @doc """ + Create a Vultr client with an API-Key for authenticated methods + """ def client(api_key) do Tesla.build_client [ {Tesla.Middleware.Headers, %{ "API-Key" => api_key }} @@ -1999,4 +2002,4 @@ defmodule Vultr do ], }, ] -end \ No newline at end of file +end diff --git a/mix.exs b/mix.exs index 9a1a631..f1b9089 100644 --- a/mix.exs +++ b/mix.exs @@ -8,7 +8,7 @@ defmodule Vultr.Mixfile do def project do [ app: :vultr, - version: "0.1.0", + version: "0.2.0", elixir: "~> 1.3", deps: deps(), description: @description, @@ -25,7 +25,7 @@ defmodule Vultr.Mixfile do [ {:inch_ex, "~> 0.5", only: :dev}, {:ex_doc, "~> 0.14", only: :dev, runtime: false}, - {:tesla, "~> 0.7.0"}, + {:tesla, "~> 0.7.1"}, {:ibrowse, "~> 4.2"}, {:poison, ">= 1.0.0"} ]