mirror of
https://github.com/ash-project/ash.git
synced 2024-09-20 05:23:03 +12:00
test the api dsl
This commit is contained in:
parent
eea6fc1003
commit
83d1761dd8
4 changed files with 223 additions and 9 deletions
|
@ -17,6 +17,12 @@ defmodule Ash.Api do
|
||||||
"The maximum page size for any read action. Any request for a higher page size will simply use this number. Uses the smaller of the Api's or Resource's value.",
|
"The maximum page size for any read action. Any request for a higher page size will simply use this number. Uses the smaller of the Api's or Resource's value.",
|
||||||
default_page_size:
|
default_page_size:
|
||||||
"The default page size for any read action. If no page size is specified, this value is used. Uses the smaller of the Api's or Resource's value."
|
"The default page size for any read action. If no page size is specified, this value is used. Uses the smaller of the Api's or Resource's value."
|
||||||
|
],
|
||||||
|
constraints: [
|
||||||
|
max_page_size:
|
||||||
|
{&Ash.Constraints.greater_than_zero?/1, "must be greater than zero"},
|
||||||
|
default_page_size:
|
||||||
|
{&Ash.Constraints.greater_than_zero?/1, "must be greater than zero"}
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
|
@ -122,6 +128,9 @@ defmodule Ash.Api do
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@doc false
|
||||||
|
def parallel_side_load_schema(), do: @parallel_side_load_schema
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
By default, side loading data happens synchronously. In order to
|
By default, side loading data happens synchronously. In order to
|
||||||
side load in parallel, you must start a task supervisor in your application
|
side load in parallel, you must start a task supervisor in your application
|
||||||
|
@ -136,13 +145,13 @@ defmodule Ash.Api do
|
||||||
"""
|
"""
|
||||||
defmacro parallel_side_load(opts \\ []) do
|
defmacro parallel_side_load(opts \\ []) do
|
||||||
quote bind_quoted: [opts: opts] do
|
quote bind_quoted: [opts: opts] do
|
||||||
case Ashton.validate(opts, @parallel_side_load_schema) do
|
case Ashton.validate(opts, Ash.Api.parallel_side_load_schema()) do
|
||||||
{:ok, opts} ->
|
{:ok, opts} ->
|
||||||
@side_load_type :parallel
|
@side_load_type :parallel
|
||||||
|
|
||||||
@side_load_config [
|
@side_load_config [
|
||||||
supervisor: config[:supervisor],
|
supervisor: opts[:supervisor],
|
||||||
max_concurrency: config[:max_concurrency],
|
max_concurrency: opts[:max_concurrency],
|
||||||
timeout: opts[:timeout],
|
timeout: opts[:timeout],
|
||||||
shutdown: opts[:shutdown]
|
shutdown: opts[:shutdown]
|
||||||
]
|
]
|
||||||
|
@ -157,18 +166,24 @@ defmodule Ash.Api do
|
||||||
end
|
end
|
||||||
|
|
||||||
defmacro __before_compile__(env) do
|
defmacro __before_compile__(env) do
|
||||||
quote do
|
quote generated: true do
|
||||||
def default_page_size(), do: @default_page_size
|
def default_page_size(), do: @default_page_size
|
||||||
def max_page_size(), do: @max_page_size
|
def max_page_size(), do: @max_page_size
|
||||||
def mix_ins(), do: @mix_ins
|
def mix_ins(), do: @mix_ins
|
||||||
def resources(), do: @resources
|
def resources(), do: @resources
|
||||||
def side_load_config(), do: {@side_load_type, @side_load_config}
|
def side_load_config(), do: {@side_load_type, @side_load_config}
|
||||||
|
|
||||||
|
def get_resource(mod) when mod in @resources, do: {:ok, mod}
|
||||||
|
|
||||||
|
def get_resource(name) do
|
||||||
|
Keyword.fetch(@named_resources, name)
|
||||||
|
end
|
||||||
|
|
||||||
if @interface? do
|
if @interface? do
|
||||||
use Ash.Api.Interface
|
use Ash.Api.Interface
|
||||||
end
|
end
|
||||||
|
|
||||||
Enum.map(@mix_ins || [], fn hook_module ->
|
Enum.map(@mix_ins, fn hook_module ->
|
||||||
code = hook_module.before_compile_hook(unquote(Macro.escape(env)))
|
code = hook_module.before_compile_hook(unquote(Macro.escape(env)))
|
||||||
Module.eval_quoted(__MODULE__, code)
|
Module.eval_quoted(__MODULE__, code)
|
||||||
end)
|
end)
|
||||||
|
|
|
@ -43,7 +43,6 @@ defmodule Ash.Api.Interface do
|
||||||
end
|
end
|
||||||
|
|
||||||
def get(api, resource, id, params \\ %{}) do
|
def get(api, resource, id, params \\ %{}) do
|
||||||
# TODO: Figure out this interface
|
|
||||||
params_with_filter =
|
params_with_filter =
|
||||||
params
|
params
|
||||||
|> Map.put_new(:filter, %{})
|
|> Map.put_new(:filter, %{})
|
||||||
|
@ -79,7 +78,13 @@ defmodule Ash.Api.Interface do
|
||||||
{:error, "no action provided, and no primary action found"}
|
{:error, "no action provided, and no primary action found"}
|
||||||
|
|
||||||
action ->
|
action ->
|
||||||
|
case api.get_resource(resource) do
|
||||||
|
{:ok, resource} ->
|
||||||
Ash.Actions.run_read_action(resource, action, api, params)
|
Ash.Actions.run_read_action(resource, action, api, params)
|
||||||
|
|
||||||
|
:error ->
|
||||||
|
{:error, "no such resource"}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -95,7 +100,13 @@ defmodule Ash.Api.Interface do
|
||||||
{:error, "no action provided, and no primary action found"}
|
{:error, "no action provided, and no primary action found"}
|
||||||
|
|
||||||
action ->
|
action ->
|
||||||
|
case api.get_resource(resource) do
|
||||||
|
{:ok, resource} ->
|
||||||
Ash.Actions.run_create_action(resource, action, api, params)
|
Ash.Actions.run_create_action(resource, action, api, params)
|
||||||
|
|
||||||
|
:error ->
|
||||||
|
{:error, "no such resource"}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
188
test/api/api_test.exs
Normal file
188
test/api/api_test.exs
Normal file
|
@ -0,0 +1,188 @@
|
||||||
|
defmodule Ash.Test.Resource.ApiTest do
|
||||||
|
use ExUnit.Case, async: true
|
||||||
|
|
||||||
|
defmacrop defposts(do: body) do
|
||||||
|
quote do
|
||||||
|
defmodule Post do
|
||||||
|
use Ash.Resource, name: "posts", type: "post", primary_key: false
|
||||||
|
|
||||||
|
unquote(body)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defmacrop defapi(opts \\ [], do: body) do
|
||||||
|
quote do
|
||||||
|
defmodule Api do
|
||||||
|
use Ash.Api, unquote(opts)
|
||||||
|
|
||||||
|
unquote(body)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "representation" do
|
||||||
|
test "if `interface?: false` is set, interface functions are not defined" do
|
||||||
|
defapi(interface?: false) do
|
||||||
|
end
|
||||||
|
|
||||||
|
interface_funcs = [
|
||||||
|
get!: 3,
|
||||||
|
get!: 3,
|
||||||
|
read!: 2,
|
||||||
|
read: 2,
|
||||||
|
create!: 2,
|
||||||
|
create: 2,
|
||||||
|
update: 3,
|
||||||
|
update!: 3,
|
||||||
|
destroy: 3,
|
||||||
|
destroy: 3
|
||||||
|
]
|
||||||
|
|
||||||
|
for {func, arity} <- interface_funcs do
|
||||||
|
refute :erlang.function_exported(Api, func, arity)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "validation" do
|
||||||
|
test "it fails if `interface?` is not a boolean" do
|
||||||
|
assert_raise(
|
||||||
|
Ash.Error.ApiDslError,
|
||||||
|
"`use Ash.Test.Resource.ApiTest.Api, ...` interface? must be of type :boolean",
|
||||||
|
fn ->
|
||||||
|
defapi(interface?: 10) do
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it fails if `max_page_size` is not an integer" do
|
||||||
|
assert_raise(
|
||||||
|
Ash.Error.ApiDslError,
|
||||||
|
"`use Ash.Test.Resource.ApiTest.Api, ...` max_page_size must be of type :integer",
|
||||||
|
fn ->
|
||||||
|
defapi(max_page_size: "ten") do
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it fails if `max_page_size` is zero" do
|
||||||
|
assert_raise(
|
||||||
|
Ash.Error.ApiDslError,
|
||||||
|
"`use Ash.Test.Resource.ApiTest.Api, ...` max_page_size failed constraint: must be greater than zero",
|
||||||
|
fn ->
|
||||||
|
defapi(max_page_size: 0) do
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it fails if `default_page_size` is not an integer" do
|
||||||
|
assert_raise(
|
||||||
|
Ash.Error.ApiDslError,
|
||||||
|
"`use Ash.Test.Resource.ApiTest.Api, ...` default_page_size must be of type :integer",
|
||||||
|
fn ->
|
||||||
|
defapi(default_page_size: "ten") do
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it fails if `default_page_size` is zero" do
|
||||||
|
assert_raise(
|
||||||
|
Ash.Error.ApiDslError,
|
||||||
|
"`use Ash.Test.Resource.ApiTest.Api, ...` default_page_size failed constraint: must be greater than zero",
|
||||||
|
fn ->
|
||||||
|
defapi(default_page_size: 0) do
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it fails if the parallel_side_load supervisor is not an atom" do
|
||||||
|
assert_raise(
|
||||||
|
Ash.Error.ApiDslError,
|
||||||
|
"option supervisor at parallel_side_load must be of type :atom",
|
||||||
|
fn ->
|
||||||
|
defapi do
|
||||||
|
parallel_side_load(supervisor: "foo")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it fails if the max_concurrency is not an integer" do
|
||||||
|
assert_raise(
|
||||||
|
Ash.Error.ApiDslError,
|
||||||
|
"option max_concurrency at parallel_side_load must be of type :integer",
|
||||||
|
fn ->
|
||||||
|
defapi do
|
||||||
|
parallel_side_load(supervisor: :foo, max_concurrency: "foo")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it fails if max_concurrency is zero" do
|
||||||
|
assert_raise(
|
||||||
|
Ash.Error.ApiDslError,
|
||||||
|
"option max_concurrency at parallel_side_load failed constraint: must be greater than zero",
|
||||||
|
fn ->
|
||||||
|
defapi do
|
||||||
|
parallel_side_load(supervisor: :foo, max_concurrency: 0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it fails if the timeout is not an integer" do
|
||||||
|
assert_raise(
|
||||||
|
Ash.Error.ApiDslError,
|
||||||
|
"option timeout at parallel_side_load must be of type :integer",
|
||||||
|
fn ->
|
||||||
|
defapi do
|
||||||
|
parallel_side_load(supervisor: :foo, timeout: "foo")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it fails if timeout is negative" do
|
||||||
|
assert_raise(
|
||||||
|
Ash.Error.ApiDslError,
|
||||||
|
"option timeout at parallel_side_load failed constraint: must be positive",
|
||||||
|
fn ->
|
||||||
|
defapi do
|
||||||
|
parallel_side_load(supervisor: :foo, timeout: -1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it fails if the shutdown is not an integer" do
|
||||||
|
assert_raise(
|
||||||
|
Ash.Error.ApiDslError,
|
||||||
|
"option shutdown at parallel_side_load must be of type :integer",
|
||||||
|
fn ->
|
||||||
|
defapi do
|
||||||
|
parallel_side_load(supervisor: :foo, shutdown: "foo")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it fails if shutdown is negative" do
|
||||||
|
assert_raise(
|
||||||
|
Ash.Error.ApiDslError,
|
||||||
|
"option shutdown at parallel_side_load failed constraint: must be positive",
|
||||||
|
fn ->
|
||||||
|
defapi do
|
||||||
|
parallel_side_load(supervisor: :foo, shutdown: -1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue