summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHentioe <me@bluerain.io>2020-10-26 09:17:59 +0800
committerHentioe <me@bluerain.io>2020-10-26 09:17:59 +0800
commitbce6e6041157c26e3282efab1e79db65decbc324 (patch)
tree1f752b8a669060af6647ef28ee53b299ce67a45a
parentFix returns type (diff)
Independent endpoint construction
-rw-r--r--lib/azure_ex/ast.ex68
-rw-r--r--lib/azure_ex/endpoint.ex20
-rw-r--r--lib/azure_ex/request.ex27
-rw-r--r--test/azure_ex/endpoint_test.exs4
4 files changed, 96 insertions, 23 deletions
diff --git a/lib/azure_ex/ast.ex b/lib/azure_ex/ast.ex
new file mode 100644
index 0000000..9ff7b0b
--- /dev/null
+++ b/lib/azure_ex/ast.ex
@@ -0,0 +1,68 @@
+defmodule AzureEx.AST do
+ @moduledoc false
+
+ @path_var_re ~r/\{([^\}]+)\}/
+
+ defmacro defendpoint(name, raw_uri) do
+ # 扫描原始 URI 中的路径变量
+ r = Regex.scan(@path_var_re, raw_uri)
+ # 处理并得到路径变量名称列表
+ path_vars =
+ r
+ |> Enum.map(&List.last/1)
+ |> Enum.map(&Macro.underscore/1)
+
+ # 路径变量列表对应的参数 AST
+ path_var_args_ast =
+ path_vars
+ |> Enum.map(fn var_name -> {String.to_atom(var_name), [], Elixir} end)
+
+ # 函数签名 AST
+ fun_sign_ast = {
+ String.to_atom(Macro.underscore(name)),
+ [],
+ path_var_args_ast ++ [{:params, [], Elixir}]
+ }
+
+ # 使用变量分割 URI
+ raw_segments = Regex.split(@path_var_re, raw_uri)
+
+ unless length(raw_segments) == length(path_vars) + 1 do
+ raise """
+ Error parsing raw URI, wrong number of path variables.\n
+ URI: #{raw_uri}
+ Vars: #{inspect(path_vars)}
+ """
+ end
+
+ # <<>> 宏的参数 AST
+ splicing_args_ast =
+ raw_segments
+ |> Enum.with_index()
+ |> Enum.reduce([], fn {seg, i}, acc ->
+ acc = acc ++ [seg]
+
+ if var = Enum.at(path_vars, i) do
+ var_ast =
+ {:"::", [],
+ [
+ {{:., [], [Kernel, :to_string]}, [], [{String.to_atom(var), [], Elixir}]},
+ {:binary, [], Elixir}
+ ]}
+
+ acc ++ [var_ast]
+ else
+ acc
+ end
+ end)
+
+ # 函数体 AST(拼接字符串)
+ fun_body_ast = {:<<>>, [], splicing_args_ast}
+
+ quote do
+ def unquote(fun_sign_ast) do
+ unquote(fun_body_ast) <> "?" <> URI.encode_query(unquote({:params, [], Elixir}))
+ end
+ end
+ end
+end
diff --git a/lib/azure_ex/endpoint.ex b/lib/azure_ex/endpoint.ex
new file mode 100644
index 0000000..c880645
--- /dev/null
+++ b/lib/azure_ex/endpoint.ex
@@ -0,0 +1,20 @@
+defmodule AzureEx.Endpoint do
+ @moduledoc false
+
+ require AzureEx.AST
+ import AzureEx.AST
+
+ defendpoint(
+ "ListAllVirtualMachines",
+ "https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.Compute/virtualMachines"
+ )
+
+ @doc """
+ iex> AzureEx.Endpoint.get_network_interface("sub-1", "resgp-1", "net-1", api_version: "2020-1-1")
+ "https://management.azure.com/subscriptions/sub-1/resourceGroups/resgp-1/providers/Microsoft.Network/networkInterfaces/net-1?api_version=2020-1-1"
+ """
+ defendpoint(
+ "GetNetworkInterface",
+ "https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Network/networkInterfaces/{networkInterfaceName}"
+ )
+end
diff --git a/lib/azure_ex/request.ex b/lib/azure_ex/request.ex
index bc6d92b..675ed84 100644
--- a/lib/azure_ex/request.ex
+++ b/lib/azure_ex/request.ex
@@ -5,23 +5,15 @@ defmodule AzureEx.Request do
alias AzureEx.Config
- @api_version "2020-06-01"
-
- def endpoint(api),
- do:
- "https://management.azure.com/subscriptions/#{Config.subscription_id()}/providers/Microsoft.Compute/#{
- api
- }?api-version=#{@api_version}"
-
- @type params :: %{query_params: keyword | map, body: keyword | map}
+ @type params :: %{body: keyword | map}
@type result :: any
@type error :: any
@type httpoison_result :: {:ok, HTTPoison.Response.t()} | {:error, HTTPoison.Error.t()}
@spec call(binary, atom, params) ::
{:ok, result} | {:error, error}
- def call(api, method, params \\ %{query_params: %{}, body: %{}}) do
- method |> send(endpoint(api), params) |> handle_response()
+ def call(endpoint, method, params \\ %{body: %{}}) do
+ method |> send(endpoint, params) |> handle_response()
end
@spec handle_response({:ok, HTTPoison.Response.t()}) :: {:ok, result} | {:error, error}
@@ -30,18 +22,7 @@ defmodule AzureEx.Request do
end
@spec send(:get, String.t(), params) :: httpoison_result
- def send(:get, endpoint, %{query_params: query_params}) do
- endpoint = endpoint <> to_query(query_params)
-
+ def send(:get, endpoint, %{}) do
HTTPoison.get(endpoint, [Authorization: "Bearer #{Config.access_token()}"], Config.timeouts())
end
-
- def to_query(params, opts \\ []) when is_list(params) or is_map(params) do
- prefix_question = Keyword.get(opts, :prefix_question, false)
-
- # TODO: 待实现
- str = ""
-
- if prefix_question, do: "?" <> str, else: str
- end
end
diff --git a/test/azure_ex/endpoint_test.exs b/test/azure_ex/endpoint_test.exs
new file mode 100644
index 0000000..719c58a
--- /dev/null
+++ b/test/azure_ex/endpoint_test.exs
@@ -0,0 +1,4 @@
+defmodule AzureEx.EndpointTest do
+ use ExUnit.Case
+ doctest AzureEx.Endpoint
+end