From 2d6f3f913d5f4c5759edc5d056718b0552af31f0 Mon Sep 17 00:00:00 2001 From: Mauro Rocchi <240925+enerdgumen@users.noreply.github.com> Date: Mon, 16 May 2022 09:53:55 +0200 Subject: [PATCH] [MOTOR-1353]: [Bburago] Support for auth0 (#49) --- CHANGELOG.md | 10 ++++ README.md | 13 ++++- lib/extensions/external_resources.ex | 64 +++++++++++++++++++++ lib/graphql/client.ex | 2 +- mix.exs | 2 +- test/extensions/external_resources_test.exs | 46 +++++++++++++++ test/extensions/resources/answer.txt | 1 + test/extensions/resources/question.txt | 1 + 8 files changed, 136 insertions(+), 3 deletions(-) create mode 100644 lib/extensions/external_resources.ex create mode 100644 test/extensions/external_resources_test.exs create mode 100644 test/extensions/resources/answer.txt create mode 100644 test/extensions/resources/question.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index bb84d03..04cfd07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Next] +## [1.2.0] - 2022-05-16 + +### Added + +- New `BridgeEx.Extensions.ExternalResources` module useful to embed external resources with less boilerplate + +### Fixed + +- Typespec of `BridgeEx.Graphql.Client.call` function is now compatible with `encode_variables: true` option + ## [1.1.0] - 2022-03-07 ### Added diff --git a/README.md b/README.md index 4ff93b0..b5df233 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,17 @@ Refer to [the documentation](https://hexdocs.pm/bridge_ex/BridgeEx.Graphql.html) If you need more control on your requests you can use [`BridgeEx.Graphql.Client.call`](https://hexdocs.pm/bridge_ex/BridgeEx.Graphql.Client.html#call/7) directly. +The library supports preloading queries from external files via the `BridgeEx.Extensions.ExternalResources` optional macro: + +```elixir +defmodule MyApp.SomeServiceBridge do + use BridgeEx.Graphql, endpoint: "http://some_service.example.com" + use BridgeEx.Extensions.ExternalResources, resources: [my_query: "my_query.graphql"] + + def my_query(%{} = variables), do: call(my_query(), variables) +end +``` + #### Call options When `call`ing you can provide the following options, some of which override the ones provided when `use`ing the bridge: @@ -186,7 +197,7 @@ The package can be installed by adding `bridge_ex` to your list of dependencies ```elixir def deps do [ - {:bridge_ex, "~> 1.1.0"} + {:bridge_ex, "~> 1.2.0"} # only if you want auth0 too # {:prima_auth0_ex, "~> 0.3.0"} ] diff --git a/lib/extensions/external_resources.ex b/lib/extensions/external_resources.ex new file mode 100644 index 0000000..38e6c30 --- /dev/null +++ b/lib/extensions/external_resources.ex @@ -0,0 +1,64 @@ +defmodule BridgeEx.Extensions.ExternalResources do + @moduledoc """ + Preload a set of resources marking them as "external" and provide the related getter functions. + + ## Options + + * `resources` (required): enumerable of resource names (atoms) and their paths (strings). + Each path is assumed to be relative to the module directory. + + ## Examples + + ```elixir + defmodule MyBridge do + use BridgeEx.Extensions.ExternalResources, + resources: [ + my_query: "queries/query.graphql", + my_mutation: "mutations/mutation.graphql" + ] + + # it generates the following code: + + @external_resource "\#{__DIR__}/queries/query.graphql" + @external_resource "\#{__DIR__}/mutations/mutation.graphql" + + @spec my_query() :: String.t() + def my_query, do: Map.fetch!(external_resources(), :my_query) + @spec my_mutation() :: String.t() + def my_mutation, do: Map.fetch!(external_resources(), :my_mutation) + + defp external_resources do + %{ + my_query: "contents of query.graphql", + my_mutation: "contents of mutation.graphql" + } + end + end + ``` + """ + + defmacro __using__(resources: resources) do + dir = Path.dirname(__CALLER__.file) + resources = for {name, path} <- resources, do: {name, Path.join(dir, path)} + contents = for {name, path} <- resources, into: %{}, do: {name, File.read!(path)} + + getters = + for {name, _path} <- resources do + quote do + @spec unquote(name)() :: String.t() + def unquote(name)(), do: Map.fetch!(external_resources(), unquote(name)) + end + end + + external_resource_attributes = + for {_name, path} <- resources do + quote do: @external_resource(unquote(path)) + end + + quote generated: true do + unquote_splicing(external_resource_attributes) + unquote_splicing(getters) + defp external_resources, do: unquote(Macro.escape(contents)) + end + end +end diff --git a/lib/graphql/client.ex b/lib/graphql/client.ex index bb8c858..ea2ef01 100644 --- a/lib/graphql/client.ex +++ b/lib/graphql/client.ex @@ -29,7 +29,7 @@ defmodule BridgeEx.Graphql.Client do @spec call( url :: String.t(), query :: String.t(), - variables :: map(), + variables :: map() | String.t(), http_options :: Keyword.t(), http_headers :: map(), retry_options :: Keyword.t(), diff --git a/mix.exs b/mix.exs index 6f858f6..6a41238 100644 --- a/mix.exs +++ b/mix.exs @@ -2,7 +2,7 @@ defmodule BridgeEx.MixProject do use Mix.Project @source_url "https://github.com/primait/bridge_ex" - @version "1.1.0" + @version "1.2.0" def project do [ diff --git a/test/extensions/external_resources_test.exs b/test/extensions/external_resources_test.exs new file mode 100644 index 0000000..f9cea22 --- /dev/null +++ b/test/extensions/external_resources_test.exs @@ -0,0 +1,46 @@ +defmodule BridgeEx.Extensions.ExternalResourcesTest do + use ExUnit.Case + + defmodule Example do + use BridgeEx.Extensions.ExternalResources, + resources: [ + question: "resources/question.txt", + answer: "resources/answer.txt" + ] + end + + test "it generates getter functions" do + assert "How many roads must a man walk down?" == Example.question() + assert "42" == Example.answer() + end + + test "it marks files as external resources" do + paths = + for {:external_resource, [path]} <- Example.__info__(:attributes), + into: MapSet.new(), + do: Path.relative_to(path, __DIR__) + + assert MapSet.new(["resources/question.txt", "resources/answer.txt"]) == paths + end + + test "it fails to compile if no resources are given" do + quoted = + quote do + use BridgeEx.Extensions.ExternalResources + end + + assert_raise FunctionClauseError, fn -> Code.eval_quoted(quoted) end + end + + test "it fails to compile if a file is missing" do + quoted = + quote do + use BridgeEx.Extensions.ExternalResources, + resources: [ + missing: "missing.txt" + ] + end + + assert_raise File.Error, fn -> Code.eval_quoted(quoted) end + end +end diff --git a/test/extensions/resources/answer.txt b/test/extensions/resources/answer.txt new file mode 100644 index 0000000..f70d7bb --- /dev/null +++ b/test/extensions/resources/answer.txt @@ -0,0 +1 @@ +42 \ No newline at end of file diff --git a/test/extensions/resources/question.txt b/test/extensions/resources/question.txt new file mode 100644 index 0000000..599b574 --- /dev/null +++ b/test/extensions/resources/question.txt @@ -0,0 +1 @@ +How many roads must a man walk down? \ No newline at end of file