zf

zenflows testing
git clone https://s.sonu.ch/~srfsh/zf.git
Log | Files | Refs | Submodules | README | LICENSE

object_interfaces_must_be_valid.ex (3169B)


      1 defmodule Absinthe.Phase.Schema.Validation.ObjectInterfacesMustBeValid do
      2   @moduledoc false
      3 
      4   use Absinthe.Phase
      5   alias Absinthe.Blueprint
      6 
      7   def run(bp, _) do
      8     bp = Blueprint.prewalk(bp, &handle_schemas/1)
      9     {:ok, bp}
     10   end
     11 
     12   defp handle_schemas(%Blueprint.Schema.SchemaDefinition{} = schema) do
     13     ifaces =
     14       schema.type_definitions
     15       |> Enum.filter(&match?(%Blueprint.Schema.InterfaceTypeDefinition{}, &1))
     16       |> Map.new(&{&1.identifier, &1})
     17 
     18     schema = Blueprint.prewalk(schema, &validate_objects(&1, ifaces))
     19     {:halt, schema}
     20   end
     21 
     22   defp handle_schemas(obj) do
     23     obj
     24   end
     25 
     26   defp validate_objects(%struct{} = object, all_interfaces)
     27        when struct in [
     28               Blueprint.Schema.ObjectTypeDefinition,
     29               Blueprint.Schema.InterfaceTypeDefinition
     30             ] do
     31     check_transitive_interfaces(object, object.interfaces, all_interfaces, nil, [])
     32   end
     33 
     34   defp validate_objects(type, _) do
     35     type
     36   end
     37 
     38   # check that the object declares it implements all interfaces up the
     39   # hierarchy chain as per spec https://github.com/graphql/graphql-spec/blame/October2021/spec/Section%203%20--%20Type%20System.md#L1158-L1161
     40   defp check_transitive_interfaces(
     41          object,
     42          [object_interface | tail],
     43          all_interfaces,
     44          implemented_by,
     45          visited
     46        ) do
     47     current_interface = all_interfaces[object_interface]
     48 
     49     if current_interface && current_interface.identifier in object.interfaces do
     50       case current_interface do
     51         %{interfaces: interfaces} = interface ->
     52           # to prevent walking in cycles we need to filter out visited interfaces
     53           interfaces = Enum.filter(interfaces, &(&1 not in visited))
     54 
     55           check_transitive_interfaces(object, tail ++ interfaces, all_interfaces, interface, [
     56             object_interface | visited
     57           ])
     58 
     59         _ ->
     60           check_transitive_interfaces(object, tail, all_interfaces, implemented_by, [
     61             object_interface | visited
     62           ])
     63       end
     64     else
     65       detail = %{
     66         object: object.identifier,
     67         interface: object_interface,
     68         implemented_by: implemented_by
     69       }
     70 
     71       object |> put_error(error(object, detail))
     72     end
     73   end
     74 
     75   defp check_transitive_interfaces(object, [], _, _, _) do
     76     object
     77   end
     78 
     79   defp error(object, data) do
     80     %Absinthe.Phase.Error{
     81       message: explanation(data),
     82       locations: [object.__reference__.location],
     83       phase: __MODULE__,
     84       extra: data
     85     }
     86   end
     87 
     88   @description """
     89   Only interfaces may be present in an Object's interface list.
     90 
     91   Reference: https://github.com/facebook/graphql/blob/master/spec/Section%203%20--%20Type%20System.md#interfaces
     92   """
     93 
     94   def explanation(%{object: obj, interface: interface, implemented_by: nil}) do
     95     """
     96     Type "#{obj}" must implement interface type "#{interface}"
     97 
     98     #{@description}
     99     """
    100   end
    101 
    102   def explanation(%{object: obj, interface: interface, implemented_by: implemented}) do
    103     """
    104     Type "#{obj}" must implement interface type "#{interface}" because it is implemented by "#{
    105       implemented.identifier
    106     }".
    107 
    108     #{@description}
    109     """
    110   end
    111 end