Alembic v4.0.0 Alembic.Relationships View Source
The value of the
relationships
key MUST be an object (a “relationships object”). Members of the relationships object (“relationships”) represent references from the resource object in which it’s defined to other resource objects.Relationships may be to-one or to-many.
— JSON API - Document Structure - Resource Objects - Relationships
Link to this section Summary
Functions
Validates that the given json
follows the spec for
“relationship”
Converts relationships to params format used by
Ecto.Changeset.cast/4
that can be merged with the params
from the primary data
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 Types
Maps String.t
name to relationship
Link to this section Functions
from_json(Alembic.json_object(), Alembic.Error.t()) :: {:ok, map()} | Alembic.FromJson.error()
from_json(nil, Alembic.Error.t()) :: {:ok, nil}
from_json( true | false | list() | float() | integer() | String.t(), Alembic.Error.t() ) :: Alembic.FromJson.error()
Validates that the given json
follows the spec for
“relationship”.
"relationships"
is optional, so it can be nil.
iex> Alembic.Relationships.from_json(
...> nil,
...> %Alembic.Error{
...> source: %Alembic.Source{
...> pointer: "/data/relationships"
...> }
...> }
...> )
{:ok, nil}
Relationships
Members of the relationships object (“relationships”) represent references from the resource object in which it’s defined to other resource objects.
— JSON API - Document Structure - Resource Objects - Relationships
iex> Alembic.Relationships.from_json(
...> %{"shirt" => []},
...> %Alembic.Error{
...> source: %Alembic.Source{
...> pointer: "/data/relationships"
...> }
...> }
...> )
{
:error,
%Alembic.Document{
errors: [
%Alembic.Error{
detail: "`/data/relationships/shirt` type is not relationship",
meta: %{
"type" => "relationship"
},
source: %Alembic.Source{
pointer: "/data/relationships/shirt"
},
status: "422",
title: "Type is wrong"
}
]
}
}
Data
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
To-one
A to-one relationship, when present, will be a single Alembic.ResourceIdentifier.t
iex> Alembic.Relationships.from_json(
...> %{
...> "author" => %{
...> "data" => %{
...> "id" => "1",
...> "type" => "author"
...> }
...> }
...> },
...> %Alembic.Error{
...> source: %Alembic.Source{
...> pointer: "/data/relationships"
...> }
...> }
...> )
{
:ok,
%{
"author" => %Alembic.Relationship{
data: %Alembic.ResourceIdentifier{
id: "1",
type: "author"
}
}
}
}
An empty to-one relationship can be signified with nil
, which would have been null
in the original JSON.
iex> Alembic.Relationships.from_json(
...> %{
...> "author" => %{
...> "data" => nil
...> }
...> },
...> %Alembic.Error{
...> source: %Alembic.Source{
...> pointer: "/data/relationships"
...> }
...> }
...> )
{
:ok,
%{
"author" => %Alembic.Relationship{
data: nil
}
}
}
To-many
A to-many relationship, when preent, will be a list of Alembic.ResourceIdentifier.t
iex> Alembic.Relationships.from_json(
...> %{
...> "comments" => %{
...> "data" => [
...> %{
...> "id" => "1",
...> "type" => "comment"
...> }
...> ]
...> }
...> },
...> %Alembic.Error{
...> source: %Alembic.Source{
...> pointer: "/data/relationships"
...> }
...> }
...> )
{
:ok,
%{
"comments" => %Alembic.Relationship{
data: [
%Alembic.ResourceIdentifier{
id: "1",
type: "comment"
}
]
}
}
}
An empty to-many resource linkage can be signified with []
.
iex> Alembic.Relationships.from_json(
...> %{
...> "comments" => %{
...> "data" => []
...> }
...> },
...> %Alembic.Error{
...> source: %Alembic.Source{
...> pointer: "/data/relationships"
...> }
...> }
...> )
{
:ok,
%{
"comments" => %Alembic.Relationship{
data: []
}
}
}
to_params(nil, Alembic.ToParams.resource_by_id_by_type()) :: %{}
to_params(t(), Alembic.ToParams.resource_by_id_by_type()) :: Alembic.ToParams.params()
Converts relationships to params format used by
Ecto.Changeset.cast/4
that can be merged with the params
from the primary data.
No relationships
No relationships are represented as nil
in Alembic.Document.t
, but since the output of
to_params/2
is expected to be Map.merge/2
with the primary resource’s params, an empty map is returned when
relationships is nil
.
iex> Alembic.Relationships.to_params(nil, %{})
%{}
Some Relationships
Relatonship params are expected to be Map.merge/2
with the primary resource’s params, so a relationships are
returned as a map using the original relationship name and each Alembic.Relationship.t
converted with
Alembic.Relationship.to_params/2
.
Resource Identifiers
If the resource linkage for a relationship is an Alembic.ResourceIdentifier.t
, then the attributes for
the resource will be looked up in resource_by_id_by_type
.
iex> Alembic.Relationships.to_params(
...> %{
...> "author" => %Alembic.Relationship{
...> data: %Alembic.ResourceIdentifier{id: "1", type: "author"}
...> }
...> },
...> %{
...> "author" => %{
...> "1" => %Alembic.Resource{
...> type: "author",
...> id: "1",
...> attributes: %{
...> "name" => "Alice"
...> }
...> }
...> }
...> }
...> )
%{
"author" => %{
"id" => "1",
"name" => "Alice"
}
}
Resources are not required to be in resources_by_id_by_type
, as would be the case when only a foreign key is
supplied.
iex> Alembic.Relationships.to_params(
...> %{
...> "author" => %Alembic.Relationship{
...> data: %Alembic.ResourceIdentifier{id: "1", type: "author"}
...> }
...> },
...> %{}
...> )
%{
"author" => %{
"id" => "1"
}
}
Resources
On create or update, the relationships can directly contain Alembic.Resource.t
that are to be created,
in which case the resource_by_id_by_type
are ignored.
iex> Alembic.Relationships.to_params(
...> %{
...> "author" => %Alembic.Relationship{
...> data: %Alembic.Resource{
...> attributes: %{"name" => "Alice"},
...> type: "author"
...> }
...> }
...> },
...> %{}
...> )
%{
"author" => %{
"name" => "Alice"
}
}
Not Included
If a relationship is not included, then no linkage data
may be present and {:error, :unset}
will be returned
from Alembic.Relationshio.to_params/3
. For those relationships, they will not appear in the params
of all relationships.
iex> Alembic.Relationships.to_params(
...> %{
...> "author" => %Alembic.Relationship{
...> data: %Alembic.Resource{
...> attributes: %{"name" => "Alice"},
...> type: "author"
...> }
...> },
...> "comments" => %Alembic.Relationship{
...> links: %{
...> "related" => "https://example.com/api/v1/posts/1/comments"
...> }
...> }
...> },
...> %{}
...> )
%{
"author" => %{
"name" => "Alice"
}
}
to_params( nil, Alembic.ToParams.resource_by_id_by_type(), Alembic.ToParams.converted_by_id_by_type() ) :: %{}
to_params( t(), 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
.