Packages
absinthe
1.7.11
1.11.0
1.10.2
1.10.1
1.10.0
1.9.1
1.9.0
1.8.0
1.7.11
1.7.10
1.7.9
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
1.7.1
1.7.0
1.6.8
1.6.7
retired
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.6.0-rc.1
1.6.0-rc.0
1.5.5
1.5.4
1.5.3
1.5.2
1.5.1
1.5.0
1.5.0-rc.5
1.5.0-rc.4
1.5.0-rc.3
1.5.0-rc.2
1.5.0-rc.1
1.5.0-rc.0
1.5.0-beta.2
1.5.0-beta.1
1.5.0-beta.0
1.5.0-alpha.4
1.5.0-alpha.3
1.5.0-alpha.2
1.5.0-alpha.1
1.5.0-alpha.0
1.4.16
1.4.15
1.4.14
1.4.13
1.4.12
1.4.11
1.4.10
1.4.9
1.4.8
retired
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.4.0-rc.3
1.4.0-rc.2
1.4.0-rc.1
1.4.0-rc.0
1.4.0-beta.5
1.4.0-beta.4
1.4.0-beta.3
1.4.0-beta.2
1.4.0-beta.1
1.3.2
1.3.1
1.3.0
1.3.0-rc.0
1.3.0-beta.2
1.3.0-beta.1
1.3.0-beta.0
1.2.6
1.2.5
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.2.0-rc.0
1.2.0-beta.0
1.2.0-alpha0
1.2.0-alpha.2
1.2.0-alpha.1
1.1.11
1.1.10
1.1.9
1.1.8
1.1.7
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.0
0.5.2
0.5.1
0.5.0
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.2.3
0.2.2
0.2.1
0.1.0
GraphQL for Elixir
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/absinthe/phase/schema/validation/object_must_implement_interfaces.ex
defmodule Absinthe.Phase.Schema.Validation.ObjectMustImplementInterfaces do
use Absinthe.Phase
alias Absinthe.Blueprint
def run(bp, _) do
bp = Blueprint.prewalk(bp, &handle_schemas/1)
{:ok, bp}
end
defp handle_schemas(%Blueprint.Schema.SchemaDefinition{} = schema) do
ifaces =
schema.type_definitions
|> Enum.filter(&match?(%Blueprint.Schema.InterfaceTypeDefinition{}, &1))
|> Map.new(&{&1.identifier, &1})
types =
schema.type_definitions
|> Map.new(&{&1.identifier, &1})
schema = Blueprint.prewalk(schema, &validate_objects(&1, ifaces, types))
{:halt, schema}
end
defp handle_schemas(obj) do
obj
end
@interface_types [
Blueprint.Schema.ObjectTypeDefinition,
Blueprint.Schema.InterfaceTypeDefinition
]
# Do not walk type extension definition as they are allowed to be invalid
defp validate_objects(%Blueprint.Schema.TypeExtensionDefinition{} = extension, _, _) do
{:halt, extension}
end
defp validate_objects(%struct{} = object, ifaces, types) when struct in @interface_types do
Enum.reduce(object.interfaces, object, fn ident, object ->
case Map.fetch(ifaces, ident) do
{:ok, iface} -> validate_object(object, iface, types)
_ -> object
end
end)
end
defp validate_objects(type, _, _) do
type
end
def validate_object(object, iface, types) do
case check_implements(iface, object, types) do
:ok ->
object
{:error, invalid_fields} ->
detail = %{
object: object.identifier,
interface: iface.identifier,
fields: invalid_fields
}
object |> put_error(error(object, detail))
end
end
defp error(object, data) do
%Absinthe.Phase.Error{
message: explanation(data),
locations: [object.__reference__.location],
phase: __MODULE__,
extra: data
}
end
@moduledoc false
@description """
An object type must be a super-set of all interfaces it implements.
* The object type must include a field of the same name for every field
defined in an interface.
* The object field must be of a type which is equal to or a sub-type of
the interface field (covariant).
* An object field type is a valid sub-type if it is equal to (the same
type as) the interface field type.
* An object field type is a valid sub-type if it is an Object type and the
interface field type is either an Interface type or a Union type and the
object field type is a possible type of the interface field type.
* An object field type is a valid sub-type if it is a List type and the
interface field type is also a List type and the list-item type of the
object field type is a valid sub-type of the list-item type of the
interface field type.
* An object field type is a valid sub-type if it is a Non-Null variant of a
valid sub-type of the interface field type.
* The object field must include an argument of the same name for every
argument defined in the interface field.
* The object field argument must accept the same type (invariant) as the
interface field argument.
* The object field may include additional arguments not defined in the
interface field, but any additional argument must not be required.
Reference: https://github.com/facebook/graphql/blob/master/spec/Section%203%20--%20Type%20System.md#object-type-validation
"""
def explanation(%{object: obj, interface: interface, fields: fields}) do
"""
Type "#{obj}" does not fully implement interface type "#{interface}" \
for fields #{inspect(fields)}
#{@description}
"""
end
def check_implements(interface, type, types) do
check_covariant(interface, type, nil, types)
end
defp check_covariant(
%Blueprint.Schema.InterfaceTypeDefinition{fields: ifields},
%{fields: type_fields},
_field_ident,
types
) do
Enum.reduce(ifields, [], fn %{identifier: ifield_ident} = ifield, invalid_fields ->
case Enum.find(type_fields, &(&1.identifier == ifield_ident)) do
nil ->
[ifield_ident | invalid_fields]
field ->
case check_covariant(ifield.type, field.type, ifield_ident, types) do
:ok ->
invalid_fields
{:error, invalid_field} ->
[invalid_field | invalid_fields]
end
end
end)
|> case do
[] ->
:ok
invalid_fields ->
{:error, invalid_fields}
end
end
defp check_covariant(
%Blueprint.Schema.InterfaceTypeDefinition{identifier: interface_ident},
interface_ident,
_field_ident,
_types
) do
:ok
end
defp check_covariant(
%Blueprint.Schema.InterfaceTypeDefinition{identifier: interface_ident},
field_type,
field_ident,
types
) do
%{interfaces: field_type_interfaces} = Map.get(types, field_type)
(interface_ident in field_type_interfaces && :ok) || {:error, field_ident}
end
defp check_covariant(
%wrapper{of_type: inner_type1},
%wrapper{of_type: inner_type2},
field_ident,
types
) do
check_covariant(inner_type1, inner_type2, field_ident, types)
end
defp check_covariant(
itype,
%Absinthe.Blueprint.TypeReference.NonNull{of_type: inner_type},
field_ident,
types
) do
check_covariant(itype, inner_type, field_ident, types)
end
defp check_covariant(%{identifier: identifier}, %{identifier: identifier}, _field_ident, _types) do
:ok
end
defp check_covariant(
%Blueprint.TypeReference.Name{name: name},
%Blueprint.TypeReference.Name{name: name},
_field_ident,
_types
) do
:ok
end
defp check_covariant(
%Blueprint.TypeReference.Name{name: iface_name},
%Blueprint.TypeReference.Name{name: type_name},
field_ident,
types
) do
{_, itype} = Enum.find(types, fn {_, %{name: name}} -> name == iface_name end)
{_, type} = Enum.find(types, fn {_, %{name: name}} -> name == type_name end)
check_covariant(itype, type, field_ident, types)
end
defp check_covariant(nil, _, field_ident, _), do: {:error, field_ident}
defp check_covariant(_, nil, field_ident, _), do: {:error, field_ident}
defp check_covariant(itype, type, field_ident, types) when is_atom(itype) do
itype = Map.get(types, itype)
check_covariant(itype, type, field_ident, types)
end
defp check_covariant(itype, type, field_ident, types) when is_atom(type) do
type = Map.get(types, type)
check_covariant(itype, type, field_ident, types)
end
defp check_covariant(_, _, field_ident, _types) do
{:error, field_ident}
end
end