class GraphQLDocs::Parser

Attributes

processed_schema[R]

Public Class Methods

new(schema, options) click to toggle source
# File lib/graphql-docs/parser.rb, line 11
def initialize(schema, options)
  @options = options

  @options[:notices] ||= ->(_schema_member_path) { [] }

  @schema = if schema.is_a?(String)
              GraphQL::Schema.from_definition(schema)
            elsif schema < GraphQL::Schema
              schema
            end

  @processed_schema = {
    operation_types: [],
    mutation_types: [],
    object_types: [],
    interface_types: [],
    enum_types: [],
    union_types: [],
    input_object_types: [],
    scalar_types: [],
    directive_types: []
  }
end

Public Instance Methods

parse() click to toggle source
# File lib/graphql-docs/parser.rb, line 35
def parse
  root_types = {}
  %w[query mutation].each do |operation|
    root_types[operation] = @schema.root_type_for_operation(operation).graphql_name unless @schema.root_type_for_operation(operation).nil?
  end
  @processed_schema[:root_types] = root_types

  @schema.types.each_value do |object|
    data = {}

    data[:notices] = @options[:notices].call(object.graphql_name)

    if object < ::GraphQL::Schema::Object
      data[:name] = object.graphql_name
      data[:description] = object.description

      if data[:name] == root_types['query']
        data[:interfaces] = object.interfaces.map(&:graphql_name).sort
        data[:fields], data[:connections] = fetch_fields(object.fields, object.graphql_name)

        @processed_schema[:operation_types] << data
      elsif data[:name] == root_types['mutation']
        @processed_schema[:operation_types] << data

        object.fields.each_value do |mutation|
          h = {}

          h[:notices] = @options[:notices].call([object.graphql_name, mutation.graphql_name].join('.'))
          h[:name] = mutation.graphql_name
          h[:description] = mutation.description
          h[:input_fields], = fetch_fields(mutation.arguments, [object.graphql_name, mutation.graphql_name].join('.'))

          return_type = mutation.type
          if return_type.unwrap.respond_to?(:fields)
            h[:return_fields], = fetch_fields(return_type.unwrap.fields, return_type.graphql_name)
          else # it is a scalar return type
            h[:return_fields], = fetch_fields({ return_type.graphql_name => mutation }, return_type.graphql_name)
          end

          @processed_schema[:mutation_types] << h
        end
      else
        data[:interfaces] = object.interfaces.map(&:graphql_name).sort
        data[:fields], data[:connections] = fetch_fields(object.fields, object.graphql_name)

        @processed_schema[:object_types] << data
      end
    elsif object < ::GraphQL::Schema::Interface
      data[:name] = object.graphql_name
      data[:description] = object.description
      data[:fields], data[:connections] = fetch_fields(object.fields, object.graphql_name)

      @processed_schema[:interface_types] << data
    elsif object < ::GraphQL::Schema::Enum
      data[:name] = object.graphql_name
      data[:description] = object.description

      data[:values] = object.values.values.map do |val|
        h = {}
        h[:notices] = @options[:notices].call([object.graphql_name, val.graphql_name].join('.'))
        h[:name] = val.graphql_name
        h[:description] = val.description
        unless val.deprecation_reason.nil?
          h[:is_deprecated] = true
          h[:deprecation_reason] = val.deprecation_reason
        end
        h
      end

      @processed_schema[:enum_types] << data
    elsif object < ::GraphQL::Schema::Union
      data[:name] = object.graphql_name
      data[:description] = object.description
      data[:possible_types] = object.possible_types.map(&:graphql_name).sort

      @processed_schema[:union_types] << data
    elsif object < GraphQL::Schema::InputObject
      data[:name] = object.graphql_name
      data[:description] = object.description

      data[:input_fields], = fetch_fields(object.arguments, object.graphql_name)

      @processed_schema[:input_object_types] << data
    elsif object < GraphQL::Schema::Scalar
      data[:name] = object.graphql_name
      data[:description] = object.description

      @processed_schema[:scalar_types] << data
    else
      raise TypeError, "I'm not sure what #{object.class} < #{object.superclass.name} is!"
    end
  end

  @schema.directives.each_value do |directive|
    data = {}
    data[:notices] = @options[:notices].call(directive.graphql_name)

    data[:name] = directive.graphql_name
    data[:description] = directive.description
    data[:locations] = directive.locations

    data[:arguments], = fetch_fields(directive.arguments, directive.graphql_name)

    @processed_schema[:directive_types] << data
  end

  sort_by_name!

  @processed_schema[:interface_types].each do |interface|
    interface[:implemented_by] = []
    @processed_schema[:object_types].each do |obj|
      interface[:implemented_by] << obj[:name] if obj[:interfaces].include?(interface[:name])
    end
  end

  @processed_schema
end

Private Instance Methods

argument?(field) click to toggle source
# File lib/graphql-docs/parser.rb, line 224
def argument?(field)
  field.is_a?(::GraphQL::Schema::Argument)
end
connection?(field) click to toggle source
# File lib/graphql-docs/parser.rb, line 228
def connection?(field)
  field.respond_to?(:connection?) && field.connection?
end
fetch_fields(object_fields, parent_path) click to toggle source
# File lib/graphql-docs/parser.rb, line 155
def fetch_fields(object_fields, parent_path)
  fields = []
  connections = []

  object_fields.each_value do |field|
    hash = {}

    hash[:notices] = @options[:notices].call([parent_path, field.graphql_name].join('.'))
    hash[:name] = field.graphql_name
    hash[:description] = field.description
    if field.respond_to?(:deprecation_reason) && !field.deprecation_reason.nil?
      hash[:is_deprecated] = true
      hash[:deprecation_reason] = field.deprecation_reason
    end

    hash[:type] = generate_type(field.type)

    hash[:arguments] = []
    if field.respond_to?(:arguments)
      field.arguments.each_value do |arg|
        h = {}
        h[:name] = arg.graphql_name
        h[:description] = arg.description
        h[:type] = generate_type(arg.type)
        h[:default_value] = arg.default_value if arg.default_value?
        hash[:arguments] << h
      end
    end

    if !argument?(field) && connection?(field)
      connections << hash
    else
      fields << hash
    end
  end

  [fields, connections]
end
generate_type(type) click to toggle source
# File lib/graphql-docs/parser.rb, line 194
def generate_type(type)
  name = type.unwrap.graphql_name

  path = if type.unwrap < GraphQL::Schema::Object
           if name == 'Query'
             'operation'
           else
             'object'
           end
         elsif type.unwrap < GraphQL::Schema::Scalar
           'scalar'
         elsif type.unwrap < GraphQL::Schema::Interface
           'interface'
         elsif type.unwrap < GraphQL::Schema::Enum
           'enum'
         elsif type.unwrap < GraphQL::Schema::InputObject
           'input_object'
         elsif type.unwrap < GraphQL::Schema::Union
           'union'
         else
           raise TypeError, "Unknown type for `#{name}`: `#{type.unwrap.class}`"
         end

  {
    name: name,
    path: "#{path}/#{slugify(name)}",
    info: type.graphql_definition
  }
end
sort_by_name!() click to toggle source
# File lib/graphql-docs/parser.rb, line 232
def sort_by_name!
  @processed_schema.each_pair do |key, value|
    next if value.empty?
    next if %i[operation_types root_types].include?(key)

    value.sort_by! { |o| o[:name] }
  end
end