mirror of
https://github.com/ash-project/ash.git
synced 2024-09-19 13:03:02 +12:00
add relationship route for has one
This commit is contained in:
parent
39d6106a9c
commit
d250271cd1
10 changed files with 148 additions and 8 deletions
|
@ -5,6 +5,9 @@ locals_without_parens = [
|
|||
post: 1,
|
||||
attribute: 2,
|
||||
belongs_to: 2,
|
||||
belongs_to: 3,
|
||||
has_one: 2,
|
||||
has_one: 3,
|
||||
has_many: 2,
|
||||
field: 2
|
||||
]
|
||||
|
|
33
lib/ash/json_api/controllers/get_belongs_to.ex
Normal file
33
lib/ash/json_api/controllers/get_belongs_to.ex
Normal file
|
@ -0,0 +1,33 @@
|
|||
defmodule Ash.JsonApi.Controllers.GetBelongsTo do
|
||||
def init(options) do
|
||||
# initialize options
|
||||
options
|
||||
end
|
||||
|
||||
def call(%{path_params: %{"id" => id}} = conn, options) do
|
||||
resource = options[:resource]
|
||||
relationship = options[:relationship]
|
||||
|
||||
request = Ash.Request.from(conn, relationship.destination, :get_belongs_to)
|
||||
|
||||
case Ash.Repo.get(resource, id) do
|
||||
nil ->
|
||||
conn
|
||||
# |> put_resp_content_type("text/plain")
|
||||
|> Plug.Conn.send_resp(404, "uh oh")
|
||||
|
||||
found ->
|
||||
related =
|
||||
found
|
||||
|> Ecto.assoc(relationship.name)
|
||||
|> Ash.Repo.one()
|
||||
|
||||
serialized = Ash.JsonApi.Serializer.serialize_one(request, related)
|
||||
|
||||
conn
|
||||
|> Plug.Conn.put_resp_content_type("application/vnd.api+json")
|
||||
|> Plug.Conn.send_resp(200, serialized)
|
||||
end
|
||||
|> Plug.Conn.halt()
|
||||
end
|
||||
end
|
33
lib/ash/json_api/controllers/get_has_one.ex
Normal file
33
lib/ash/json_api/controllers/get_has_one.ex
Normal file
|
@ -0,0 +1,33 @@
|
|||
defmodule Ash.JsonApi.Controllers.GetHasOne do
|
||||
def init(options) do
|
||||
# initialize options
|
||||
options
|
||||
end
|
||||
|
||||
def call(%{path_params: %{"id" => id}} = conn, options) do
|
||||
resource = options[:resource]
|
||||
relationship = options[:relationship]
|
||||
|
||||
request = Ash.Request.from(conn, relationship.destination, :get_has_one)
|
||||
|
||||
case Ash.Repo.get(resource, id) do
|
||||
nil ->
|
||||
conn
|
||||
# |> put_resp_content_type("text/plain")
|
||||
|> Plug.Conn.send_resp(404, "uh oh")
|
||||
|
||||
found ->
|
||||
related =
|
||||
found
|
||||
|> Ecto.assoc(relationship.name)
|
||||
|> Ash.Repo.one()
|
||||
|
||||
serialized = Ash.JsonApi.Serializer.serialize_one(request, related)
|
||||
|
||||
conn
|
||||
|> Plug.Conn.put_resp_content_type("application/vnd.api+json")
|
||||
|> Plug.Conn.send_resp(200, serialized)
|
||||
end
|
||||
|> Plug.Conn.halt()
|
||||
end
|
||||
end
|
|
@ -3,6 +3,32 @@ defmodule Ash.JsonApi.RouteBuilder do
|
|||
quote bind_quoted: [resource: resource] do
|
||||
Ash.JsonApi.RouteBuilder.build_get_route(resource)
|
||||
Ash.JsonApi.RouteBuilder.build_index_route(resource)
|
||||
Ash.JsonApi.RouteBuilder.build_belongs_to_relationship_routes(resource)
|
||||
Ash.JsonApi.RouteBuilder.build_has_one_relationship_routes(resource)
|
||||
end
|
||||
end
|
||||
|
||||
defmacro build_belongs_to_relationship_routes(resource) do
|
||||
quote bind_quoted: [resource: resource] do
|
||||
for %{expose?: true, type: :belongs_to, route: route} = relationship <-
|
||||
Ash.relationships(resource) do
|
||||
get(route,
|
||||
to: Ash.JsonApi.Controllers.GetBelongsTo,
|
||||
init_opts: [resource: resource, relationship: relationship]
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defmacro build_has_one_relationship_routes(resource) do
|
||||
quote bind_quoted: [resource: resource] do
|
||||
for %{expose?: true, type: :has_one, route: route} = relationship <-
|
||||
Ash.relationships(resource) do
|
||||
get(route,
|
||||
to: Ash.JsonApi.Controllers.GetHasOne,
|
||||
init_opts: [resource: resource, relationship: relationship]
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -9,8 +9,16 @@ defmodule Ash.JsonApi.Serializer do
|
|||
Jason.encode!(%{data: data, json_api: json_api, links: links})
|
||||
end
|
||||
|
||||
def serialize_one(request, nil) do
|
||||
# TODO `included`
|
||||
json_api = %{version: "1.0"}
|
||||
links = one_links(request)
|
||||
|
||||
Jason.encode!(%{data: nil, json_api: json_api, links: links})
|
||||
end
|
||||
|
||||
def serialize_one(request, record) do
|
||||
# TODO `links` and `included`
|
||||
# TODO `included`
|
||||
data = serialize_one_record(request, record)
|
||||
json_api = %{version: "1.0"}
|
||||
links = one_links(request)
|
||||
|
@ -135,20 +143,23 @@ defmodule Ash.JsonApi.Serializer do
|
|||
id: record.id,
|
||||
type: Ash.type(resource),
|
||||
attributes: serialize_attributes(resource, record),
|
||||
relationships: serialize_relationships(resource, record),
|
||||
relationships: serialize_relationships(request, record),
|
||||
links: %{
|
||||
self: at_host(request, Ash.Routes.get(resource, record.id))
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp serialize_relationships(resource, _record) do
|
||||
defp serialize_relationships(request, record) do
|
||||
# TODO: links.self, links.related
|
||||
resource
|
||||
request.resource
|
||||
|> Ash.relationships()
|
||||
|> Enum.filter(& &1.expose?)
|
||||
|> Enum.into(%{}, fn relationship ->
|
||||
value = %{
|
||||
links: %{},
|
||||
links: %{
|
||||
self: at_host(with_path_params(request, %{"id" => record.id}), relationship.route)
|
||||
},
|
||||
data: %{},
|
||||
meta: %{}
|
||||
}
|
||||
|
@ -157,11 +168,28 @@ defmodule Ash.JsonApi.Serializer do
|
|||
end)
|
||||
end
|
||||
|
||||
defp with_path_params(request, params) do
|
||||
Map.update!(request, :path_params, &Map.merge(&1, params))
|
||||
end
|
||||
|
||||
defp at_host(request, route) do
|
||||
request.url
|
||||
|> URI.parse()
|
||||
|> Map.put(:query, nil)
|
||||
|> Map.put(:path, "/" <> Path.join(request.json_api_prefix, route))
|
||||
|> Map.update!(:path, fn path ->
|
||||
path
|
||||
|> Path.split()
|
||||
|> Enum.map(fn path_element ->
|
||||
if String.starts_with?(path_element, ":") do
|
||||
"replacing #{path_element}"
|
||||
Map.get(request.path_params, String.slice(path_element, 1..-1)) || ""
|
||||
else
|
||||
path_element
|
||||
end
|
||||
end)
|
||||
|> Path.join()
|
||||
end)
|
||||
|> URI.to_string()
|
||||
end
|
||||
|
||||
|
|
|
@ -20,6 +20,14 @@ defmodule Ash.Resource do
|
|||
|
||||
@name name
|
||||
@resource_type resource_type
|
||||
|
||||
unless @name do
|
||||
raise "Must set name"
|
||||
end
|
||||
|
||||
unless @resource_type do
|
||||
raise "Must set resource type"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
defmodule Ash.Resource.Relationships.BelongsTo do
|
||||
defstruct [:name, :type, :destination, :destination_field, :source_field]
|
||||
defstruct [:name, :expose?, :type, :route, :destination, :destination_field, :source_field]
|
||||
|
||||
def new(name, related_resource, opts \\ []) do
|
||||
def new(resource_name, name, related_resource, opts \\ []) do
|
||||
%__MODULE__{
|
||||
name: name,
|
||||
type: :belongs_to,
|
||||
expose?: opts[:expose?] || false,
|
||||
route: resource_name <> "/:id/" <> to_string(name),
|
||||
destination: related_resource,
|
||||
destination_field: opts[:destination_field] || "id",
|
||||
source_field: opts[:source_field] || "#{name}_id"
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
defmodule Ash.Resource.Relationships.HasOne do
|
||||
defstruct [:name, :type, :destination, :destination_field, :source_field]
|
||||
defstruct [:name, :type, :expose?, :route, :destination, :destination_field, :source_field]
|
||||
|
||||
def new(resource_name, name, related_resource, opts \\ []) do
|
||||
%__MODULE__{
|
||||
name: name,
|
||||
type: :has_one,
|
||||
expose?: opts[:expose?] || false,
|
||||
route: resource_name <> "/:id/" <> to_string(name),
|
||||
destination: related_resource,
|
||||
destination_field: opts[:destination_field] || "#{resource_name}_id",
|
||||
source_field: opts[:source_field] || "id"
|
||||
|
|
|
@ -21,6 +21,7 @@ defmodule Ash.Resource.Relationships do
|
|||
defmacro belongs_to(relationship_name, resource, config \\ []) do
|
||||
quote do
|
||||
@relationships Ash.Resource.Relationships.BelongsTo.new(
|
||||
@name,
|
||||
unquote(relationship_name),
|
||||
unquote(resource),
|
||||
unquote(config)
|
||||
|
|
|
@ -15,6 +15,10 @@ defmodule Ash.Resource.Schema do
|
|||
for relationship <- Enum.filter(@relationships, &(&1.type == :belongs_to)) do
|
||||
belongs_to relationship.name, relationship.destination
|
||||
end
|
||||
|
||||
for relationship <- Enum.filter(@relationships, &(&1.type == :has_one)) do
|
||||
has_one relationship.name, relationship.destination
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue