add relationship route for has one

This commit is contained in:
Zach Daniel 2019-10-04 04:30:25 -04:00
parent 39d6106a9c
commit d250271cd1
No known key found for this signature in database
GPG key ID: A57053A671EE649E
10 changed files with 148 additions and 8 deletions

View file

@ -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
]

View 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

View 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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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"

View file

@ -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)

View file

@ -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