schema.ex (8191B)
1 defmodule Absinthe.Blueprint.Schema do 2 @moduledoc false 3 4 alias __MODULE__ 5 6 alias Absinthe.Blueprint 7 8 @type directive_t :: Schema.DirectiveDefinition.t() 9 10 @type type_t :: 11 Blueprint.Schema.EnumTypeDefinition.t() 12 | Blueprint.Schema.InputObjectTypeDefinition.t() 13 | Blueprint.Schema.InterfaceTypeDefinition.t() 14 | Blueprint.Schema.ObjectTypeDefinition.t() 15 | Blueprint.Schema.ScalarTypeDefinition.t() 16 | Blueprint.Schema.UnionTypeDefinition.t() 17 18 @type t :: 19 Blueprint.Schema.EnumValueDefinition.t() 20 | Blueprint.Schema.InputValueDefinition.t() 21 | Blueprint.Schema.SchemaDeclaration.t() 22 | Blueprint.Schema.SchemaDefinition.t() 23 | type_t() 24 | directive_t() 25 26 @doc """ 27 Lookup a type definition that is part of a schema. 28 """ 29 @spec lookup_type(Blueprint.t(), atom) :: nil | Blueprint.Schema.t() 30 def lookup_type(blueprint, identifier) do 31 blueprint.schema_definitions 32 |> List.first() 33 |> Map.get(:type_definitions) 34 |> Enum.find(fn 35 %{identifier: ^identifier} -> 36 true 37 38 _ -> 39 false 40 end) 41 end 42 43 @doc """ 44 Lookup a directive definition that is part of a schema. 45 """ 46 @spec lookup_directive(Blueprint.t(), atom) :: nil | Blueprint.Schema.directive_t() 47 def lookup_directive(blueprint, identifier) do 48 blueprint.schema_definitions 49 |> List.first() 50 |> Map.get(:directive_definitions) 51 |> Enum.find(fn 52 %{identifier: ^identifier} -> 53 true 54 55 _ -> 56 false 57 end) 58 end 59 60 def functions(module) do 61 if function_exported?(module, :functions, 0) do 62 module.functions 63 else 64 [] 65 end 66 end 67 68 def build([%Absinthe.Blueprint{} = bp | attrs]) do 69 build_types(attrs, [bp], []) 70 end 71 72 defp build_types([], [bp], buffer) do 73 if buffer != [] do 74 raise """ 75 Unused buffer! #{inspect(buffer)} 76 """ 77 end 78 79 Map.update!(bp, :schema_definitions, &Enum.reverse/1) 80 end 81 82 # this rather insane scheme lets interior macros get back out to exterior 83 # scopes so that they can define top level entities as necessary, and then 84 # return to the regularly scheduled programming. 85 defp build_types([:stash | rest], [head | tail], buff) do 86 build_types(rest, tail, [head | buff]) 87 end 88 89 defp build_types([:pop | rest], remaining, [head | buff]) do 90 build_types(rest, [head | remaining], buff) 91 end 92 93 defp build_types([%Schema.SchemaDefinition{} = schema | rest], stack, buff) do 94 build_types(rest, [schema | stack], buff) 95 end 96 97 @simple_open [ 98 Schema.ScalarTypeDefinition, 99 Schema.ObjectTypeDefinition, 100 Schema.FieldDefinition, 101 Schema.EnumTypeDefinition, 102 Schema.DirectiveDefinition, 103 Schema.InputObjectTypeDefinition, 104 Schema.InputValueDefinition, 105 Schema.InterfaceTypeDefinition, 106 Schema.UnionTypeDefinition, 107 Schema.EnumValueDefinition 108 ] 109 110 defp build_types([%module{} = type | rest], stack, buff) when module in @simple_open do 111 build_types(rest, [type | stack], buff) 112 end 113 114 defp build_types([{:import_fields, criterion} | rest], [obj | stack], buff) do 115 build_types(rest, [push(obj, :imports, criterion) | stack], buff) 116 end 117 118 defp build_types([{:desc, desc} | rest], [item | stack], buff) do 119 build_types(rest, [%{item | description: desc} | stack], buff) 120 end 121 122 defp build_types([{:middleware, middleware} | rest], [field, obj | stack], buff) do 123 field = Map.update!(field, :middleware, &(middleware ++ &1)) 124 build_types(rest, [field, obj | stack], buff) 125 end 126 127 defp build_types([{:config, config} | rest], [field | stack], buff) do 128 field = %{field | config: config} 129 build_types(rest, [field | stack], buff) 130 end 131 132 defp build_types([{:directive, trigger} | rest], [field | stack], buff) do 133 field = Map.update!(field, :directives, &[trigger | &1]) 134 build_types(rest, [field | stack], buff) 135 end 136 137 defp build_types([{:trigger, trigger} | rest], [field | stack], buff) do 138 field = Map.update!(field, :triggers, &[trigger | &1]) 139 build_types(rest, [field | stack], buff) 140 end 141 142 defp build_types([{:interface, interface} | rest], [obj | stack], buff) do 143 obj = Map.update!(obj, :interfaces, &[interface | &1]) 144 build_types(rest, [obj | stack], buff) 145 end 146 147 defp build_types([{:__private__, private} | rest], [entity | stack], buff) do 148 entity = Map.update!(entity, :__private__, &update_private(&1, private)) 149 build_types(rest, [entity | stack], buff) 150 end 151 152 defp build_types([{:values, values} | rest], [enum | stack], buff) do 153 enum = Map.update!(enum, :values, &(List.wrap(values) ++ &1)) 154 build_types(rest, [enum | stack], buff) 155 end 156 157 defp build_types([{:sdl, sdl_definitions} | rest], [schema | stack], buff) do 158 # TODO: Handle directives, etc 159 build_types(rest, [concat(schema, :type_definitions, sdl_definitions) | stack], buff) 160 end 161 162 defp build_types([{:locations, locations} | rest], [directive | stack], buff) do 163 directive = Map.update!(directive, :locations, &(locations ++ &1)) 164 build_types(rest, [directive | stack], buff) 165 end 166 167 defp build_types([{attr, value} | rest], [entity | stack], buff) do 168 entity = %{entity | attr => value} 169 build_types(rest, [entity | stack], buff) 170 end 171 172 defp build_types([:close | rest], [%Schema.EnumValueDefinition{} = value, enum | stack], buff) do 173 build_types(rest, [push(enum, :values, value) | stack], buff) 174 end 175 176 defp build_types([:close | rest], [%Schema.InputValueDefinition{} = arg, field | stack], buff) do 177 build_types(rest, [push(field, :arguments, arg) | stack], buff) 178 end 179 180 defp build_types([:close | rest], [%Schema.FieldDefinition{} = field, obj | stack], buff) do 181 field = 182 field 183 |> Map.update!(:middleware, &Enum.reverse/1) 184 |> Map.update!(:arguments, &Enum.reverse/1) 185 |> Map.update!(:triggers, &{:%{}, [], &1}) 186 |> Map.put(:function_ref, {obj.identifier, field.identifier}) 187 188 build_types(rest, [push(obj, :fields, field) | stack], buff) 189 end 190 191 defp build_types([:close | rest], [%Schema.ObjectTypeDefinition{} = obj, schema | stack], buff) do 192 obj = Map.update!(obj, :fields, &Enum.reverse/1) 193 build_types(rest, [push(schema, :type_definitions, obj) | stack], buff) 194 end 195 196 defp build_types( 197 [:close | rest], 198 [%Schema.InputObjectTypeDefinition{} = obj, schema | stack], 199 buff 200 ) do 201 obj = Map.update!(obj, :fields, &Enum.reverse/1) 202 build_types(rest, [push(schema, :type_definitions, obj) | stack], buff) 203 end 204 205 defp build_types( 206 [:close | rest], 207 [%Schema.InterfaceTypeDefinition{} = iface, schema | stack], 208 buff 209 ) do 210 iface = Map.update!(iface, :fields, &Enum.reverse/1) 211 build_types(rest, [push(schema, :type_definitions, iface) | stack], buff) 212 end 213 214 defp build_types([:close | rest], [%Schema.UnionTypeDefinition{} = union, schema | stack], buff) do 215 build_types(rest, [push(schema, :type_definitions, union) | stack], buff) 216 end 217 218 defp build_types([:close | rest], [%Schema.DirectiveDefinition{} = dir, schema | stack], buff) do 219 build_types(rest, [push(schema, :directive_definitions, dir) | stack], buff) 220 end 221 222 defp build_types([:close | rest], [%Schema.EnumTypeDefinition{} = type, schema | stack], buff) do 223 type = Map.update!(type, :values, &Enum.reverse/1) 224 schema = push(schema, :type_definitions, type) 225 build_types(rest, [schema | stack], buff) 226 end 227 228 defp build_types([:close | rest], [%Schema.ScalarTypeDefinition{} = type, schema | stack], buff) do 229 schema = push(schema, :type_definitions, type) 230 build_types(rest, [schema | stack], buff) 231 end 232 233 defp build_types([:close | rest], [%Schema.SchemaDefinition{} = schema, bp], buff) do 234 bp = push(bp, :schema_definitions, schema) 235 build_types(rest, [bp], buff) 236 end 237 238 defp push(entity, key, value) do 239 Map.update!(entity, key, &[value | &1]) 240 end 241 242 defp concat(entity, key, value) do 243 Map.update!(entity, key, &(&1 ++ value)) 244 end 245 246 defp update_private(existing_private, private) do 247 Keyword.merge(existing_private, private, fn 248 _, v1, v2 -> 249 update_private(v1, v2) 250 end) 251 end 252 end