From 23e188bee43493aae959c03e31a89905e390fa91 Mon Sep 17 00:00:00 2001 From: "Garen J. Torikian" Date: Mon, 24 Feb 2020 14:27:42 +0000 Subject: [PATCH 1/6] Bump GraphQL gem up --- graphql-docs.gemspec | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/graphql-docs.gemspec b/graphql-docs.gemspec index 50307291..461af854 100644 --- a/graphql-docs.gemspec +++ b/graphql-docs.gemspec @@ -1,6 +1,6 @@ -# coding: utf-8 # frozen_string_literal: true -lib = File.expand_path('../lib', __FILE__) + +lib = File.expand_path('lib', __dir__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'graphql-docs/version' @@ -21,14 +21,14 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.require_paths = ['lib'] - spec.add_dependency 'graphql', '~> 1.8' + spec.add_dependency 'graphql', '~> 1.10' # rendering spec.add_dependency 'commonmarker', '~> 0.16' - spec.add_dependency 'html-pipeline', '~> 2.9' - spec.add_dependency 'escape_utils', '~> 1.2' + spec.add_dependency 'escape_utils', '~> 1.2' spec.add_dependency 'extended-markdown-filter', '~> 0.4' spec.add_dependency 'gemoji', '~> 3.0' + spec.add_dependency 'html-pipeline', '~> 2.9' spec.add_dependency 'sass', '~> 3.4' spec.add_development_dependency 'awesome_print' From 6f29f4bdd8eec263c142cadf7610d852228b4bd3 Mon Sep 17 00:00:00 2001 From: "Garen J. Torikian" Date: Mon, 24 Feb 2020 16:43:21 +0000 Subject: [PATCH 2/6] Upgrade to new GQL gem --- lib/graphql-docs.rb | 38 +++---- lib/graphql-docs/parser.rb | 138 ++++++++++++------------- test/graphql-docs/generator_test.rb | 9 +- test/graphql-docs/graphql-docs_test.rb | 5 + test/graphql-docs/parser_test.rb | 27 ++--- test/test_helper.rb | 13 +++ 6 files changed, 110 insertions(+), 120 deletions(-) diff --git a/lib/graphql-docs.rb b/lib/graphql-docs.rb index d1a326c6..f772c4bf 100644 --- a/lib/graphql-docs.rb +++ b/lib/graphql-docs.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true + require 'graphql-docs/helpers' require 'graphql-docs/renderer' require 'graphql-docs/configuration' @@ -10,18 +11,15 @@ require 'awesome_print' require 'pry' rescue LoadError; end - module GraphQLDocs class << self def build(options) # do not let user provided values overwrite every single value - %i(templates landing_pages).each do |opt| - if options.key?(opt) - GraphQLDocs::Configuration::GRAPHQLDOCS_DEFAULTS[opt].each_pair do |key, value| - unless options[opt].key?(key) - options[opt][key] = value - end - end + %i[templates landing_pages].each do |opt| + next unless options.key?(opt) + + GraphQLDocs::Configuration::GRAPHQLDOCS_DEFAULTS[opt].each_pair do |key, value| + options[opt][key] = value unless options[opt].key?(key) end end @@ -30,28 +28,18 @@ def build(options) filename = options[:filename] schema = options[:schema] - if !filename.nil? && !schema.nil? - raise ArgumentError, 'Pass in `filename` or `schema`, but not both!' - end + raise ArgumentError, 'Pass in `filename` or `schema`, but not both!' if !filename.nil? && !schema.nil? - if filename.nil? && schema.nil? - raise ArgumentError, 'Pass in either `filename` or `schema`' - end + raise ArgumentError, 'Pass in either `filename` or `schema`' if filename.nil? && schema.nil? if filename - unless filename.is_a?(String) - raise TypeError, "Expected `String`, got `#{filename.class}`" - end + raise TypeError, "Expected `String`, got `#{filename.class}`" unless filename.is_a?(String) - unless File.exist?(filename) - raise ArgumentError, "#{filename} does not exist!" - end + raise ArgumentError, "#{filename} does not exist!" unless File.exist?(filename) schema = File.read(filename) else - if !schema.is_a?(String) && !schema.is_a?(GraphQL::Schema) - raise TypeError, "Expected `String` or `GraphQL::Schema`, got `#{schema.class}`" - end + raise TypeError, "Expected `String` or `GraphQL::Schema`, got `#{schema.class}`" if !schema.is_a?(String) && !schema_type?(schema) schema = schema end @@ -63,5 +51,9 @@ def build(options) generator.generate end + + private def schema_type?(object) + object.respond_to?(:ancestors) && object.ancestors.include?(GraphQL::Schema) + end end end diff --git a/lib/graphql-docs/parser.rb b/lib/graphql-docs/parser.rb index bedd1fcd..aa1d2b0f 100644 --- a/lib/graphql-docs/parser.rb +++ b/lib/graphql-docs/parser.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true + require 'graphql' module GraphQLDocs @@ -10,13 +11,13 @@ class Parser def initialize(schema, options) @options = options - @options[:notices] ||= -> (schema_member_path) { [] } + @options[:notices] ||= ->(_schema_member_path) { [] } - if schema.is_a?(GraphQL::Schema) - @schema = schema - else - @schema = GraphQL::Schema.from_definition(schema) - end + @schema = if schema.is_a?(String) + GraphQL::Schema.from_definition(schema) + elsif schema < GraphQL::Schema + schema + end @processed_schema = { operation_types: [], @@ -27,80 +28,71 @@ def initialize(schema, options) union_types: [], input_object_types: [], scalar_types: [], - directive_types: [], + directive_types: [] } end def parse root_types = {} - ['query', 'mutation'].each do |operation| - unless @schema.root_type_for_operation(operation).nil? - root_types[operation] = @schema.root_type_for_operation(operation).name - end + %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.name) + data[:notices] = @options[:notices].call(object.graphql_name) - case object - when ::GraphQL::ObjectType - if object.name == root_types['query'] - data[:name] = object.name - data[:description] = object.description + if object < ::GraphQL::Schema::Object + data[:name] = object.graphql_name + data[:description] = object.description - data[:interfaces] = object.interfaces.map(&:name).sort - data[:fields], data[:connections] = fetch_fields(object.fields, object.name) + 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 object.name == root_types['mutation'] - data[:name] = object.name - data[:description] = object.description - + elsif data[:name] == root_types['mutation'] @processed_schema[:operation_types] << data object.fields.each_value do |mutation| h = {} - h[:notices] = @options[:notices].call([object.name, mutation.name].join('.')) - h[:name] = mutation.name + 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.name, mutation.name].join('.')) + 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.name) + 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.name}" => mutation }, return_type.name) + h[:return_fields], = fetch_fields({ return_type.graphql_name => mutation }, return_type.graphql_name) end @processed_schema[:mutation_types] << h end else - data[:name] = object.name - data[:description] = object.description - - data[:interfaces] = object.interfaces.map(&:name).sort - data[:fields], data[:connections] = fetch_fields(object.fields, object.name) + 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 - when ::GraphQL::InterfaceType - data[:name] = object.name + elsif object < ::GraphQL::Schema::Interface + data[:name] = object.graphql_name data[:description] = object.description - data[:fields], data[:connections] = fetch_fields(object.fields, object.name) + data[:fields], data[:connections] = fetch_fields(object.fields, object.graphql_name) @processed_schema[:interface_types] << data - when ::GraphQL::EnumType - data[:name] = object.name + 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.name, val.name].join('.')) - h[:name] = val.name + 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 @@ -110,38 +102,38 @@ def parse end @processed_schema[:enum_types] << data - when ::GraphQL::UnionType - data[:name] = object.name + elsif object < ::GraphQL::Schema::Union + data[:name] = object.graphql_name data[:description] = object.description - data[:possible_types] = object.possible_types.map(&:name).sort + data[:possible_types] = object.possible_types.map(&:graphql_name).sort @processed_schema[:union_types] << data - when ::GraphQL::InputObjectType - data[:name] = object.name + elsif object < GraphQL::Schema::InputObject + data[:name] = object.graphql_name data[:description] = object.description - data[:input_fields], _ = fetch_fields(object.input_fields, object.name) + data[:input_fields], = fetch_fields(object.arguments, object.graphql_name) @processed_schema[:input_object_types] << data - when ::GraphQL::ScalarType - data[:name] = object.name + 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} is!" + 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.name) + data[:notices] = @options[:notices].call(directive.graphql_name) - data[:name] = directive.name + data[:name] = directive.graphql_name data[:description] = directive.description data[:locations] = directive.locations - data[:arguments], _ = fetch_fields(directive.arguments, directive.name) + data[:arguments], = fetch_fields(directive.arguments, directive.graphql_name) @processed_schema[:directive_types] << data end @@ -151,9 +143,7 @@ def parse @processed_schema[:interface_types].each do |interface| interface[:implemented_by] = [] @processed_schema[:object_types].each do |obj| - if obj[:interfaces].include?(interface[:name]) - interface[:implemented_by] << obj[:name] - end + interface[:implemented_by] << obj[:name] if obj[:interfaces].include?(interface[:name]) end end @@ -169,8 +159,8 @@ def fetch_fields(object_fields, parent_path) object_fields.each_value do |field| hash = {} - hash[:notices] = @options[:notices].call([parent_path, field.name].join('.')) - hash[:name] = field.name + 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 @@ -183,17 +173,15 @@ def fetch_fields(object_fields, parent_path) if field.respond_to?(:arguments) field.arguments.each_value do |arg| h = {} - h[:name] = arg.name + h[:name] = arg.graphql_name h[:description] = arg.description h[:type] = generate_type(arg.type) - if arg.default_value? - h[:default_value] = arg.default_value - end + h[:default_value] = arg.default_value if arg.default_value? hash[:arguments] << h end end - if !argument?(field) && field.connection? + if !argument?(field) && connection?(field) connections << hash else fields << hash @@ -204,26 +192,26 @@ def fetch_fields(object_fields, parent_path) end def generate_type(type) - name = type.unwrap.to_s - path = case type.unwrap - when ::GraphQL::ObjectType + name = type.unwrap.graphql_name + + path = if type.unwrap < GraphQL::Schema::Object if name == 'Query' 'operation' else 'object' end - when ::GraphQL::ScalarType + elsif type.unwrap < GraphQL::Schema::Scalar 'scalar' - when ::GraphQL::InterfaceType + elsif type.unwrap < GraphQL::Schema::Interface 'interface' - when ::GraphQL::EnumType + elsif type.unwrap < GraphQL::Schema::Enum 'enum' - when ::GraphQL::InputObjectType + elsif type.unwrap < GraphQL::Schema::InputObject 'input_object' - when ::GraphQL::UnionType + elsif type.unwrap < GraphQL::Schema::Union 'union' else - raise TypeError, "Unknown type: `#{type.unwrap.class}`" + raise TypeError, "Unknown type for `#{name}`: `#{type.unwrap.class}`" end { @@ -234,12 +222,18 @@ def generate_type(type) end def argument?(field) - field.is_a?(::GraphQL::Argument) + field.is_a?(::GraphQL::Schema::Argument) + end + + def connection?(field) + field.respond_to?(:connection?) && field.connection? end def sort_by_name! @processed_schema.each_pair do |key, value| + next if value.empty? next if key == :operation_types || key == :root_types + value.sort_by! { |o| o[:name] } end end diff --git a/test/graphql-docs/generator_test.rb b/test/graphql-docs/generator_test.rb index d5dbcc6b..ced278bd 100644 --- a/test/graphql-docs/generator_test.rb +++ b/test/graphql-docs/generator_test.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true + require 'test_helper' class GeneratorTest < Minitest::Test class CustomRenderer - def initialize(_, _) - end + def initialize(_, _); end def render(contents, type: nil, name: nil, filename: nil) to_html(contents) @@ -12,6 +12,7 @@ def render(contents, type: nil, name: nil, filename: nil) def to_html(contents) return '' if contents.nil? + contents.sub(/CodeOfConduct/i, 'CoC!!!!!') end end @@ -191,7 +192,7 @@ def test_that_markdown_preserves_whitespace contents = File.read File.join(@output_dir, 'index.html') - assert_match %r{ "nest2": \{}, contents + assert_match(/ "nest2": \{/, contents) end def test_that_empty_html_lines_not_interpreted_by_markdown @@ -203,7 +204,7 @@ def test_that_empty_html_lines_not_interpreted_by_markdown contents = File.read File.join(@output_dir, 'operation', 'query', 'index.html') - assert_match %r{\s+

