introspection.ex (9293B)
1 defmodule Absinthe.Type.BuiltIns.Introspection do 2 @moduledoc false 3 4 use Absinthe.Schema.Notation 5 6 object :__schema do 7 description "Represents a schema" 8 9 field :description, :string do 10 resolve(fn _, %{schema: schema} -> 11 {:ok, Absinthe.Schema.lookup_type(schema, :__schema).description} 12 end) 13 end 14 15 field :types, non_null(list_of(non_null(:__type))) do 16 resolve fn _, %{schema: schema} -> 17 types = 18 Absinthe.Schema.types(schema) 19 |> Enum.sort_by(& &1.identifier) 20 21 {:ok, types} 22 end 23 end 24 25 field :query_type, 26 type: :__type, 27 resolve: fn _, %{schema: schema} -> 28 {:ok, Absinthe.Schema.lookup_type(schema, :query)} 29 end 30 31 field :mutation_type, 32 type: :__type, 33 resolve: fn _, %{schema: schema} -> 34 {:ok, Absinthe.Schema.lookup_type(schema, :mutation)} 35 end 36 37 field :subscription_type, 38 type: :__type, 39 resolve: fn _, %{schema: schema} -> 40 {:ok, Absinthe.Schema.lookup_type(schema, :subscription)} 41 end 42 43 field :directives, 44 type: non_null(list_of(non_null(:__directive))), 45 resolve: fn _, %{schema: schema} -> 46 directives = 47 Absinthe.Schema.directives(schema) 48 |> Enum.sort_by(& &1.identifier) 49 50 {:ok, directives} 51 end 52 end 53 54 object :__directive do 55 description "Represents a directive" 56 57 field :name, non_null(:string) 58 59 field :description, :string 60 61 field :is_repeatable, non_null(:boolean), 62 resolve: fn _, %{source: source} -> 63 {:ok, source.repeatable} 64 end 65 66 field :args, 67 type: non_null(list_of(non_null(:__inputvalue))), 68 resolve: fn _, %{source: source} -> 69 args = 70 source.args 71 |> Map.values() 72 |> Enum.sort_by(& &1.identifier) 73 74 {:ok, args} 75 end 76 77 field :locations, non_null(list_of(non_null(:__directive_location))) 78 end 79 80 enum :__type_kind, 81 values: Absinthe.Introspection.TypeKind.values() 82 83 enum :__directive_location, 84 values: Absinthe.Introspection.DirectiveLocation.values() 85 86 object :__type do 87 description "Represents scalars, interfaces, object types, unions, enums in the system" 88 89 field :kind, 90 type: non_null(:__type_kind), 91 resolve: fn _, %{source: %{__struct__: type}} -> 92 {:ok, type.kind} 93 end 94 95 field :name, :string 96 97 field :description, :string 98 99 field :fields, list_of(non_null(:__field)) do 100 arg :include_deprecated, :boolean, default_value: false 101 102 resolve fn 103 %{include_deprecated: show_deprecated}, %{source: %{__struct__: str, fields: fields}} 104 when str in [Absinthe.Type.Object, Absinthe.Type.Interface] -> 105 result = 106 fields 107 |> Enum.flat_map(fn {_, %{deprecation: is_deprecated} = field} -> 108 cond do 109 Absinthe.Type.introspection?(field) -> 110 [] 111 112 !is_deprecated || (is_deprecated && show_deprecated) -> 113 [field] 114 115 true -> 116 [] 117 end 118 end) 119 |> Enum.sort_by(& &1.identifier) 120 121 {:ok, result} 122 123 _, _ -> 124 {:ok, nil} 125 end 126 end 127 128 field :interfaces, 129 type: list_of(non_null(:__type)), 130 resolve: fn 131 _, %{schema: schema, source: %{interfaces: interfaces}} -> 132 interfaces = 133 interfaces 134 |> Enum.map(&Absinthe.Schema.lookup_type(schema, &1)) 135 |> Enum.sort_by(& &1.identifier) 136 137 {:ok, interfaces} 138 139 _, _ -> 140 {:ok, nil} 141 end 142 143 field :possible_types, 144 type: list_of(non_null(:__type)), 145 resolve: fn 146 _, %{schema: schema, source: %{types: types}} -> 147 possible_types = 148 types 149 |> Enum.map(&Absinthe.Schema.lookup_type(schema, &1)) 150 |> Enum.sort_by(& &1.identifier) 151 152 {:ok, possible_types} 153 154 _, %{schema: schema, source: %Absinthe.Type.Interface{identifier: ident}} -> 155 {:ok, Absinthe.Schema.implementors(schema, ident)} 156 157 _, _ -> 158 {:ok, nil} 159 end 160 161 field :enum_values, 162 type: list_of(non_null(:__enumvalue)), 163 args: [ 164 include_deprecated: [ 165 type: :boolean, 166 default_value: false 167 ] 168 ], 169 resolve: fn 170 %{include_deprecated: show_deprecated}, %{source: %Absinthe.Type.Enum{values: values}} -> 171 result = 172 values 173 |> Enum.flat_map(fn {_, %{deprecation: is_deprecated} = value} -> 174 if !is_deprecated || (is_deprecated && show_deprecated) do 175 [value] 176 else 177 [] 178 end 179 end) 180 |> Enum.sort_by(& &1.value) 181 182 {:ok, result} 183 184 _, _ -> 185 {:ok, nil} 186 end 187 188 field :input_fields, 189 type: list_of(non_null(:__inputvalue)), 190 resolve: fn 191 _, %{source: %Absinthe.Type.InputObject{fields: fields}} -> 192 input_fields = 193 fields 194 |> Map.values() 195 |> Enum.sort_by(& &1.identifier) 196 197 {:ok, input_fields} 198 199 _, %{source: _} -> 200 {:ok, nil} 201 end 202 203 field :of_type, 204 type: :__type, 205 resolve: fn 206 _, %{schema: schema, source: %{of_type: type}} -> 207 {:ok, Absinthe.Schema.lookup_type(schema, type, unwrap: false)} 208 209 _, _ -> 210 {:ok, nil} 211 end 212 end 213 214 object :__field do 215 field :name, 216 type: non_null(:string), 217 resolve: fn _, %{adapter: adapter, source: source} -> 218 {:ok, adapter.to_external_name(source.name, :field)} 219 end 220 221 field :description, :string 222 223 field :args, 224 type: non_null(list_of(non_null(:__inputvalue))), 225 resolve: fn _, %{source: %{args: args}} -> 226 args = 227 args 228 |> Map.values() 229 |> Enum.sort_by(& &1.identifier) 230 231 {:ok, args} 232 end 233 234 field :type, 235 type: non_null(:__type), 236 resolve: fn _, %{schema: schema, source: source} -> 237 result = 238 case source.type do 239 type when is_atom(type) -> 240 Absinthe.Schema.lookup_type(schema, source.type) 241 242 type -> 243 type 244 end 245 246 {:ok, result} 247 end 248 249 field :is_deprecated, 250 type: non_null(:boolean), 251 resolve: fn 252 _, %{source: %{deprecation: nil}} -> 253 {:ok, false} 254 255 _, _ -> 256 {:ok, true} 257 end 258 259 field :deprecation_reason, 260 type: :string, 261 resolve: fn 262 _, %{source: %{deprecation: nil}} -> 263 {:ok, nil} 264 265 _, %{source: %{deprecation: dep}} -> 266 {:ok, dep.reason} 267 end 268 end 269 270 object :__inputvalue, name: "__InputValue" do 271 field :name, 272 type: non_null(:string), 273 resolve: fn _, %{adapter: adapter, source: source} -> 274 {:ok, adapter.to_external_name(source.name, :field)} 275 end 276 277 field :description, :string 278 279 field :type, 280 type: non_null(:__type), 281 resolve: fn _, %{schema: schema, source: %{type: ident}} -> 282 type = Absinthe.Schema.lookup_type(schema, ident, unwrap: false) 283 {:ok, type} 284 end 285 286 field :default_value, 287 type: :string, 288 resolve: fn 289 _, %{source: %{default_value: nil}} -> 290 {:ok, nil} 291 292 _, %{schema: schema, source: %{default_value: value, type: type}, adapter: adapter} -> 293 {:ok, render_default_value(schema, adapter, type, value)} 294 295 _, %{source: _} -> 296 {:ok, nil} 297 end 298 end 299 300 object :__enumvalue, name: "__EnumValue" do 301 field :name, non_null(:string) 302 303 field :description, :string 304 305 field :is_deprecated, 306 type: non_null(:boolean), 307 resolve: fn 308 _, %{source: %{deprecation: nil}} -> 309 {:ok, false} 310 311 _, _ -> 312 {:ok, true} 313 end 314 315 field :deprecation_reason, 316 type: :string, 317 resolve: fn 318 _, %{source: %{deprecation: nil}} -> 319 {:ok, nil} 320 321 _, %{source: %{deprecation: dep}} -> 322 {:ok, dep.reason} 323 end 324 end 325 326 def render_default_value(schema, adapter, type, value) do 327 case Absinthe.Schema.lookup_type(schema, type, unwrap: false) do 328 %Absinthe.Type.InputObject{fields: fields} -> 329 object_values = 330 fields 331 |> Map.take(Map.keys(value)) 332 |> Map.values() 333 |> Enum.map(&render_default_value(schema, adapter, &1, value)) 334 |> Enum.join(", ") 335 336 "{#{object_values}}" 337 338 %Absinthe.Type.List{of_type: type} -> 339 list_values = 340 value 341 |> List.wrap() 342 |> Enum.map(&render_default_value(schema, adapter, type, &1)) 343 |> Enum.join(", ") 344 345 "[#{list_values}]" 346 347 %Absinthe.Type.Field{type: type, name: name, identifier: identifier} -> 348 key = adapter.to_external_name(name, :field) 349 val = render_default_value(schema, adapter, type, value[identifier]) 350 "#{key}: #{val}" 351 352 %Absinthe.Type.Enum{values_by_internal_value: values} -> 353 values[value].name 354 355 %Absinthe.Type.NonNull{of_type: type} -> 356 render_default_value(schema, adapter, type, value) 357 358 %Absinthe.Type.Scalar{} = sc -> 359 inspect(Absinthe.Type.Scalar.serialize(sc, value)) 360 end 361 end 362 end