zf

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

sdl_render.ex (12858B)


      1 defmodule Absinthe.Schema.Notation.SDL.Render do
      2   @moduledoc false
      3   import Inspect.Algebra
      4   import Absinthe.Utils.Render
      5 
      6   alias Absinthe.Blueprint
      7 
      8   @line_width 120
      9 
     10   def inspect(term, %{pretty: true}) do
     11     term
     12     |> render()
     13     |> concat(line())
     14     |> format(@line_width)
     15     |> to_string
     16   end
     17 
     18   def inspect(term, options) do
     19     Inspect.Any.inspect(term, options)
     20   end
     21 
     22   @skip_modules [
     23     Absinthe.Phase.Schema.Introspection,
     24     Absinthe.Type.BuiltIns.Directives,
     25     Absinthe.Type.BuiltIns.Scalars,
     26     Absinthe.Type.BuiltIns.Introspection
     27   ]
     28   defp render(bp, type_definitions \\ [])
     29 
     30   defp render(%Blueprint{} = bp, _) do
     31     %{
     32       schema_definitions: [
     33         %Blueprint.Schema.SchemaDefinition{
     34           type_definitions: type_definitions,
     35           directive_definitions: directive_definitions,
     36           schema_declaration: schema_declaration
     37         }
     38       ]
     39     } = bp
     40 
     41     schema_declaration =
     42       schema_declaration ||
     43         %{
     44           query: Enum.find(type_definitions, &(&1.identifier == :query)),
     45           mutation: Enum.find(type_definitions, &(&1.identifier == :mutation)),
     46           subscription: Enum.find(type_definitions, &(&1.identifier == :subscription)),
     47           description: Enum.find(type_definitions, &(&1.identifier == :__schema)).description
     48         }
     49 
     50     directive_definitions =
     51       directive_definitions
     52       |> Enum.reject(&(&1.module in @skip_modules))
     53 
     54     all_type_definitions =
     55       type_definitions
     56       |> Enum.reject(&(&1.__struct__ == Blueprint.Schema.SchemaDeclaration))
     57 
     58     types_to_render =
     59       all_type_definitions
     60       |> Enum.reject(&(&1.module in @skip_modules))
     61       |> Enum.filter(& &1.__private__[:__absinthe_referenced__])
     62 
     63     ([schema_declaration] ++ directive_definitions ++ types_to_render)
     64     |> Enum.map(&render(&1, all_type_definitions))
     65     |> Enum.reject(&(&1 == empty()))
     66     |> join([line(), line()])
     67   end
     68 
     69   defp render(%Blueprint.Schema.SchemaDeclaration{} = schema, type_definitions) do
     70     block(
     71       concat([
     72         "schema",
     73         directives(schema.directives, type_definitions)
     74       ]),
     75       render_list(schema.field_definitions, type_definitions)
     76     )
     77     |> description(schema.description)
     78   end
     79 
     80   defp render(
     81          %{
     82            query: query_type,
     83            mutation: mutation_type,
     84            subscription: subscription_type,
     85            description: description
     86          },
     87          _type_definitions
     88        ) do
     89     schema_type_docs =
     90       [
     91         query_type && concat("query: ", string(query_type.name)),
     92         mutation_type && concat("mutation: ", string(mutation_type.name)),
     93         subscription_type && concat("subscription: ", string(subscription_type.name))
     94       ]
     95       |> Enum.reject(&is_nil/1)
     96       |> join([line()])
     97 
     98     block(
     99       "schema",
    100       schema_type_docs
    101     )
    102     |> description(description)
    103   end
    104 
    105   @adapter Absinthe.Adapter.LanguageConventions
    106   defp render(%Blueprint.Schema.InputValueDefinition{} = input_value, type_definitions) do
    107     concat([
    108       string(@adapter.to_external_name(input_value.name, :argument)),
    109       ": ",
    110       render(input_value.type, type_definitions),
    111       default(input_value.default_value_blueprint),
    112       directives(input_value.directives, type_definitions)
    113     ])
    114     |> description(input_value.description)
    115   end
    116 
    117   defp render(%Blueprint.Schema.FieldDefinition{} = field, type_definitions) do
    118     concat([
    119       string(@adapter.to_external_name(field.name, :field)),
    120       arguments(field.arguments, type_definitions),
    121       ": ",
    122       render(field.type, type_definitions),
    123       directives(field.directives, type_definitions)
    124     ])
    125     |> description(field.description)
    126   end
    127 
    128   defp render(%Blueprint.Schema.ObjectTypeDefinition{} = object_type, type_definitions) do
    129     block(
    130       "type",
    131       concat([
    132         string(object_type.name),
    133         implements(object_type, type_definitions),
    134         directives(object_type.directives, type_definitions)
    135       ]),
    136       render_list(object_type.fields, type_definitions)
    137     )
    138     |> description(object_type.description)
    139   end
    140 
    141   defp render(%Blueprint.Schema.InputObjectTypeDefinition{} = input_object_type, type_definitions) do
    142     block(
    143       concat([
    144         "input ",
    145         string(input_object_type.name),
    146         directives(input_object_type.directives, type_definitions)
    147       ]),
    148       render_list(input_object_type.fields, type_definitions)
    149     )
    150     |> description(input_object_type.description)
    151   end
    152 
    153   defp render(%Blueprint.Schema.UnionTypeDefinition{} = union_type, type_definitions) do
    154     Enum.map(union_type.types, fn
    155       identifier when is_atom(identifier) ->
    156         render(%Blueprint.TypeReference.Identifier{id: identifier}, type_definitions)
    157 
    158       %Blueprint.TypeReference.Name{} = ref ->
    159         render(ref, type_definitions)
    160 
    161       %Blueprint.TypeReference.Identifier{} = ref ->
    162         render(ref, type_definitions)
    163     end)
    164     |> case do
    165       [] ->
    166         concat([
    167           "union ",
    168           string(union_type.name),
    169           directives(union_type.directives, type_definitions)
    170         ])
    171 
    172       types ->
    173         concat([
    174           "union ",
    175           string(union_type.name),
    176           directives(union_type.directives, type_definitions),
    177           " = ",
    178           join(types, " | ")
    179         ])
    180     end
    181     |> description(union_type.description)
    182   end
    183 
    184   defp render(%Blueprint.Schema.InterfaceTypeDefinition{} = interface_type, type_definitions) do
    185     block(
    186       "interface",
    187       concat([
    188         string(interface_type.name),
    189         implements(interface_type, type_definitions),
    190         directives(interface_type.directives, type_definitions)
    191       ]),
    192       render_list(interface_type.fields, type_definitions)
    193     )
    194     |> description(interface_type.description)
    195   end
    196 
    197   defp render(%Blueprint.Schema.EnumTypeDefinition{} = enum_type, type_definitions) do
    198     block(
    199       concat([
    200         "enum ",
    201         string(enum_type.name),
    202         directives(enum_type.directives, type_definitions)
    203       ]),
    204       render_list(List.flatten(enum_type.values), type_definitions)
    205     )
    206     |> description(enum_type.description)
    207   end
    208 
    209   defp render(%Blueprint.Schema.EnumValueDefinition{} = enum_value, type_definitions) do
    210     concat([
    211       string(enum_value.name),
    212       directives(enum_value.directives, type_definitions)
    213     ])
    214     |> description(enum_value.description)
    215   end
    216 
    217   defp render(%Blueprint.Schema.ScalarTypeDefinition{} = scalar_type, type_definitions) do
    218     concat([
    219       "scalar ",
    220       string(scalar_type.name),
    221       directives(scalar_type.directives, type_definitions)
    222     ])
    223     |> description(scalar_type.description)
    224   end
    225 
    226   defp render(%Blueprint.Schema.DirectiveDefinition{} = directive, type_definitions) do
    227     locations = directive.locations |> Enum.map(&String.upcase(to_string(&1)))
    228 
    229     concat([
    230       "directive ",
    231       "@",
    232       string(directive.name),
    233       arguments(directive.arguments, type_definitions),
    234       repeatable(directive.repeatable),
    235       " on ",
    236       join(locations, " | ")
    237     ])
    238     |> description(directive.description)
    239   end
    240 
    241   defp render(%Blueprint.Directive{} = directive, type_definitions) do
    242     concat([
    243       " @",
    244       directive.name,
    245       directive_arguments(directive.arguments, type_definitions)
    246     ])
    247   end
    248 
    249   defp render(%Blueprint.Input.Argument{} = argument, _type_definitions) do
    250     concat([
    251       argument.name,
    252       ": ",
    253       render_value(argument.input_value)
    254     ])
    255   end
    256 
    257   defp render(%Blueprint.TypeReference.Name{name: name}, _type_definitions) do
    258     string(name)
    259   end
    260 
    261   defp render(%Blueprint.TypeReference.Identifier{id: id}, type_definitions) do
    262     type = Enum.find(type_definitions, &(&1.identifier == id))
    263 
    264     if type do
    265       string(type.name)
    266     else
    267       all_type_ids = Enum.map(type_definitions, & &1.identifier)
    268 
    269       raise """
    270       No type found for identifier #{inspect(id)} in #{inspect(all_type_ids)}
    271       """
    272     end
    273   end
    274 
    275   defp render(%Blueprint.TypeReference.List{of_type: of_type}, type_definitions) do
    276     concat(["[", render(of_type, type_definitions), "]"])
    277   end
    278 
    279   defp render(%Blueprint.TypeReference.NonNull{of_type: of_type}, type_definitions) do
    280     concat([render(of_type, type_definitions), "!"])
    281   end
    282 
    283   defp render(nil, _) do
    284     raise "Unexpected nil"
    285   end
    286 
    287   defp render(identifier, type_definitions) when is_atom(identifier) do
    288     render(%Blueprint.TypeReference.Identifier{id: identifier}, type_definitions)
    289   end
    290 
    291   # SDL Syntax Helpers
    292 
    293   defp directives([], _) do
    294     empty()
    295   end
    296 
    297   defp directives(directives, type_definitions) do
    298     concat(Enum.map(directives, &render(&1, type_definitions)))
    299   end
    300 
    301   defp directive_arguments([], _) do
    302     empty()
    303   end
    304 
    305   defp directive_arguments(arguments, type_definitions) do
    306     args = Enum.map(arguments, &render(&1, type_definitions))
    307 
    308     concat([
    309       "(",
    310       join(args, ", "),
    311       ")"
    312     ])
    313   end
    314 
    315   defp arguments([], _) do
    316     empty()
    317   end
    318 
    319   defp arguments(args, type_definitions) do
    320     any_descriptions? = Enum.any?(args, & &1.description)
    321 
    322     group(
    323       glue(
    324         nest(
    325           multiline(
    326             glue(
    327               "(",
    328               "",
    329               render_list(args, type_definitions, ", ")
    330             ),
    331             any_descriptions?
    332           ),
    333           2,
    334           :break
    335         ),
    336         "",
    337         ")"
    338       )
    339     )
    340   end
    341 
    342   defp default(nil) do
    343     empty()
    344   end
    345 
    346   defp default(default_value) do
    347     concat([" = ", render_value(default_value)])
    348   end
    349 
    350   defp description(docs, nil) do
    351     docs
    352   end
    353 
    354   defp description(docs, description) do
    355     concat([
    356       render_string_value(description, 0),
    357       line(),
    358       docs
    359     ])
    360   end
    361 
    362   defp implements(%{interface_blueprints: [], interfaces: []}, _) do
    363     empty()
    364   end
    365 
    366   defp implements(interface, type_definitions) do
    367     interface_names =
    368       case interface do
    369         %{interface_blueprints: [], interfaces: identifiers} ->
    370           Enum.map(identifiers, fn identifier ->
    371             Enum.find_value(type_definitions, fn
    372               %{identifier: ^identifier, name: name} -> name
    373               _ -> nil
    374             end)
    375           end)
    376 
    377         %{interface_blueprints: blueprints} ->
    378           Enum.map(blueprints, & &1.name)
    379       end
    380 
    381     concat([
    382       " implements ",
    383       join(interface_names, " & ")
    384     ])
    385   end
    386 
    387   defp repeatable(true), do: " repeatable"
    388   defp repeatable(_), do: empty()
    389 
    390   # Render Helpers
    391 
    392   defp render_list(items, type_definitions, seperator \\ line())
    393 
    394   # Workaround for `values` macro which temporarily defines
    395   # values as raw atoms to support dynamic schemas
    396   defp render_list([first | _] = items, type_definitions, seperator) when is_atom(first) do
    397     items
    398     |> Enum.map(
    399       &%Blueprint.Schema.EnumValueDefinition{
    400         value: &1,
    401         name: String.upcase(to_string(&1))
    402       }
    403     )
    404     |> render_list(type_definitions, seperator)
    405   end
    406 
    407   defp render_list(items, type_definitions, seperator) do
    408     items = Enum.reject(items, &(&1.module in @skip_modules))
    409 
    410     splitter =
    411       items
    412       |> Enum.any?(&(&1.description not in ["", nil]))
    413       |> case do
    414         true -> [nest(line(), :reset), line()]
    415         false -> [seperator]
    416       end
    417 
    418     items
    419     |> Enum.reverse()
    420     |> Enum.reduce(:start, fn
    421       item, :start -> render(item, type_definitions)
    422       item, acc -> concat([render(item, type_definitions)] ++ splitter ++ [acc])
    423     end)
    424   end
    425 
    426   defp render_value(%Blueprint.Input.String{value: value}),
    427     do: render_string_value(value)
    428 
    429   defp render_value(%Blueprint.Input.RawValue{content: content}),
    430     do: render_value(content)
    431 
    432   defp render_value(%Blueprint.Input.Value{raw: raw}),
    433     do: render_value(raw)
    434 
    435   defp render_value(%Blueprint.Input.Null{}),
    436     do: "null"
    437 
    438   defp render_value(%Blueprint.Input.Object{fields: fields}) do
    439     default_fields = Enum.map(fields, &render_value/1)
    440     concat(["{", join(default_fields, ", "), "}"])
    441   end
    442 
    443   defp render_value(%Blueprint.Input.List{items: items}) do
    444     default_list = Enum.map(items, &render_value/1)
    445     concat(["[", join(default_list, ", "), "]"])
    446   end
    447 
    448   defp render_value(%Blueprint.Input.Field{name: name, input_value: value}),
    449     do: concat([name, ": ", render_value(value)])
    450 
    451   defp render_value(%{value: value}),
    452     do: to_string(value)
    453 
    454   # Algebra Helpers
    455 
    456   defp multiline(docs, true) do
    457     force_unfit(docs)
    458   end
    459 
    460   defp multiline(docs, false) do
    461     docs
    462   end
    463 
    464   defp block(kind, name, docs) do
    465     glue(
    466       kind,
    467       block(name, docs)
    468     )
    469   end
    470 
    471   defp block(name, docs) do
    472     glue(
    473       name,
    474       group(
    475         glue(
    476           nest(
    477             force_unfit(
    478               glue(
    479                 "{",
    480                 "",
    481                 docs
    482               )
    483             ),
    484             2,
    485             :always
    486           ),
    487           "",
    488           "}"
    489         )
    490       )
    491     )
    492   end
    493 end