The code of conduct's key

\s+}, contents + assert_match(%r{\s+

The code of conduct's key

\s+}, contents) end def test_that_non_empty_html_lines_not_interpreted_by_markdown diff --git a/test/graphql-docs/graphql-docs_test.rb b/test/graphql-docs/graphql-docs_test.rb index 28b6d3db..176216b0 100644 --- a/test/graphql-docs/graphql-docs_test.rb +++ b/test/graphql-docs/graphql-docs_test.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true + require 'test_helper' class GraphQLDocsTest < Minitest::Test @@ -33,4 +34,8 @@ def test_it_needs_one_or_the_other GraphQLDocs.build end end + + def test_it_accepts_class_schema + GraphQLDocs.build(schema: MySchema) + end end diff --git a/test/graphql-docs/parser_test.rb b/test/graphql-docs/parser_test.rb index 83490acb..38cab514 100644 --- a/test/graphql-docs/parser_test.rb +++ b/test/graphql-docs/parser_test.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true + require 'test_helper' class ParserTest < Minitest::Test @@ -10,27 +11,11 @@ def setup end def test_it_accepts_schema_class - query_type = GraphQL::ObjectType.define do - name 'Query' - - field :test do - type types.Int - description "Title paragraph. - ``` - line1 - line2 - line3 - ```" - end - end - - schema = GraphQL::Schema.define do - query query_type - end + schema = MySchema results = GraphQLDocs::Parser.new(schema, {}).parse assert_equal 'test', results[:operation_types][0][:fields][0][:name] - assert_equal "Title paragraph.\n ```\n line1\n line2\n line3\n ```", results[:operation_types][0][:fields][0][:description] + assert_equal "Title paragraph.\n ```\n line1\n line2\n line3\n ```", results[:operation_types][0][:fields][0][:description] end def test_types_are_sorted @@ -61,10 +46,10 @@ def test_groups_items_by_type def test_directives names = @gh_results[:directive_types].map { |t| t[:name] } - assert_equal %w(deprecated include preview skip), names + assert_equal %w[deprecated include preview skip], names - preview_directive = @gh_results[:directive_types].find { |t| t[:name] == 'deprecated' } - assert_equal %i(FIELD_DEFINITION ENUM_VALUE), preview_directive[:locations] + preview_directive = @gh_results[:directive_types].find { |t| t[:name] == 'deprecated' } + assert_equal %i[FIELD_DEFINITION ENUM_VALUE], preview_directive[:locations] assert_equal 'Marks an element of a GraphQL schema as no longer supported.', preview_directive[:description] reason_arg = preview_directive[:arguments].first diff --git a/test/test_helper.rb b/test/test_helper.rb index ed75626f..30c8cc5f 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -12,3 +12,16 @@ def fixtures_dir end FileUtils.rm_rf(File.join(fixtures_dir, 'output')) + +class QueryType < GraphQL::Schema::Object + field :test, Int, "Title paragraph. + ``` + line1 + line2 + line3 + ```", null: false +end + +class MySchema < GraphQL::Schema + query QueryType +end From b49e5ed109a1ae306a83fa8b25021c2e3f658fbb Mon Sep 17 00:00:00 2001 From: "Garen J. Torikian" Date: Mon, 24 Feb 2020 16:46:22 +0000 Subject: [PATCH 3/6] Rubocop --- Gemfile | 1 + Rakefile | 3 +- lib/graphql-docs.rb | 2 +- lib/graphql-docs/configuration.rb | 11 ++-- lib/graphql-docs/generator.rb | 84 +++++++++++------------------ lib/graphql-docs/helpers.rb | 24 ++++----- lib/graphql-docs/parser.rb | 2 +- lib/graphql-docs/renderer.rb | 16 +++--- lib/graphql-docs/version.rb | 3 +- test/graphql-docs/generator_test.rb | 12 ++--- test/graphql-docs/renderer_test.rb | 54 +++++++++---------- test/test_helper.rb | 3 +- 12 files changed, 99 insertions(+), 116 deletions(-) diff --git a/Gemfile b/Gemfile index 9c75612b..664950e0 100644 --- a/Gemfile +++ b/Gemfile @@ -1,4 +1,5 @@ # frozen_string_literal: true + source 'https://rubygems.org' # Specify your gem's dependencies in graphql-docs.gemspec diff --git a/Rakefile b/Rakefile index 794437d7..b52d61b9 100644 --- a/Rakefile +++ b/Rakefile @@ -1,4 +1,5 @@ # frozen_string_literal: true + require 'bundler/gem_tasks' require 'rake/testtask' @@ -31,7 +32,7 @@ task :console do require 'graphql-docs' def reload! - files = $LOADED_FEATURES.select { |feat| feat =~ /\/graphql-docs\// } + files = $LOADED_FEATURES.select { |feat| feat =~ %r{/graphql-docs/} } files.each { |file| load file } end diff --git a/lib/graphql-docs.rb b/lib/graphql-docs.rb index f772c4bf..96cc5726 100644 --- a/lib/graphql-docs.rb +++ b/lib/graphql-docs.rb @@ -10,7 +10,7 @@ begin require 'awesome_print' require 'pry' -rescue LoadError; end +rescue LoadError; end # rubocop:disable Lint/SuppressedException module GraphQLDocs class << self def build(options) diff --git a/lib/graphql-docs/configuration.rb b/lib/graphql-docs/configuration.rb index 2120deb7..e0441977 100644 --- a/lib/graphql-docs/configuration.rb +++ b/lib/graphql-docs/configuration.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true + module GraphQLDocs module Configuration GRAPHQLDOCS_DEFAULTS = { @@ -11,9 +12,9 @@ module Configuration output_dir: './output/', pipeline_config: { pipeline: - %i(ExtendedMarkdownFilter - EmojiFilter - TableOfContentsFilter), + %i[ExtendedMarkdownFilter + EmojiFilter + TableOfContentsFilter], context: { gfm: false, unsafe: true, # necessary for layout needs, given that it's all HTML templates @@ -37,7 +38,7 @@ module Configuration unions: "#{File.dirname(__FILE__)}/layouts/graphql_unions.html", input_objects: "#{File.dirname(__FILE__)}/layouts/graphql_input_objects.html", scalars: "#{File.dirname(__FILE__)}/layouts/graphql_scalars.html", - directives: "#{File.dirname(__FILE__)}/layouts/graphql_directives.html", + directives: "#{File.dirname(__FILE__)}/layouts/graphql_directives.html" }, landing_pages: { @@ -59,7 +60,7 @@ module Configuration field_entry: '', deprecation_notice: '', notice: '', - notice_title: '', + notice_title: '' } }.freeze end diff --git a/lib/graphql-docs/generator.rb b/lib/graphql-docs/generator.rb index 2f90e8bb..906334c1 100644 --- a/lib/graphql-docs/generator.rb +++ b/lib/graphql-docs/generator.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true + require 'erb' require 'sass' @@ -14,14 +15,13 @@ def initialize(parsed_schema, options) @renderer = @options[:renderer].new(@parsed_schema, @options) - %i(operations objects mutations interfaces enums unions input_objects scalars directives).each do |sym| - if !File.exist?(@options[:templates][sym]) - raise IOError, "`#{sym}` template #{@options[:templates][sym]} was not found" - end + %i[operations objects mutations interfaces enums unions input_objects scalars directives].each do |sym| + raise IOError, "`#{sym}` template #{@options[:templates][sym]} was not found" unless File.exist?(@options[:templates][sym]) + instance_variable_set("@graphql_#{sym}_template", ERB.new(File.read(@options[:templates][sym]))) end - %i(index object query mutation interface enum union input_object scalar directive).each do |sym| + %i[index object query mutation interface enum union input_object scalar directive].each do |sym| if @options[:landing_pages][sym].nil? instance_variable_set("@#{sym}_landing_page", nil) elsif !File.exist?(@options[:landing_pages][sym]) @@ -33,7 +33,7 @@ def initialize(parsed_schema, options) if File.extname((@options[:landing_pages][sym])) == '.erb' opts = @options.merge(@options[:landing_pages][:variables]).merge(helper_methods) - if has_yaml?(landing_page_contents) + if yaml?(landing_page_contents) metadata, landing_page = split_into_metadata_and_contents(landing_page_contents, parse: false) erb_template = ERB.new(landing_page) else @@ -60,45 +60,25 @@ def generate create_graphql_scalar_pages create_graphql_directive_pages - unless @graphql_index_landing_page.nil? - write_file('static', 'index', @graphql_index_landing_page, trim: false) - end + write_file('static', 'index', @graphql_index_landing_page, trim: false) unless @graphql_index_landing_page.nil? - unless @graphql_object_landing_page.nil? - write_file('static', 'object', @graphql_object_landing_page, trim: false) - end + write_file('static', 'object', @graphql_object_landing_page, trim: false) unless @graphql_object_landing_page.nil? - if !@graphql_query_landing_page.nil? && !has_query - write_file('operation', 'query', @graphql_query_landing_page, trim: false) - end + write_file('operation', 'query', @graphql_query_landing_page, trim: false) if !@graphql_query_landing_page.nil? && !has_query - unless @graphql_mutation_landing_page.nil? - write_file('operation', 'mutation', @graphql_mutation_landing_page, trim: false) - end + write_file('operation', 'mutation', @graphql_mutation_landing_page, trim: false) unless @graphql_mutation_landing_page.nil? - unless @graphql_interface_landing_page.nil? - write_file('static', 'interface', @graphql_interface_landing_page, trim: false) - end + write_file('static', 'interface', @graphql_interface_landing_page, trim: false) unless @graphql_interface_landing_page.nil? - unless @graphql_enum_landing_page.nil? - write_file('static', 'enum', @graphql_enum_landing_page, trim: false) - end + write_file('static', 'enum', @graphql_enum_landing_page, trim: false) unless @graphql_enum_landing_page.nil? - unless @graphql_union_landing_page.nil? - write_file('static', 'union', @graphql_union_landing_page, trim: false) - end + write_file('static', 'union', @graphql_union_landing_page, trim: false) unless @graphql_union_landing_page.nil? - unless @graphql_input_object_landing_page.nil? - write_file('static', 'input_object', @graphql_input_object_landing_page, trim: false) - end + write_file('static', 'input_object', @graphql_input_object_landing_page, trim: false) unless @graphql_input_object_landing_page.nil? - unless @graphql_scalar_landing_page.nil? - write_file('static', 'scalar', @graphql_scalar_landing_page, trim: false) - end + write_file('static', 'scalar', @graphql_scalar_landing_page, trim: false) unless @graphql_scalar_landing_page.nil? - unless @graphql_directive_landing_page.nil? - write_file('static', 'directive', @graphql_directive_landing_page, trim: false) - end + write_file('static', 'directive', @graphql_directive_landing_page, trim: false) unless @graphql_directive_landing_page.nil? if @options[:use_default_styles] assets_dir = File.join(File.dirname(__FILE__), 'layouts', 'assets') @@ -117,23 +97,23 @@ def generate def create_graphql_query_pages graphql_operation_types.each do |query_type| metadata = '' - if query_type[:name] == graphql_root_types['query'] - unless @options[:landing_pages][:query].nil? - query_landing_page = @options[:landing_pages][:query] - query_landing_page = File.read(query_landing_page) - if has_yaml?(query_landing_page) - pieces = yaml_split(query_landing_page) - pieces[2] = pieces[2].chomp - metadata = pieces[1, 3].join("\n") - query_landing_page = pieces[4] - end - query_type[:description] = query_landing_page + next unless query_type[:name] == graphql_root_types['query'] + + unless @options[:landing_pages][:query].nil? + query_landing_page = @options[:landing_pages][:query] + query_landing_page = File.read(query_landing_page) + if yaml?(query_landing_page) + pieces = yaml_split(query_landing_page) + pieces[2] = pieces[2].chomp + metadata = pieces[1, 3].join("\n") + query_landing_page = pieces[4] end - opts = default_generator_options(type: query_type) - contents = @graphql_operations_template.result(OpenStruct.new(opts).instance_eval { binding }) - write_file('operation', 'query', metadata + contents) - return true + query_type[:description] = query_landing_page end + opts = default_generator_options(type: query_type) + contents = @graphql_operations_template.result(OpenStruct.new(opts).instance_eval { binding }) + write_file('operation', 'query', metadata + contents) + return true end false end @@ -229,7 +209,7 @@ def write_file(type, name, contents, trim: true) FileUtils.mkdir_p(path) end - if has_yaml?(contents) + if yaml?(contents) # Split data meta, contents = split_into_metadata_and_contents(contents) @options = @options.merge(meta) diff --git a/lib/graphql-docs/helpers.rb b/lib/graphql-docs/helpers.rb index 3819ef07..3637278b 100644 --- a/lib/graphql-docs/helpers.rb +++ b/lib/graphql-docs/helpers.rb @@ -10,7 +10,7 @@ module Helpers def slugify(str) slug = str.gsub(SLUGIFY_PRETTY_REGEXP, '-') - slug.gsub!(%r!^\-|\-$!i, '') + slug.gsub!(/^\-|\-$/i, '') slug.downcase end @@ -22,6 +22,7 @@ def include(filename, opts = {}) def markdownify(string) return '' if string.nil? + type = @options[:pipeline_config][:context][:unsafe] ? :UNSAFE : :DEFAULT ::CommonMarker.render_html(string, type).strip end @@ -67,27 +68,23 @@ def graphql_directive_types end def split_into_metadata_and_contents(contents, parse: true) - opts = {} pieces = yaml_split(contents) - if pieces.size < 4 - raise RuntimeError.new( - "The file '#{content_filename}' appears to start with a metadata section (three or five dashes at the top) but it does not seem to be in the correct format.", - ) - end + raise "The file '#{content_filename}' appears to start with a metadata section (three or five dashes at the top) but it does not seem to be in the correct format." if pieces.size < 4 + # Parse begin - if parse - meta = YAML.load(pieces[2]) || {} - else - meta = pieces[2] - end + meta = if parse + YAML.safe_load(pieces[2]) || {} + else + pieces[2] + end rescue Exception => e # rubocop:disable Lint/RescueException raise "Could not parse YAML for #{name}: #{e.message}" end [meta, pieces[4]] end - def has_yaml?(contents) + def yaml?(contents) contents =~ /\A-{3,5}\s*$/ end @@ -114,6 +111,7 @@ def helper_methods Helpers.instance_methods.each do |name| next if name == :helper_methods + @helper_methods[name] = method(name) end diff --git a/lib/graphql-docs/parser.rb b/lib/graphql-docs/parser.rb index aa1d2b0f..d4dce81d 100644 --- a/lib/graphql-docs/parser.rb +++ b/lib/graphql-docs/parser.rb @@ -232,7 +232,7 @@ def connection?(field) def sort_by_name! @processed_schema.each_pair do |key, value| next if value.empty? - next if key == :operation_types || key == :root_types + next if %i[operation_types root_types].include?(key) value.sort_by! { |o| o[:name] } end diff --git a/lib/graphql-docs/renderer.rb b/lib/graphql-docs/renderer.rb index fa10cd66..b8cf52c3 100644 --- a/lib/graphql-docs/renderer.rb +++ b/lib/graphql-docs/renderer.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true + require 'html/pipeline' require 'yaml' require 'extended-markdown-filter' @@ -13,9 +14,7 @@ def initialize(parsed_schema, options) @parsed_schema = parsed_schema @options = options - unless @options[:templates][:default].nil? - @graphql_default_layout = ERB.new(File.read(@options[:templates][:default])) - end + @graphql_default_layout = ERB.new(File.read(@options[:templates][:default])) unless @options[:templates][:default].nil? @pipeline_config = @options[:pipeline_config] || {} pipeline = @pipeline_config[:pipeline] || {} @@ -40,10 +39,11 @@ def initialize(parsed_schema, options) end def render(contents, type: nil, name: nil, filename: nil) - opts = { base_url: @options[:base_url], output_dir: @options[:output_dir] }.merge({ type: type, name: name, filename: filename}).merge(helper_methods) + opts = { base_url: @options[:base_url], output_dir: @options[:output_dir] }.merge({ type: type, name: name, filename: filename }).merge(helper_methods) contents = to_html(contents, context: { filename: filename }) return contents if @graphql_default_layout.nil? + opts[:content] = contents @graphql_default_layout.result(OpenStruct.new(opts).instance_eval { binding }) end @@ -54,12 +54,12 @@ def to_html(string, context: {}) private - def filter_key(s) - s.downcase + def filter_key(str) + str.downcase end - def filter?(f) - f < HTML::Pipeline::Filter + def filter?(filter) + filter < HTML::Pipeline::Filter rescue LoadError, ArgumentError false end diff --git a/lib/graphql-docs/version.rb b/lib/graphql-docs/version.rb index bcc654ec..b0322c96 100644 --- a/lib/graphql-docs/version.rb +++ b/lib/graphql-docs/version.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true + module GraphQLDocs - VERSION = '1.9.1'.freeze + VERSION = '1.9.1' end diff --git a/test/graphql-docs/generator_test.rb b/test/graphql-docs/generator_test.rb index ced278bd..e93a1632 100644 --- a/test/graphql-docs/generator_test.rb +++ b/test/graphql-docs/generator_test.rb @@ -6,7 +6,7 @@ class GeneratorTest < Minitest::Test class CustomRenderer def initialize(_, _); end - def render(contents, type: nil, name: nil, filename: nil) + def render(contents) to_html(contents) end @@ -119,7 +119,7 @@ def test_that_custom_renderer_can_be_used contents = File.read(File.join(@output_dir, 'object', 'codeofconduct', 'index.html')) - assert_match /CoC!!!!!/, contents + assert_match(/CoC!!!!!/, contents) end def test_that_it_sets_classes @@ -133,7 +133,7 @@ def test_that_it_sets_classes object = File.read File.join(@output_dir, 'object', 'codeofconduct', 'index.html') - assert_match /
/, object + assert_match(/
/, object) end def test_that_named_query_root_generates_fields @@ -145,7 +145,7 @@ def test_that_named_query_root_generates_fields object = File.read File.join(@output_dir, 'operation', 'query', 'index.html') - assert_match /Do a thing/, object + assert_match(/Do a thing/, object) end def test_that_missing_landing_pages_are_reported @@ -168,8 +168,8 @@ def test_that_erb_landing_pages_work object = File.read File.join(@output_dir, 'object', 'index.html') - assert_match /Variable Objects/, object - assert_match /wowie!!/, object + assert_match(/Variable Objects/, object) + assert_match(/wowie!!/, object) end def test_that_broken_yaml_is_caught diff --git a/test/graphql-docs/renderer_test.rb b/test/graphql-docs/renderer_test.rb index 7e366b40..fc200e36 100644 --- a/test/graphql-docs/renderer_test.rb +++ b/test/graphql-docs/renderer_test.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true + require 'test_helper' class RendererTest < Minitest::Test - def setup @swapi = File.read(File.join(fixtures_dir, 'sw-schema.graphql')) @parsed_schema = GraphQLDocs::Parser.new(@swapi, {}).parse @@ -29,18 +29,18 @@ def test_that_unsafe_html_is_not_blocked_by_default def test_that_unsafe_html_is_blocked_when_asked renderer = GraphQLDocs::Renderer.new(@parsed_schema, GraphQLDocs::Configuration::GRAPHQLDOCS_DEFAULTS.merge({ - pipeline_config: { - pipeline: - %i(ExtendedMarkdownFilter - EmojiFilter - TableOfContentsFilter), - context: { - gfm: false, - unsafe: false, - asset_root: 'https://a248.e.akamai.net/assets.github.com/images/icons' - } - } - })) + pipeline_config: { + pipeline: + %i[ExtendedMarkdownFilter + EmojiFilter + TableOfContentsFilter], + context: { + gfm: false, + unsafe: false, + asset_root: 'https://a248.e.akamai.net/assets.github.com/images/icons' + } + } + })) contents = renderer.to_html('Oh **hello**') assert_equal '

Oh hello

', contents @@ -48,19 +48,19 @@ def test_that_unsafe_html_is_blocked_when_asked def test_that_filename_accessible_to_filters renderer = GraphQLDocs::Renderer.new(@parsed_schema, GraphQLDocs::Configuration::GRAPHQLDOCS_DEFAULTS.merge({ - pipeline_config: { - pipeline: - %i(ExtendedMarkdownFilter - EmojiFilter - TableOfContentsFilter - AddFilenameFilter), - context: { - gfm: false, - unsafe: true, - asset_root: 'https://a248.e.akamai.net/assets.github.com/images/icons' - } - } - })) + pipeline_config: { + pipeline: + %i[ExtendedMarkdownFilter + EmojiFilter + TableOfContentsFilter + AddFilenameFilter], + context: { + gfm: false, + unsafe: true, + asset_root: 'https://a248.e.akamai.net/assets.github.com/images/icons' + } + } + })) contents = renderer.render('', type: 'Droid', name: 'R2D2', filename: '/this/is/the/filename') assert_match %r{/this/is/the/filename}, contents end @@ -69,7 +69,7 @@ def test_that_filename_accessible_to_filters class AddFilenameFilter < HTML::Pipeline::Filter def call doc.search('span[@id="fill-me"]').each do |span| - span.inner_html=(context[:filename]) + span.inner_html = context[:filename] end doc end diff --git a/test/test_helper.rb b/test/test_helper.rb index 30c8cc5f..518ca7bd 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true -$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) + +$LOAD_PATH.unshift File.expand_path('../lib', __dir__) require 'graphql-docs' require 'minitest/autorun' From 69a02c48ae9909336cfbf5082ea8633222fe001c Mon Sep 17 00:00:00 2001 From: "Garen J. Torikian" Date: Mon, 24 Feb 2020 17:21:52 +0000 Subject: [PATCH 4/6] Correct type --- Rakefile | 3 ++- lib/graphql-docs/parser.rb | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Rakefile b/Rakefile index b52d61b9..724df680 100644 --- a/Rakefile +++ b/Rakefile @@ -47,7 +47,7 @@ task :generate_sample do options = {} options[:delete_output] = true - options[:base_url] = '/graphql-docs' + options[:base_url] = ENV.fetch('GQL_DOCS_BASE_URL', '') options[:filename] = File.join(File.dirname(__FILE__), 'test', 'graphql-docs', 'fixtures', 'gh-schema.graphql') GraphQLDocs.build(options) @@ -67,6 +67,7 @@ end desc 'Generate and publish docs to gh-pages' task :publish do + ENV['GQL_DOCS_BASE_URL'] = '/graphql-docs' Rake::Task[:generate_sample].invoke('https://www.gjtorikian.com/graphql-docs') Dir.mktmpdir do |tmp| system "mv output/* #{tmp}" diff --git a/lib/graphql-docs/parser.rb b/lib/graphql-docs/parser.rb index d4dce81d..7f7e4a6f 100644 --- a/lib/graphql-docs/parser.rb +++ b/lib/graphql-docs/parser.rb @@ -216,8 +216,8 @@ def generate_type(type) { name: name, - path: path + '/' + slugify(name), - info: type.to_s + path: "#{path}/#{slugify(name)}", + info: type.graphql_definition } end From c1482cb5e54e54049c8f9c183e8396719e78fc83 Mon Sep 17 00:00:00 2001 From: "Garen J. Torikian" Date: Mon, 24 Feb 2020 17:23:27 +0000 Subject: [PATCH 5/6] Update Travis rubies --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 909ed38f..8adf0726 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,10 +3,10 @@ language: ruby cache: bundler rvm: - - 2.3.6 - - 2.4.3 - - 2.5.0 - - 2.6.0 + - 2.4.9 + - 2.5.7 + - 2.6.5 + - 2.7.0 git: depth: 10 From fe9e28880f382e4ebbb44a6ff7a32ee8bc643225 Mon Sep 17 00:00:00 2001 From: "Garen J. Torikian" Date: Mon, 24 Feb 2020 17:31:09 +0000 Subject: [PATCH 6/6] hush Rubocop --- test/graphql-docs/generator_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/graphql-docs/generator_test.rb b/test/graphql-docs/generator_test.rb index e93a1632..f42704ad 100644 --- a/test/graphql-docs/generator_test.rb +++ b/test/graphql-docs/generator_test.rb @@ -6,7 +6,7 @@ class GeneratorTest < Minitest::Test class CustomRenderer def initialize(_, _); end - def render(contents) + def render(contents, type: nil, name: nil, filename: nil) # rubocop:disable Lint/UnusedMethodArgument to_html(contents) end