zf

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

type.ex (9934B)


      1 defmodule Absinthe.Type do
      2   @moduledoc false
      3 
      4   alias __MODULE__
      5 
      6   alias Absinthe.Schema
      7 
      8   @type function_identifier :: {module, any}
      9   @type function_ref :: {:ref, module, function_identifier}
     10 
     11   # ALL TYPES
     12 
     13   @type_modules [
     14     Type.Scalar,
     15     Type.Object,
     16     Type.Interface,
     17     Type.Union,
     18     Type.Enum,
     19     Type.InputObject,
     20     Type.List,
     21     Type.NonNull
     22   ]
     23 
     24   @typedoc "The types that can be custom-built in a schema"
     25   @type custom_t ::
     26           Type.Scalar.t()
     27           | Type.Object.t()
     28           | Type.Field.t()
     29           | Type.Interface.t()
     30           | Type.Union.t()
     31           | Type.Enum.t()
     32           | Type.InputObject.t()
     33 
     34   @typedoc "All the possible types"
     35   @type t :: custom_t | wrapping_t
     36 
     37   @typedoc "A type identifier"
     38   @type identifier_t :: atom
     39 
     40   @typedoc "A type reference"
     41   @type reference_t :: identifier_t | binary | t
     42 
     43   def function(type, key) do
     44     case Map.fetch!(type, key) do
     45       {:ref, module, identifier} ->
     46         module.__absinthe_function__(identifier, key)
     47 
     48       function ->
     49         function
     50     end
     51   end
     52 
     53   @doc false
     54   # this is just for debugging
     55   def expand(%module{} = type) do
     56     module.functions()
     57     |> Enum.reduce(type, fn
     58       :middleware, type ->
     59         type
     60 
     61       attr, type ->
     62         Map.put(type, attr, Absinthe.Type.function(type, attr))
     63     end)
     64   end
     65 
     66   @doc "Lookup a custom metadata field on a type"
     67   @spec meta(custom_t, atom) :: nil | any
     68   def meta(%{__private__: store}, key) do
     69     get_in(store, [:meta, key])
     70   end
     71 
     72   @doc "Return all custom metadata on a type"
     73   @spec meta(custom_t) :: map
     74   def meta(%{__private__: store}) do
     75     Keyword.get(store, :meta, [])
     76     |> Enum.into(%{})
     77   end
     78 
     79   @doc "Determine if a struct matches one of the types"
     80   @spec type?(any) :: boolean
     81   def type?(%{__struct__: mod}) when mod in @type_modules, do: true
     82   def type?(_), do: false
     83 
     84   @doc "Determine whether a field/argument is deprecated"
     85   @spec deprecated?(Type.Field.t() | Type.Argument.t()) :: boolean
     86   def deprecated?(%{deprecation: nil}), do: false
     87   def deprecated?(%{deprecation: _}), do: true
     88 
     89   def equal?(%{name: name}, %{name: name}), do: true
     90   def equal?(_, _), do: false
     91 
     92   def built_in?(type) do
     93     type.definition
     94     |> built_in_module?()
     95   end
     96 
     97   def built_in_module?(module) do
     98     module
     99     |> Module.split()
    100     |> Enum.take(3)
    101     |> Module.concat() == Absinthe.Type.BuiltIns
    102   end
    103 
    104   # INPUT TYPES
    105 
    106   @input_type_modules [Type.Scalar, Type.Enum, Type.InputObject, Type.List, Type.NonNull]
    107 
    108   @typedoc "These types may be used as input types for arguments and directives."
    109   @type input_t ::
    110           Type.Scalar.t()
    111           | Type.Enum.t()
    112           | Type.InputObject.t()
    113           | Type.List.t()
    114           | Type.NonNull.t()
    115 
    116   @doc "Determine if a term is an input type"
    117   @spec input_type?(any) :: boolean
    118   def input_type?(term) do
    119     term
    120     |> named_type
    121     |> do_input_type?
    122   end
    123 
    124   defp do_input_type?(%{__struct__: mod}) when mod in @input_type_modules, do: true
    125   defp do_input_type?(_), do: false
    126 
    127   # OBJECT TYPE
    128 
    129   @doc "Determine if a term is an object type"
    130   @spec object_type?(any) :: boolean
    131   def object_type?(%Type.Object{}), do: true
    132   def object_type?(_), do: false
    133 
    134   @doc "Resolve a type for a value from an interface (if necessary)"
    135   @spec resolve_type(t, any) :: t
    136   def resolve_type(%{resolve_type: resolver}, value), do: resolver.(value)
    137   def resolve_type(type, _value), do: type
    138 
    139   # TYPE WITH FIELDS
    140 
    141   @doc "Determine if a type has fields"
    142   @spec fielded?(any) :: boolean
    143   def fielded?(%{fields: _}), do: true
    144   def fielded?(_), do: false
    145 
    146   # OUTPUT TYPES
    147 
    148   @output_type_modules [Type.Scalar, Type.Object, Type.Interface, Type.Union, Type.Enum]
    149 
    150   @typedoc "These types may be used as output types as the result of fields."
    151   @type output_t ::
    152           Type.Scalar.t() | Type.Object.t() | Type.Interface.t() | Type.Union.t() | Type.Enum.t()
    153 
    154   @doc "Determine if a term is an output type"
    155   @spec output_type?(any) :: boolean
    156   def output_type?(term) do
    157     term
    158     |> named_type
    159     |> do_output_type?
    160   end
    161 
    162   defp do_output_type?(%{__struct__: mod}) when mod in @output_type_modules, do: true
    163   defp do_output_type?(_), do: false
    164 
    165   # LEAF TYPES
    166 
    167   @leaf_type_modules [Type.Scalar, Type.Enum]
    168 
    169   @typedoc "These types may describe types which may be leaf values."
    170   @type leaf_t :: Type.Scalar.t() | Type.Enum.t()
    171 
    172   @doc "Determine if a term is a leaf type"
    173   @spec leaf_type?(any) :: boolean
    174   def leaf_type?(term) do
    175     term
    176     |> named_type
    177     |> do_leaf_type?
    178   end
    179 
    180   defp do_leaf_type?(%{__struct__: mod}) when mod in @leaf_type_modules, do: true
    181   defp do_leaf_type?(_), do: false
    182 
    183   # COMPOSITE TYPES
    184 
    185   @composite_type_modules [Type.Object, Type.Interface, Type.Union]
    186 
    187   @typedoc "These types may describe the parent context of a selection set."
    188   @type composite_t :: Type.Object.t() | Type.Interface.t() | Type.Union.t()
    189 
    190   @doc "Determine if a term is a composite type"
    191   @spec composite_type?(any) :: boolean
    192   def composite_type?(%{__struct__: mod}) when mod in @composite_type_modules, do: true
    193   def composite_type?(_), do: false
    194 
    195   # ABSTRACT TYPES
    196 
    197   @abstract_type_modules [Type.Interface, Type.Union]
    198 
    199   @typedoc "These types may describe the parent context of a selection set."
    200   @type abstract_t :: Type.Interface.t() | Type.Union.t()
    201 
    202   @doc "Determine if a term is an abstract type"
    203   @spec abstract?(any) :: boolean
    204   def abstract?(%{__struct__: mod}) when mod in @abstract_type_modules, do: true
    205   def abstract?(_), do: false
    206 
    207   # NULLABLE TYPES
    208 
    209   # @nullable_type_modules [Type.Scalar, Type.Object, Type.Interface, Type.Union, Type.Enum, Type.InputObject, Type.List]
    210 
    211   @typedoc "These types can all accept null as a value."
    212   @type nullable_t ::
    213           Type.Scalar.t()
    214           | Type.Object.t()
    215           | Type.Interface.t()
    216           | Type.Union.t()
    217           | Type.Enum.t()
    218           | Type.InputObject.t()
    219           | Type.List.t()
    220 
    221   @doc "Unwrap the underlying nullable type or return unmodified"
    222   # nullable_t is a subset of t, but broken out for clarity
    223   @spec nullable(any) :: nullable_t | t
    224   def nullable(%Type.NonNull{of_type: nullable}), do: nullable
    225   def nullable(term), do: term
    226 
    227   @doc "Determine if a type is non null"
    228   @spec non_null?(t) :: boolean
    229   def non_null?(%Type.NonNull{}), do: true
    230   def non_null?(_), do: false
    231 
    232   # NAMED TYPES
    233 
    234   @named_type_modules [
    235     Type.Scalar,
    236     Type.Object,
    237     Type.Interface,
    238     Type.Union,
    239     Type.Enum,
    240     Type.InputObject
    241   ]
    242 
    243   @typedoc "These named types do not include modifiers like Absinthe.Type.List or Absinthe.Type.NonNull."
    244   @type named_t ::
    245           Type.Scalar.t()
    246           | Type.Object.t()
    247           | Type.Interface.t()
    248           | Type.Union.t()
    249           | Type.Enum.t()
    250           | Type.InputObject.t()
    251 
    252   @doc "Determine the underlying named type, if any"
    253   @spec named_type(any) :: nil | named_t
    254   def named_type(%{__struct__: mod, of_type: unmodified}) when mod in [Type.List, Type.NonNull] do
    255     named_type(unmodified)
    256   end
    257 
    258   def named_type(%{__struct__: mod} = term) when mod in @named_type_modules, do: term
    259   def named_type(_), do: nil
    260 
    261   @doc "Determine if a type is named"
    262   @spec named?(t) :: boolean
    263   def named?(%{name: _}), do: true
    264   def named?(_), do: false
    265 
    266   # WRAPPERS
    267 
    268   @wrapping_modules [Type.List, Type.NonNull]
    269 
    270   @typedoc "A type wrapped in a List on NonNull"
    271   @type wrapping_t :: Type.List.t() | Type.NonNull.t()
    272 
    273   @spec wrapped?(t) :: boolean
    274   def wrapped?(%{__struct__: mod}) when mod in @wrapping_modules, do: true
    275   def wrapped?(_), do: false
    276 
    277   @doc "Unwrap a type from a List or NonNull"
    278   @spec unwrap(custom_t | wrapping_t | map) :: reference_t | map | nil
    279   def unwrap(%{of_type: t}), do: unwrap(t)
    280   def unwrap(type), do: type
    281 
    282   @doc "Unwrap a type from NonNull"
    283   @spec unwrap_non_null(Type.NonNull.t()) :: reference_t
    284   @spec unwrap_non_null(type) :: type when type: custom_t | Type.List.t()
    285   def unwrap_non_null(%Type.NonNull{of_type: t}), do: unwrap_non_null(t)
    286   def unwrap_non_null(type), do: type
    287 
    288   @doc """
    289   Get the GraphQL name for a (possibly wrapped) type, expanding
    290   any references if necessary using the provided schema.
    291   """
    292   @spec name(reference_t, Schema.t()) :: String.t()
    293   def name(ref, schema) do
    294     expanded = expand(ref, schema)
    295     name(expanded)
    296   end
    297 
    298   @doc """
    299   Get the GraphQL name for a (possibly wrapped) type.
    300 
    301   Note: Use `name/2` if the provided type reference needs to
    302   be expanded to resolve any atom type references.
    303   """
    304   @spec name(wrapping_t | t) :: String.t()
    305   def name(%Type.NonNull{of_type: contents}) do
    306     name(contents) <> "!"
    307   end
    308 
    309   def name(%Type.List{of_type: contents}) do
    310     "[" <> name(contents) <> "]"
    311   end
    312 
    313   def name(%{name: name}) do
    314     name
    315   end
    316 
    317   @doc "Expand any atom type references inside a List or NonNull"
    318   @spec expand(reference_t, Schema.t()) :: wrapping_t | t
    319   def expand(ref, schema) when is_atom(ref) do
    320     schema.__absinthe_lookup__(ref)
    321   end
    322 
    323   def expand(%{of_type: contents} = ref, schema) do
    324     %{ref | of_type: expand(contents, schema)}
    325   end
    326 
    327   def expand(type, _) do
    328     type
    329   end
    330 
    331   # INTROSPECTION TYPE
    332 
    333   @spec introspection?(t) :: boolean
    334   def introspection?(%{name: "__" <> _}) do
    335     true
    336   end
    337 
    338   def introspection?(_) do
    339     false
    340   end
    341 
    342   # VALUE TYPE
    343 
    344   @spec value_type(t, Schema.t()) :: Type.t()
    345   def value_type(%Type.Field{} = node, schema) do
    346     Type.expand(node.type, schema)
    347   end
    348 
    349   def value_type(type, schema) do
    350     Type.expand(type, schema)
    351   end
    352 
    353   # VALID TYPE
    354 
    355   def valid_input?(%Type.NonNull{}, nil) do
    356     false
    357   end
    358 
    359   def valid_input?(%Type.NonNull{of_type: internal_type}, value) do
    360     valid_input?(internal_type, value)
    361   end
    362 
    363   def valid_input?(_type, nil) do
    364     true
    365   end
    366 
    367   def valid_input?(%{parse: parse}, value) do
    368     case parse.(value) do
    369       {:ok, _} -> true
    370       :error -> false
    371     end
    372   end
    373 
    374   def valid_input?(_, _) do
    375     true
    376   end
    377 end