mirror of
https://github.com/ash-project/ash_json_api_wrapper.git
synced 2024-09-19 12:53:10 +12:00
WIP
This commit is contained in:
parent
11c76e2bde
commit
4cf510fbd8
13 changed files with 595 additions and 32 deletions
|
@ -1,4 +1,11 @@
|
||||||
# Used by "mix format"
|
# THIS FILE IS AUTOGENERATED USING `mix ash.formatter`
|
||||||
|
# DONT MODIFY IT BY HAND
|
||||||
|
locals_without_parens = [endpoint_base: 1, entity_path: 1, field: 1, field: 2, finch: 1, path: 1]
|
||||||
|
|
||||||
[
|
[
|
||||||
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
|
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"],
|
||||||
|
locals_without_parens: locals_without_parens,
|
||||||
|
export: [
|
||||||
|
locals_without_parens: locals_without_parens
|
||||||
|
]
|
||||||
]
|
]
|
||||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -20,7 +20,7 @@ erl_crash.dump
|
||||||
*.ez
|
*.ez
|
||||||
|
|
||||||
# Ignore package tarball (built via "mix hex.build").
|
# Ignore package tarball (built via "mix hex.build").
|
||||||
ash_api_wrapper-*.tar
|
ash_json_api_wrapper-*.tar
|
||||||
|
|
||||||
# Temporary files, for example, from tests.
|
# Temporary files, for example, from tests.
|
||||||
/tmp/
|
/tmp/
|
||||||
|
|
7
.vscode/settings.json
vendored
Normal file
7
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"workbench.colorCustomizations": {
|
||||||
|
"activityBar.background": "#041593",
|
||||||
|
"titleBar.activeBackground": "#061ECE",
|
||||||
|
"titleBar.activeForeground": "#F9F9FF"
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,21 +1,21 @@
|
||||||
# AshApiWrapper
|
# AshJsonApiWrapper
|
||||||
|
|
||||||
**TODO: Add description**
|
**TODO: Add description**
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
If [available in Hex](https://hex.pm/docs/publish), the package can be installed
|
If [available in Hex](https://hex.pm/docs/publish), the package can be installed
|
||||||
by adding `ash_api_wrapper` to your list of dependencies in `mix.exs`:
|
by adding `ash_json_api_wrapper` to your list of dependencies in `mix.exs`:
|
||||||
|
|
||||||
```elixir
|
```elixir
|
||||||
def deps do
|
def deps do
|
||||||
[
|
[
|
||||||
{:ash_api_wrapper, "~> 0.1.0"}
|
{:ash_json_api_wrapper, "~> 0.1.0"}
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
|
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
|
||||||
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
|
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
|
||||||
be found at [https://hexdocs.pm/ash_api_wrapper](https://hexdocs.pm/ash_api_wrapper).
|
be found at [https://hexdocs.pm/ash_json_api_wrapper](https://hexdocs.pm/ash_json_api_wrapper).
|
||||||
|
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
defmodule AshApiWrapper do
|
|
||||||
@moduledoc """
|
|
||||||
Documentation for `AshApiWrapper`.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Hello world.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
iex> AshApiWrapper.hello()
|
|
||||||
:world
|
|
||||||
|
|
||||||
"""
|
|
||||||
def hello do
|
|
||||||
:world
|
|
||||||
end
|
|
||||||
end
|
|
95
lib/ash_json_api_wrapper.ex
Normal file
95
lib/ash_json_api_wrapper.ex
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
defmodule AshJsonApiWrapper do
|
||||||
|
@moduledoc """
|
||||||
|
"""
|
||||||
|
|
||||||
|
alias Ash.Dsl.Extension
|
||||||
|
|
||||||
|
@spec endpoint_base(Ash.Resource.t()) :: String.t() | nil
|
||||||
|
def endpoint_base(resource) do
|
||||||
|
Extension.get_opt(resource, [:json_api_wrapper, :endpoints], :base, nil, false)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec finch(Ash.Resource.t()) :: module | nil
|
||||||
|
def finch(resource) do
|
||||||
|
Extension.get_opt(resource, [:json_api_wrapper], :finch, nil, false)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec before_request(Ash.Resource.t()) :: module | nil
|
||||||
|
def before_request(resource) do
|
||||||
|
Extension.get_opt(resource, [:json_api_wrapper], :before_request, nil, false)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec field(Ash.Resource.t(), atom) :: AshJsonApiWrapper.Field.t() | nil
|
||||||
|
def field(resource, name) do
|
||||||
|
resource
|
||||||
|
|> fields()
|
||||||
|
|> Enum.find(&(&1.name == name))
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec fields(Ash.Resource.t()) :: list(AshJsonApiWrapper.Field.t())
|
||||||
|
def fields(resource) do
|
||||||
|
Extension.get_entities(resource, [:json_api_wrapper, :fields])
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec endpoint(Ash.Resource.t(), atom) :: AshJsonApiWrapper.Endpoint.t() | nil
|
||||||
|
def endpoint(resource, action) do
|
||||||
|
default_endpoint = AshJsonApiWrapper.Endpoint.default(endpoint_base(resource))
|
||||||
|
|
||||||
|
resource
|
||||||
|
|> Extension.get_entities([:json_api_wrapper, :endpoints])
|
||||||
|
|> Enum.find(&(&1.action == action))
|
||||||
|
|> case do
|
||||||
|
nil ->
|
||||||
|
default_endpoint
|
||||||
|
endpoint ->
|
||||||
|
if default_endpoint.path && endpoint.path do
|
||||||
|
%{endpoint | path: default_endpoint.path <> endpoint.path}
|
||||||
|
else
|
||||||
|
%{endpoint | path: endpoint.path || default_endpoint.path}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec endpoints(Ash.Resource.t()) :: list(AshJsonApiWrapper.Endpoint.t())
|
||||||
|
def endpoints(resource) do
|
||||||
|
Extension.get_entities(resource, [:json_api_wrapper, :endpoints])
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec set_body_param(query_or_changeset, String.t(), any) :: query_or_changeset
|
||||||
|
when query_or_changeset: Ash.Query.t() | Ash.Changeset.t()
|
||||||
|
def set_body_param(query, key, value) do
|
||||||
|
new_context =
|
||||||
|
query.context
|
||||||
|
|> Map.put_new(:data_layer, %{})
|
||||||
|
|> Map.update!(:data_layer, fn data_layer ->
|
||||||
|
data_layer
|
||||||
|
|> Map.put_new(:body, %{})
|
||||||
|
|> Map.update!(:body, &Map.put(&1, key, value))
|
||||||
|
end)
|
||||||
|
|
||||||
|
%{query | context: new_context}
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec merge_query_params(query_or_changeset, map) :: query_or_changeset
|
||||||
|
when query_or_changeset: Ash.Query.t() | Ash.Changeset.t()
|
||||||
|
def merge_query_params(%Ash.Query{} = query, params) do
|
||||||
|
Ash.Query.set_context(query, %{data_layer: %{query_params: params}})
|
||||||
|
end
|
||||||
|
|
||||||
|
def merge_query_params(%Ash.Changeset{} = changeset, params) do
|
||||||
|
Ash.Changeset.set_context(changeset, %{data_layer: %{query_params: params}})
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec set_query_params(query_or_changeset, map) :: query_or_changeset
|
||||||
|
when query_or_changeset: Ash.Query.t() | Ash.Changeset.t()
|
||||||
|
def set_query_params(query, params) do
|
||||||
|
new_context =
|
||||||
|
query.context
|
||||||
|
|> Map.put_new(:data_layer, %{})
|
||||||
|
|> Map.update!(:data_layer, fn data_layer ->
|
||||||
|
Map.put(data_layer, :query_params, params)
|
||||||
|
end)
|
||||||
|
|
||||||
|
%{query | context: new_context}
|
||||||
|
end
|
||||||
|
end
|
324
lib/data_layer.ex
Normal file
324
lib/data_layer.ex
Normal file
|
@ -0,0 +1,324 @@
|
||||||
|
defmodule AshJsonApiWrapper.DataLayer do
|
||||||
|
@field %Ash.Dsl.Entity{
|
||||||
|
name: :field,
|
||||||
|
target: AshJsonApiWrapper.Field,
|
||||||
|
schema: AshJsonApiWrapper.Field.schema(),
|
||||||
|
docs: """
|
||||||
|
Configure an individual field's behavior, for example its path in the response.
|
||||||
|
""",
|
||||||
|
args: [:name]
|
||||||
|
}
|
||||||
|
|
||||||
|
@fields %Ash.Dsl.Section{
|
||||||
|
name: :fields,
|
||||||
|
describe: "Contains configuration for individual fields in the response",
|
||||||
|
entities: [
|
||||||
|
@field
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
@endpoint %Ash.Dsl.Entity{
|
||||||
|
name: :endpoint,
|
||||||
|
target: AshJsonApiWrapper.Endpoint,
|
||||||
|
schema: AshJsonApiWrapper.Endpoint.schema(),
|
||||||
|
docs: """
|
||||||
|
Configure the endpoint that a given action will use.
|
||||||
|
|
||||||
|
Accepts overrides for fields as well.
|
||||||
|
""",
|
||||||
|
entities: [
|
||||||
|
fields: @field
|
||||||
|
],
|
||||||
|
args: [:action]
|
||||||
|
}
|
||||||
|
|
||||||
|
@endpoints %Ash.Dsl.Section{
|
||||||
|
name: :endpoints,
|
||||||
|
describe: "Contains the configuration for the endpoints used in each action",
|
||||||
|
schema: [
|
||||||
|
base: [
|
||||||
|
type: :string,
|
||||||
|
doc: "The base endpoint to which all relative urls provided will be appended."
|
||||||
|
]
|
||||||
|
],
|
||||||
|
entities: [
|
||||||
|
@endpoint
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
@json_api_wrapper %Ash.Dsl.Section{
|
||||||
|
name: :json_api_wrapper,
|
||||||
|
describe: "Contains the configuration for the json_api_wrapper data layer",
|
||||||
|
sections: [
|
||||||
|
@fields,
|
||||||
|
@endpoints
|
||||||
|
],
|
||||||
|
schema: [
|
||||||
|
before_request: [
|
||||||
|
type: :any,
|
||||||
|
doc: """
|
||||||
|
A function that takes the finch request and returns the finch request.
|
||||||
|
Will be called just before the request is made for all requests, but before JSON encoding the body and query encoding the query parameters.
|
||||||
|
"""
|
||||||
|
],
|
||||||
|
finch: [
|
||||||
|
type: :atom,
|
||||||
|
required: true,
|
||||||
|
doc: """
|
||||||
|
The name used when setting up your finch supervisor in your Application.
|
||||||
|
|
||||||
|
e.g in this example from finch's readme:
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
{Finch, name: MyConfiguredFinch <- this value}
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
use Ash.Dsl.Extension, sections: [@json_api_wrapper]
|
||||||
|
|
||||||
|
defmodule Query do
|
||||||
|
defstruct [:request, :action]
|
||||||
|
end
|
||||||
|
|
||||||
|
@behaviour Ash.DataLayer
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def can?(_, :create), do: true
|
||||||
|
def can?(_, _), do: false
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def resource_to_query(resource) do
|
||||||
|
%Query{request: Finch.build(:get, AshJsonApiWrapper.endpoint_base(resource))}
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def set_context(_resource, query, context) do
|
||||||
|
params = context[:data_layer][:query_params]
|
||||||
|
|
||||||
|
if params do
|
||||||
|
{:ok, %{query | request: %{query.request | query: params}, action: context[:action]}}
|
||||||
|
else
|
||||||
|
{:ok, %{query | action: context[:action]}}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def create(resource, changeset) do
|
||||||
|
endpoint = AshJsonApiWrapper.endpoint(resource, changeset.action.name)
|
||||||
|
|
||||||
|
base =
|
||||||
|
case endpoint && endpoint.fields_in do
|
||||||
|
:body ->
|
||||||
|
changeset.context[:data_layer][:body] || %{}
|
||||||
|
|
||||||
|
:params ->
|
||||||
|
changeset.context[:data_layer][:query_params] || %{}
|
||||||
|
end
|
||||||
|
|
||||||
|
{:ok, with_attrs} =
|
||||||
|
changeset.attributes
|
||||||
|
|> Kernel.||(%{})
|
||||||
|
|> Enum.reduce_while({:ok, base}, fn {key, value}, {:ok, acc} ->
|
||||||
|
attribute = Ash.Resource.Info.attribute(resource, key)
|
||||||
|
field = AshJsonApiWrapper.field(resource, attribute.name)
|
||||||
|
|
||||||
|
case Ash.Type.dump_to_embedded(
|
||||||
|
attribute.type,
|
||||||
|
value,
|
||||||
|
attribute.constraints
|
||||||
|
) do
|
||||||
|
{:ok, dumped} ->
|
||||||
|
path =
|
||||||
|
if field && field.write_path do
|
||||||
|
field.write_path
|
||||||
|
else
|
||||||
|
[to_string(attribute.name)]
|
||||||
|
end
|
||||||
|
|
||||||
|
path =
|
||||||
|
if endpoint.write_entity_path do
|
||||||
|
endpoint.write_entity_path ++ path
|
||||||
|
else
|
||||||
|
path
|
||||||
|
end
|
||||||
|
|
||||||
|
{:cont, {:ok, put_in!(acc, path, dumped)}}
|
||||||
|
|
||||||
|
:error ->
|
||||||
|
{:halt,
|
||||||
|
{:error,
|
||||||
|
Ash.Error.Changes.InvalidAttribute.exception(
|
||||||
|
field: attribute.name,
|
||||||
|
message: "Could not be dumped to embedded"
|
||||||
|
)}}
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
{body, params} =
|
||||||
|
case endpoint.fields_in do
|
||||||
|
:params ->
|
||||||
|
{changeset.context[:data_layer][:body] || %{}, with_attrs}
|
||||||
|
|
||||||
|
:body ->
|
||||||
|
{with_attrs, changeset.context[:data_layer][:query_params] || %{}}
|
||||||
|
end
|
||||||
|
|
||||||
|
:post
|
||||||
|
|> Finch.build(
|
||||||
|
endpoint.path || AshJsonApiWrapper.endpoint_base(resource),
|
||||||
|
[{"Content-Type", "application/json"}, {"Accept", "application/json"}],
|
||||||
|
body
|
||||||
|
)
|
||||||
|
|> Map.put(:query, params)
|
||||||
|
|> request(resource)
|
||||||
|
|
||||||
|
{:ok, struct(resource, [])}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp put_in!(body, [key], value) do
|
||||||
|
Map.put(body, key, value)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp put_in!(body, [first | rest], value) do
|
||||||
|
body
|
||||||
|
|> Map.put_new(first, %{})
|
||||||
|
|> Map.update!(first, &put_in!(&1, rest, value))
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def run_query(query, resource) do
|
||||||
|
endpoint = AshJsonApiWrapper.endpoint(resource, query.action.name)
|
||||||
|
|
||||||
|
with {:ok, %{status: status} = response} when status >= 200 and status < 300 <-
|
||||||
|
request(query.request, resource),
|
||||||
|
{:ok, body} <- Jason.decode(response.body),
|
||||||
|
{:ok, entities} <- get_entities(body, endpoint) do
|
||||||
|
process_entities(entities, resource)
|
||||||
|
else
|
||||||
|
{:ok, %{status: status} = response} ->
|
||||||
|
{:error,
|
||||||
|
"Received status code #{status} in request #{inspect(query.request)}. Response: #{inspect(response)}"}
|
||||||
|
|
||||||
|
other ->
|
||||||
|
other
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp request(request, resource) do
|
||||||
|
case AshJsonApiWrapper.before_request(resource) do
|
||||||
|
nil ->
|
||||||
|
request
|
||||||
|
|> encode_query()
|
||||||
|
|> encode_body()
|
||||||
|
|> IO.inspect()
|
||||||
|
|> Finch.request(AshJsonApiWrapper.finch(resource))
|
||||||
|
|
||||||
|
hook ->
|
||||||
|
request
|
||||||
|
|> hook.()
|
||||||
|
|> encode_query()
|
||||||
|
|> encode_body()
|
||||||
|
|> IO.inspect()
|
||||||
|
|> Finch.request(AshJsonApiWrapper.finch(resource))
|
||||||
|
end
|
||||||
|
|> IO.inspect()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp encode_query(%{query: query} = request) when is_map(query) do
|
||||||
|
%{request | query: URI.encode_query(query)}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp encode_query(request), do: request
|
||||||
|
|
||||||
|
defp encode_body(%{body: body} = request) when is_map(body) do
|
||||||
|
%{request | body: Jason.encode!(body)}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp encode_body(request), do: request
|
||||||
|
|
||||||
|
defp process_entities(entities, resource) do
|
||||||
|
Enum.reduce_while(entities, {:ok, []}, fn entity, {:ok, entities} ->
|
||||||
|
case process_entity(entity, resource) do
|
||||||
|
{:ok, entity} -> {:cont, {:ok, [entity | entities]}}
|
||||||
|
{:error, error} -> {:halt, {:error, error}}
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|> case do
|
||||||
|
{:ok, entities} -> {:ok, Enum.reverse(entities)}
|
||||||
|
{:error, error} -> {:error, error}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp process_entity(entity, resource) do
|
||||||
|
resource
|
||||||
|
|> Ash.Resource.Info.attributes()
|
||||||
|
|> Enum.reduce_while(
|
||||||
|
{:ok,
|
||||||
|
struct(resource,
|
||||||
|
__meta__: %Ecto.Schema.Metadata{
|
||||||
|
state: :loaded
|
||||||
|
}
|
||||||
|
)},
|
||||||
|
fn attr, {:ok, record} ->
|
||||||
|
case get_field(entity, attr, resource) do
|
||||||
|
{:ok, value} ->
|
||||||
|
{:cont, {:ok, Map.put(record, attr.name, value)}}
|
||||||
|
|
||||||
|
{:error, error} ->
|
||||||
|
{:halt, {:error, error}}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_field(entity, attr, resource) do
|
||||||
|
raw_value = get_raw_value(entity, attr, resource)
|
||||||
|
|
||||||
|
case Ash.Type.cast_stored(attr.type, raw_value, attr.constraints) do
|
||||||
|
{:ok, value} ->
|
||||||
|
{:ok, value}
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
{:error,
|
||||||
|
AshJsonApiWrapper.Errors.InvalidData.exception(field: attr.name, value: raw_value)}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_raw_value(entity, attr, resource) do
|
||||||
|
case Enum.find(AshJsonApiWrapper.fields(resource), &(&1.name == attr.name)) do
|
||||||
|
%{path: path} when not is_nil(path) ->
|
||||||
|
case ExJSONPath.eval(entity, path) do
|
||||||
|
{:ok, [value | _]} ->
|
||||||
|
value
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
Map.get(entity, to_string(attr.name))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_entities(body, endpoint) do
|
||||||
|
case endpoint.entity_path do
|
||||||
|
nil ->
|
||||||
|
{:ok, List.wrap(body)}
|
||||||
|
|
||||||
|
path ->
|
||||||
|
case ExJSONPath.eval(body, path) do
|
||||||
|
{:ok, [entities | _]} ->
|
||||||
|
{:ok, List.wrap(entities)}
|
||||||
|
|
||||||
|
{:ok, _} ->
|
||||||
|
{:ok, []}
|
||||||
|
|
||||||
|
{:error, error} ->
|
||||||
|
{:error, error}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
41
lib/endpoint.ex
Normal file
41
lib/endpoint.ex
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
defmodule AshJsonApiWrapper.Endpoint do
|
||||||
|
defstruct [:action, :path, :entity_path, :fields, :fields_in, :write_entity_path]
|
||||||
|
|
||||||
|
@type t :: %__MODULE__{}
|
||||||
|
|
||||||
|
def schema do
|
||||||
|
[
|
||||||
|
action: [
|
||||||
|
type: :atom,
|
||||||
|
required: true,
|
||||||
|
doc: "The action this path is for"
|
||||||
|
],
|
||||||
|
path: [
|
||||||
|
type: :string,
|
||||||
|
default: "/",
|
||||||
|
doc: "The path of the endpoint relative to the base, or an absolute path"
|
||||||
|
],
|
||||||
|
fields_in: [
|
||||||
|
type: {:in, [:body, :params]},
|
||||||
|
default: :body,
|
||||||
|
doc: "Where to place the fields when writing them."
|
||||||
|
],
|
||||||
|
write_entity_path: [
|
||||||
|
type: {:list, :string},
|
||||||
|
doc:
|
||||||
|
"The list path at which the entity should be placed in the body when creating/updating."
|
||||||
|
],
|
||||||
|
entity_path: [
|
||||||
|
type: :string,
|
||||||
|
doc: "A json path at which the entities can be read back from the response"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
def default(path) do
|
||||||
|
%__MODULE__{
|
||||||
|
path: path,
|
||||||
|
fields_in: :body
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
19
lib/errors/invalid_data.ex
Normal file
19
lib/errors/invalid_data.ex
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
defmodule AshJsonApiWrapper.Errors.InvalidData do
|
||||||
|
@moduledoc "Used when an invalid value is present in the response for a given attribute"
|
||||||
|
use Ash.Error.Exception
|
||||||
|
|
||||||
|
def_ash_error([:field, :value], class: :invalid)
|
||||||
|
|
||||||
|
defimpl Ash.ErrorKind do
|
||||||
|
def id(_), do: Ash.UUID.generate()
|
||||||
|
|
||||||
|
def code(_), do: "invalid_data"
|
||||||
|
|
||||||
|
def message(error) do
|
||||||
|
"Invalid value provided#{for_field(error)}: #{inspect(error.value)}"
|
||||||
|
end
|
||||||
|
|
||||||
|
defp for_field(%{field: field}) when not is_nil(field), do: " for #{field}"
|
||||||
|
defp for_field(_), do: ""
|
||||||
|
end
|
||||||
|
end
|
23
lib/field.ex
Normal file
23
lib/field.ex
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
defmodule AshJsonApiWrapper.Field do
|
||||||
|
defstruct [:name, :path, :write_path]
|
||||||
|
|
||||||
|
@type t :: %__MODULE__{}
|
||||||
|
|
||||||
|
def schema do
|
||||||
|
[
|
||||||
|
name: [
|
||||||
|
type: :atom,
|
||||||
|
required: true,
|
||||||
|
doc: "The attribute this field is configuring"
|
||||||
|
],
|
||||||
|
path: [
|
||||||
|
type: :string,
|
||||||
|
doc: "The path of the value for this field, relative to the entity's path"
|
||||||
|
],
|
||||||
|
write_path: [
|
||||||
|
type: {:list, :string},
|
||||||
|
doc: "The list path of the value for this field when writing."
|
||||||
|
]
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
26
mix.exs
26
mix.exs
|
@ -1,11 +1,12 @@
|
||||||
defmodule AshApiWrapper.MixProject do
|
defmodule AshJsonApiWrapper.MixProject do
|
||||||
use Mix.Project
|
use Mix.Project
|
||||||
|
|
||||||
def project do
|
def project do
|
||||||
[
|
[
|
||||||
app: :ash_api_wrapper,
|
app: :ash_json_api_wrapper,
|
||||||
version: "0.1.0",
|
version: "0.1.0",
|
||||||
elixir: "~> 1.12",
|
elixir: "~> 1.12",
|
||||||
|
aliases: aliases(),
|
||||||
start_permanent: Mix.env() == :prod,
|
start_permanent: Mix.env() == :prod,
|
||||||
deps: deps()
|
deps: deps()
|
||||||
]
|
]
|
||||||
|
@ -18,11 +19,28 @@ defmodule AshApiWrapper.MixProject do
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp aliases do
|
||||||
|
[
|
||||||
|
"ash.formatter": "ash.formatter --extensions AshJsonApiWrapper.DataLayer"
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
# Run "mix help deps" to learn about dependencies.
|
# Run "mix help deps" to learn about dependencies.
|
||||||
defp deps do
|
defp deps do
|
||||||
[
|
[
|
||||||
# {:dep_from_hexpm, "~> 0.3.0"},
|
{:ash, "~> 1.48.0-rc.23"},
|
||||||
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
|
{:finch, "~> 0.9.0"},
|
||||||
|
{:exjsonpath, "~> 0.1"},
|
||||||
|
# Dev/Test dependencies
|
||||||
|
{:ex_doc, "~> 0.22", only: :dev, runtime: false},
|
||||||
|
{:ex_check, "~> 0.12.0", only: :dev},
|
||||||
|
{:credo, ">= 0.0.0", only: :dev, runtime: false},
|
||||||
|
{:dialyxir, ">= 0.0.0", only: :dev, runtime: false},
|
||||||
|
{:sobelow, ">= 0.0.0", only: :dev, runtime: false},
|
||||||
|
{:git_ops, "~> 2.4.4", only: :dev},
|
||||||
|
{:excoveralls, "~> 0.13.0", only: [:dev, :test]},
|
||||||
|
{:mix_test_watch, "~> 1.0", only: :dev, runtime: false},
|
||||||
|
{:parse_trans, "3.3.0", only: [:dev, :test], override: true}
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
47
mix.lock
Normal file
47
mix.lock
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
%{
|
||||||
|
"ash": {:hex, :ash, "1.48.0-rc.23", "0a41e4ad62dff764bb8ea9b4645f2f9539a596587910d1b1919f3d25a30f35e9", [:mix], [{:comparable, "~> 1.0", [hex: :comparable, repo: "hexpm", optional: false]}, {:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8.0", [hex: :ets, repo: "hexpm", optional: false]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.3.5", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: false]}, {:timex, ">= 3.0.0", [hex: :timex, repo: "hexpm", optional: false]}], "hexpm", "9cce85c4a2bcc91aaaee3b1e5d39f1218bc608974792caefe27cf56d6532cfd1"},
|
||||||
|
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"},
|
||||||
|
"castore": {:hex, :castore, "0.1.12", "b5755d7668668a74c0e3c4c68df91da927e063a5cade17d693eff04e6ab64805", [:mix], [], "hexpm", "981c79528f88ec4ffd627214ad4cdd25052dc56c002996c603011ae37ec1b4b0"},
|
||||||
|
"certifi": {:hex, :certifi, "2.8.0", "d4fb0a6bb20b7c9c3643e22507e42f356ac090a1dcea9ab99e27e0376d695eba", [:rebar3], [], "hexpm", "6ac7efc1c6f8600b08d625292d4bbf584e14847ce1b6b5c44d983d273e1097ea"},
|
||||||
|
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
|
||||||
|
"comparable": {:hex, :comparable, "1.0.0", "bb669e91cedd14ae9937053e5bcbc3c52bb2f22422611f43b6e38367d94a495f", [:mix], [{:typable, "~> 0.1", [hex: :typable, repo: "hexpm", optional: false]}], "hexpm", "277c11eeb1cd726e7cd41c6c199e7e52fa16ee6830b45ad4cdc62e51f62eb60c"},
|
||||||
|
"credo": {:hex, :credo, "1.5.6", "e04cc0fdc236fefbb578e0c04bd01a471081616e741d386909e527ac146016c6", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "4b52a3e558bd64e30de62a648518a5ea2b6e3e5d2b164ef5296244753fc7eb17"},
|
||||||
|
"decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"},
|
||||||
|
"dialyxir": {:hex, :dialyxir, "1.1.0", "c5aab0d6e71e5522e77beff7ba9e08f8e02bad90dfbeffae60eaf0cb47e29488", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "07ea8e49c45f15264ebe6d5b93799d4dd56a44036cf42d0ad9c960bc266c0b9a"},
|
||||||
|
"earmark_parser": {:hex, :earmark_parser, "1.4.16", "607709303e1d4e3e02f1444df0c821529af1c03b8578dfc81bb9cf64553d02b9", [:mix], [], "hexpm", "69fcf696168f5a274dd012e3e305027010658b2d1630cef68421d6baaeaccead"},
|
||||||
|
"ecto": {:hex, :ecto, "3.7.1", "a20598862351b29f80f285b21ec5297da1181c0442687f9b8329f0445d228892", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d36e5b39fc479e654cffd4dbe1865d9716e4a9b6311faff799b6f90ab81b8638"},
|
||||||
|
"elixir_make": {:hex, :elixir_make, "0.6.3", "bc07d53221216838d79e03a8019d0839786703129599e9619f4ab74c8c096eac", [:mix], [], "hexpm", "f5cbd651c5678bcaabdbb7857658ee106b12509cd976c2c2fca99688e1daf716"},
|
||||||
|
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
|
||||||
|
"ets": {:hex, :ets, "0.8.1", "8ff9bcda5682b98493f8878fc9dbd990e48d566cba8cce59f7c2a78130da29ea", [:mix], [], "hexpm", "6be41b50adb5bc5c43626f25ea2d0af1f4a242fb3fad8d53f0c67c20b78915cc"},
|
||||||
|
"ex_check": {:hex, :ex_check, "0.12.0", "c0e2919ecc06afeaf62c52d64f3d91bd4bc7dd8deaac5f84becb6278888c967a", [:mix], [], "hexpm", "cfafa8ef97c2596d45a1f19b5794cb5c7f700f25d164d3c9f8d7ec17ee67cf42"},
|
||||||
|
"ex_doc": {:hex, :ex_doc, "0.25.5", "ac3c5425a80b4b7c4dfecdf51fa9c23a44877124dd8ca34ee45ff608b1c6deb9", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "688cfa538cdc146bc4291607764a7f1fcfa4cce8009ecd62de03b27197528350"},
|
||||||
|
"excoveralls": {:hex, :excoveralls, "0.13.4", "7b0baee01fe150ef81153e6ffc0fc68214737f54570dc257b3ca4da8e419b812", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "faae00b3eee35cdf0342c10b669a7c91f942728217d2a7c7f644b24d391e6190"},
|
||||||
|
"exjsonpath": {:hex, :exjsonpath, "0.9.0", "87e593eb0deb53aa0688ca9f9edc9fb3456aca83c82245f83201ea04d696feba", [:mix], [], "hexpm", "8d7a8e9ba784e1f7a67c6f1074a3ac91a3a79a45969514ee5d95cea5bf749627"},
|
||||||
|
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
|
||||||
|
"finch": {:hex, :finch, "0.9.0", "8b772324aebafcaba763f1dffaa3e7f52f8c4e52485f50f48bbb2f42219a2e87", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.3.5", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a93bfcad9ca50fa3cb2d459f27667d9a87cfbb7fecf9b29b2e78a50bc2ab445d"},
|
||||||
|
"gettext": {:hex, :gettext, "0.18.2", "7df3ea191bb56c0309c00a783334b288d08a879f53a7014341284635850a6e55", [:mix], [], "hexpm", "f9f537b13d4fdd30f3039d33cb80144c3aa1f8d9698e47d7bcbcc8df93b1f5c5"},
|
||||||
|
"git_cli": {:hex, :git_cli, "0.3.0", "a5422f9b95c99483385b976f5d43f7e8233283a47cda13533d7c16131cb14df5", [:mix], [], "hexpm", "78cb952f4c86a41f4d3511f1d3ecb28edb268e3a7df278de2faa1bd4672eaf9b"},
|
||||||
|
"git_ops": {:hex, :git_ops, "2.4.5", "185a724dfde3745edd22f7571d59c47a835cf54ded67e9ccbc951920b7eec4c2", [:mix], [{:git_cli, "~> 0.2", [hex: :git_cli, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "e323a5b01ad53bc8c19c3a444be3e61ed7803ecd2e95530446ae9327d0143ecc"},
|
||||||
|
"hackney": {:hex, :hackney, "1.18.0", "c4443d960bb9fba6d01161d01cd81173089686717d9490e5d3606644c48d121f", [:rebar3], [{:certifi, "~>2.8.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "9afcda620704d720db8c6a3123e9848d09c87586dc1c10479c42627b905b5c5e"},
|
||||||
|
"idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
|
||||||
|
"jason": {:hex, :jason, "1.2.2", "ba43e3f2709fd1aa1dce90aaabfd039d000469c05c56f0b8e31978e03fa39052", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "18a228f5f0058ee183f29f9eae0805c6e59d61c3b006760668d8d18ff0d12179"},
|
||||||
|
"makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"},
|
||||||
|
"makeup_elixir": {:hex, :makeup_elixir, "0.15.2", "dc72dfe17eb240552857465cc00cce390960d9a0c055c4ccd38b70629227e97c", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "fd23ae48d09b32eff49d4ced2b43c9f086d402ee4fd4fcb2d7fad97fa8823e75"},
|
||||||
|
"makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"},
|
||||||
|
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
|
||||||
|
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
|
||||||
|
"mint": {:hex, :mint, "1.4.0", "cd7d2451b201fc8e4a8fd86257fb3878d9e3752899eb67b0c5b25b180bde1212", [:mix], [{:castore, "~> 0.1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "10a99e144b815cbf8522dccbc8199d15802440fc7a64d67b6853adb6fa170217"},
|
||||||
|
"mix_test_watch": {:hex, :mix_test_watch, "1.1.0", "330bb91c8ed271fe408c42d07e0773340a7938d8a0d281d57a14243eae9dc8c3", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "52b6b1c476cbb70fd899ca5394506482f12e5f6b0d6acff9df95c7f1e0812ec3"},
|
||||||
|
"nimble_options": {:hex, :nimble_options, "0.3.7", "1e52dd7673d36138b1a5dede183b5d86dff175dc46d104a8e98e396b85b04670", [:mix], [], "hexpm", "2086907e6665c6b6579be54ef5001928df5231f355f71ed258f80a55e9f63633"},
|
||||||
|
"nimble_parsec": {:hex, :nimble_parsec, "1.1.0", "3a6fca1550363552e54c216debb6a9e95bd8d32348938e13de5eda962c0d7f89", [:mix], [], "hexpm", "08eb32d66b706e913ff748f11694b17981c0b04a33ef470e33e11b3d3ac8f54b"},
|
||||||
|
"nimble_pool": {:hex, :nimble_pool, "0.2.4", "1db8e9f8a53d967d595e0b32a17030cdb6c0dc4a451b8ac787bf601d3f7704c3", [:mix], [], "hexpm", "367e8071e137b787764e6a9992ccb57b276dc2282535f767a07d881951ebeac6"},
|
||||||
|
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"},
|
||||||
|
"picosat_elixir": {:hex, :picosat_elixir, "0.2.1", "407dcb90755167fd9e3311b60565ff32ed0d234010363406c07cdb4175b95bc5", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "68f4bdb2ac3b594209e54625d3d58c9e2e98b90f2ec8e03235f66e88c9eda5fe"},
|
||||||
|
"sobelow": {:hex, :sobelow, "0.11.1", "23438964486f8112b41e743bbfd402da3e5b296fdc9eacab29914b79c48916dd", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "9897363a7eff96f4809304a90aad819e2ad5e5d24db547af502885146746a53c"},
|
||||||
|
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
|
||||||
|
"telemetry": {:hex, :telemetry, "1.0.0", "0f453a102cdf13d506b7c0ab158324c337c41f1cc7548f0bc0e130bbf0ae9452", [:rebar3], [], "hexpm", "73bc09fa59b4a0284efb4624335583c528e07ec9ae76aca96ea0673850aec57a"},
|
||||||
|
"timex": {:hex, :timex, "3.7.6", "502d2347ec550e77fdf419bc12d15bdccd31266bb7d925b30bf478268098282f", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "a296327f79cb1ec795b896698c56e662ed7210cc9eb31f0ab365eb3a62e2c589"},
|
||||||
|
"typable": {:hex, :typable, "0.3.0", "0431e121d124cd26f312123e313d2689b9a5322b15add65d424c07779eaa3ca1", [:mix], [], "hexpm", "880a0797752da1a4c508ac48f94711e04c86156f498065a83d160eef945858f8"},
|
||||||
|
"tzdata": {:hex, :tzdata, "1.1.0", "72f5babaa9390d0f131465c8702fa76da0919e37ba32baa90d93c583301a8359", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "18f453739b48d3dc5bcf0e8906d2dc112bb40baafe2c707596d89f3c8dd14034"},
|
||||||
|
"unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"},
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
defmodule AshApiWrapperTest do
|
defmodule AshJsonApiWrapperTest do
|
||||||
use ExUnit.Case
|
use ExUnit.Case
|
||||||
doctest AshApiWrapper
|
doctest AshJsonApiWrapper
|
||||||
|
|
||||||
test "greets the world" do
|
test "greets the world" do
|
||||||
assert AshApiWrapper.hello() == :world
|
assert AshJsonApiWrapper.hello() == :world
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue