mirror of
https://github.com/ash-project/ash.git
synced 2024-09-19 21:13:10 +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.",
|
||||
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."
|
||||
],
|
||||
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 """
|
||||
|
@ -122,6 +128,9 @@ defmodule Ash.Api do
|
|||
]
|
||||
)
|
||||
|
||||
@doc false
|
||||
def parallel_side_load_schema(), do: @parallel_side_load_schema
|
||||
|
||||
@doc """
|
||||
By default, side loading data happens synchronously. In order to
|
||||
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
|
||||
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} ->
|
||||
@side_load_type :parallel
|
||||
|
||||
@side_load_config [
|
||||
supervisor: config[:supervisor],
|
||||
max_concurrency: config[:max_concurrency],
|
||||
supervisor: opts[:supervisor],
|
||||
max_concurrency: opts[:max_concurrency],
|
||||
timeout: opts[:timeout],
|
||||
shutdown: opts[:shutdown]
|
||||
]
|
||||
|
@ -157,18 +166,24 @@ defmodule Ash.Api do
|
|||
end
|
||||
|
||||
defmacro __before_compile__(env) do
|
||||
quote do
|
||||
quote generated: true do
|
||||
def default_page_size(), do: @default_page_size
|
||||
def max_page_size(), do: @max_page_size
|
||||
def mix_ins(), do: @mix_ins
|
||||
def resources(), do: @resources
|
||||
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
|
||||
use Ash.Api.Interface
|
||||
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)))
|
||||
Module.eval_quoted(__MODULE__, code)
|
||||
end)
|
||||
|
|
|
@ -43,7 +43,6 @@ defmodule Ash.Api.Interface do
|
|||
end
|
||||
|
||||
def get(api, resource, id, params \\ %{}) do
|
||||
# TODO: Figure out this interface
|
||||
params_with_filter =
|
||||
params
|
||||
|> Map.put_new(:filter, %{})
|
||||
|
@ -79,7 +78,13 @@ defmodule Ash.Api.Interface do
|
|||
{:error, "no action provided, and no primary action found"}
|
||||
|
||||
action ->
|
||||
Ash.Actions.run_read_action(resource, action, api, params)
|
||||
case api.get_resource(resource) do
|
||||
{:ok, resource} ->
|
||||
Ash.Actions.run_read_action(resource, action, api, params)
|
||||
|
||||
:error ->
|
||||
{:error, "no such resource"}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -95,7 +100,13 @@ defmodule Ash.Api.Interface do
|
|||
{:error, "no action provided, and no primary action found"}
|
||||
|
||||
action ->
|
||||
Ash.Actions.run_create_action(resource, action, api, params)
|
||||
case api.get_resource(resource) do
|
||||
{:ok, resource} ->
|
||||
Ash.Actions.run_create_action(resource, action, api, params)
|
||||
|
||||
:error ->
|
||||
{:error, "no such resource"}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ defmodule Ash.Error.ApiDslError do
|
|||
defexception [:message, :path, :option, :using]
|
||||
|
||||
def message(%{message: message, path: nil, option: option, using: using}) do
|
||||
"`use #{inspect(using)}, ...` #{option} #{message} "
|
||||
"`use #{inspect(using)}, ...` #{option} #{message}"
|
||||
end
|
||||
|
||||
def message(%{message: message, path: nil, option: option}) do
|
||||
|
|
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