Alembic v4.0.0 Alembic.ResourceLinkage View Source
Resource linkage in a compound document allows a client to link together all of the included resource objects without having to
GET
any URLs via links.Resource linkage MUST be represented as one of the following:
null
for empty to-one relationships.- an empty array (
[]
) for empty to-many relationships.- a single resource identifier object for non-empty to-one relationships.
- an array of resource identifier objects for non-empty to-many relationships.
— JSON API - Document Structure - Resource Objects - Resource Linkage
Link to this section Summary
Functions
Converts a resource linkage to one or more Alembic.ResoureIdentifier.t
Converts resource linkage to params format used by
Ecto.Changeset.cast/4
Unlike to_params/2
, if type
and id
of convertable
already exists in converted_by_id_by_type
, then the params
returned are only %{ "id" => id }
without any further expansion, that is, a resource identifier, so that loops are
prevented
Link to this section Functions
from_json(nil, Alembic.Error.t()) :: {:ok, nil}
from_json([], Alembic.Error.t()) :: {:ok, []}
from_json(Alembic.json_object(), Alembic.Error.t()) :: {:ok, Alembic.Resource.t() | Alembic.ResourceIdentifier.t()} | Alembic.FromJson.error()
from_json([Alembic.json_object(), ...], Alembic.Error.t()) :: {:ok, [Alembic.Resource.t() | Alembic.ResourceIdentifier.t()]} | Alembic.FromJson.error()
from_json(true | false | float() | integer(), Alembic.Error.t()) :: Alembic.FromJson.error()
Converts a resource linkage to one or more Alembic.ResoureIdentifier.t
.
To-one
A to-one resource linkage, when present, can be a single Alembic.Resource.t
or
Alembic.ResourceIdentifier.t
A JSON object is assumed to be an resource object if it has "attributes"
iex> Alembic.ResourceLinkage.from_json(
...> %{
...> "attributes" => %{
...> "name" => "Alice"
...> },
...> "id" => "1",
...> "type" => "author"
...> },
...> %Alembic.Error{
...> meta: %{
...> "action" => :create,
...> "sender" => :client
...> },
...> source: %Alembic.Source{
...> pointer: "/data/relationships/author/data"
...> }
...> }
...> )
{
:ok,
%Alembic.Resource{
attributes: %{
"name" => "Alice"
},
id: "1",
type: "author"
}
}
Or if the JSON object has "relationships"
iex> Alembic.ResourceLinkage.from_json(
...> %{
...> "id" => "1",
...> "relationships" => %{
...> "author" => %{
...> "data" => %{
...> "id" => "1",
...> "type" => "author"
...> }
...> }
...> },
...> "type" => "post"
...> },
...> %Alembic.Error{
...> meta: %{
...> "action" => :create,
...> "sender" => :client
...> },
...> source: %Alembic.Source{
...> pointer: "/data"
...> }
...> }
...> )
{
:ok,
%Alembic.Resource{
id: "1",
relationships: %{
"author" => %Alembic.Relationship{
data: %Alembic.ResourceIdentifier{
id: "1",
meta: nil,
type: "author"
}
}
},
type: "post"
}
}
If neither "attributes"
nor "relationships"
is present, then JSON object is assumed to be an
Alembic.ResourceIdentifier.t
.
iex> Alembic.ResourceLinkage.from_json(
...> %{
...> "id" => "1",
...> "type" => "author"
...> },
...> %Alembic.Error{
...> source: %Alembic.Source{
...> pointer: "/data/relationships/author/data"
...> }
...> }
...> )
{
:ok,
%Alembic.ResourceIdentifier{
id: "1",
type: "author"
}
}
An empty to-one resource linkage can be signified with nil
, which would have been null
in the original JSON.
iex> Alembic.ResourceLinkage.from_json(
...> nil,
...> %Alembic.Error{
...> source: %Alembic.Source{
...> pointer: "/data/relationships/author/data"
...> }
...> }
...> )
{:ok, nil}
To-many
A to-many resource linkage, when present, can be a list of Alembic.Resource.t
.
iex> Alembic.ResourceLinkage.from_json(
...> [
...> %{
...> "attributes" => %{
...> "text" => "First Post!"
...> },
...> "id" => "1",
...> "relationships" => %{
...> "comments" => %{
...> "data" => [
...> %{
...> "id" => "1",
...> "type" => "comment"
...> }
...> ]
...> }
...> },
...> "type" => "post"
...> }
...> ],
...> %Alembic.Error{
...> meta: %{
...> "action" => :create,
...> "sender" => :client
...> },
...> source: %Alembic.Source{
...> pointer: "/data"
...> }
...> }
...> )
{
:ok,
[
%Alembic.Resource{
attributes: %{
"text" => "First Post!"
},
id: "1",
links: nil,
relationships: %{
"comments" => %Alembic.Relationship{
data: [
%Alembic.ResourceIdentifier{
id: "1",
type: "comment"
}
]
}
},
type: "post"
}
]
}
Or a list of Alembic.ResourceIdentifier.t
.
iex> Alembic.ResourceLinkage.from_json(
...> [
...> %{
...> "id" => "1",
...> "type" => "post"
...> }
...> ],
...> %Alembic.Error{
...> source: %Alembic.Source{
...> pointer: "/data"
...> }
...> }
...> )
{
:ok,
[
%Alembic.ResourceIdentifier{
id: "1",
type: "post"
}
]
}
A mix of resources and resource identifiers is an error
iex> Alembic.ResourceLinkage.from_json(
...> [
...> %{
...> "attributes" => %{
...> "text" => "First Post!"
...> },
...> "id" => "1",
...> "type" => "post"
...> },
...> %{
...> "id" => "2",
...> "type" => "post"
...> }
...> ],
...> %Alembic.Error{
...> meta: %{
...> "action" => :create,
...> "sender" => :client
...> },
...> source: %Alembic.Source{
...> pointer: "/data"
...> }
...> }
...> )
{
:error,
%Alembic.Document{
errors: [
%Alembic.Error{
detail: "`/data` type is not resource linkage",
meta: %{
"type" => "resource linkage"
},
source: %Alembic.Source{
pointer: "/data"
},
status: "422",
title: "Type is wrong"
}
]
}
}
An empty to-many resource linkage can be signified with []
.
iex> Alembic.ResourceLinkage.from_json(
...> [],
...> %Alembic.Error{
...> source: %Alembic.Source{
...> pointer: "/data/relationships/comments/data"
...> }
...> }
...> )
{:ok, []}
Invalid
If the json
isn’t any of the above, valid formats, then a type error will be returned
iex> Alembic.ResourceLinkage.from_json(
...> "that resource over there",
...> %Alembic.Error{
...> source: %Alembic.Source{
...> pointer: "/data/relationships/resource/data"
...> }
...> }
...> )
{
:error,
%Alembic.Document{
errors: [
%Alembic.Error{
detail: "`/data/relationships/resource/data` type is not resource linkage",
meta: %{
"type" => "resource linkage"
},
source: %Alembic.Source{
pointer: "/data/relationships/resource/data"
},
status: "422",
title: "Type is wrong"
}
]
}
}
to_params( [Alembic.Resource.t() | Alembic.ResourceIdentifier.t()] | Alembic.Resource.t() | Alembic.ResourceIdentifier.t() | nil, Alembic.ToParams.resource_by_id_by_type() ) :: Alembic.ToParams.params()
Converts resource linkage to params format used by
Ecto.Changeset.cast/4
.
To-one
An empty to-one, nil
, is nil
when converted to params.
iex> Alembic.ResourceLinkage.to_params(nil, %{})
nil
A resource identifier uses resource_by_id_by_type
to fill in the attributes of the referenced resource. type
is
dropped as Ecto.Changeset.cast/4
doesn’t verify types in the
params.
iex> Alembic.ResourceLinkage.to_params(
...> %Alembic.ResourceIdentifier{
...> type: "shirt",
...> id: "1"
...> },
...> %{
...> "shirt" => %{
...> "1" => %Alembic.Resource{
...> type: "shirt",
...> id: "1",
...> attributes: %{
...> "size" => "L"
...> }
...> }
...> }
...> }
...> )
%{
"id" => "1",
"size" => "L"
}
On create or update, a relationship can be created by having an Alembic.Resource.t
, in which case the
attributes are supplied by the Alembic.Resource.t
, instead of resource_by_id_by_type
.
iex> Alembic.ResourceLinkage.to_params(
...> %Alembic.Resource{
...> attributes: %{
...> "size" => "L"
...> },
...> type: "shirt"
...> },
...> %{}
...> )
%{
"size" => "L"
}
To-many
An empty to-many, []
, is []
when converted to params
iex> Alembic.ResourceLinkage.to_params([], %{})
[]
A list of resource identifiers uses attributes_by_id_by_type
to fill in the attributes of the referenced resources.
type
is dropped as Ecto.Changeset.cast/4
doesn’t verify types
in the params.
iex> Alembic.ResourceLinkage.to_params(
...> [
...> %Alembic.ResourceIdentifier{
...> type: "shirt",
...> id: "1"
...> }
...> ],
...> %{
...> "shirt" => %{
...> "1" => %Alembic.Resource{
...> type: "shirt",
...> id: "1",
...> attributes: %{
...> "size" => "L"
...> }
...> }
...> }
...> }
...> )
[
%{
"id" => "1",
"size" => "L"
}
]
On create or update, a relationship can be created by having an Alembic.Resource
, in which case the
attributes are supplied by the Alembic.Resource
, instead of attributes_by_id_by_type
.
iex> Alembic.ResourceLinkage.to_params(
...> [
...> %Alembic.Resource{
...> attributes: %{
...> "size" => "L"
...> },
...> type: "shirt"
...> }
...> ],
...> %{}
...> )
[
%{
"size" => "L"
}
]
to_params( [Alembic.Resource.t() | Alembic.ResourceIdentifier.t()] | Alembic.Resource.t() | Alembic.ResourceIdentifier.t() | nil, Alembic.ToParams.resource_by_id_by_type(), Alembic.ToParams.converted_by_id_by_type() ) :: Alembic.ToParams.params()
Unlike to_params/2
, if type
and id
of convertable
already exists in converted_by_id_by_type
, then the params
returned are only %{ "id" => id }
without any further expansion, that is, a resource identifier, so that loops are
prevented.
Parameters
convertable
- anAlembic.Document.t
hierarchy data structureresources_by_id_by_type
- A nest map with the outer layer keyed by theAlembic.Resource.type
, then the next layer keyed by theAlembic.Resource.id
with the values being the fullAlembic.Resource.t
fromAlembic.Document.t
included
.converted_by_id_by_type
- Tracks which (type, id) have been converted already to prevent infinite recursion when expanding indirect relationships.
Returns
Success
{nil}
if an empty singleton%{}
- if a non-empty singleton[]
- if an empty collection[%{}]
- if a non-empty collection
Errors
{:error, :already_converted}
- if thetype
andid
ofconvertable
already exists inconverted_by_id_by_type
{:error, :unset}
- if theconvertable
data is not set
Callback implementation for Alembic.ToParams.to_params/3
.