Commit 1af3cb8f authored by Alex Kalderimis's avatar Alex Kalderimis

Add mutation result fields to the output

This ensures that we render the mutation result fields.

It also adds tests for input type rendering.
parent 8469b536
...@@ -28,6 +28,7 @@ module Gitlab ...@@ -28,6 +28,7 @@ module Gitlab
# You can check the included module on: https://github.com/gjtorikian/graphql-docs/blob/v1.6.0/lib/graphql-docs/helpers.rb # You can check the included module on: https://github.com/gjtorikian/graphql-docs/blob/v1.6.0/lib/graphql-docs/helpers.rb
module Helper module Helper
include GraphQLDocs::Helpers include GraphQLDocs::Helpers
include Gitlab::Utils::StrongMemoize
def auto_generated_comment def auto_generated_comment
<<-MD.strip_heredoc <<-MD.strip_heredoc
...@@ -58,7 +59,8 @@ module Gitlab ...@@ -58,7 +59,8 @@ module Gitlab
render_return_type(field), render_return_type(field),
render_input_type(field), render_input_type(field),
render_connection_note(field), render_connection_note(field),
render_argument_table(heading_level, args, arg_owner) render_argument_table(heading_level, args, arg_owner),
render_return_fields(field, owner: owner)
].compact.join("\n\n") ].compact.join("\n\n")
end end
...@@ -87,7 +89,7 @@ module Gitlab ...@@ -87,7 +89,7 @@ module Gitlab
end end
def render_object_fields(fields, owner:, level_bump: 0) def render_object_fields(fields, owner:, level_bump: 0)
return if fields.empty? return if fields.blank?
(with_args, no_args) = fields.partition { |f| args?(f) } (with_args, no_args) = fields.partition { |f| args?(f) }
type_name = owner[:name] if owner type_name = owner[:name] if owner
...@@ -124,6 +126,8 @@ module Gitlab ...@@ -124,6 +126,8 @@ module Gitlab
end end
def render_return_type(query) def render_return_type(query)
return unless query[:type] # for example, mutations
"Returns #{render_field_type(query[:type])}." "Returns #{render_field_type(query[:type])}."
end end
...@@ -158,7 +162,7 @@ module Gitlab ...@@ -158,7 +162,7 @@ module Gitlab
end end
def object_types def object_types
objects.reject { |t| t[:edge] || t[:connection] } objects.reject { |t| t[:edge] || t[:connection] || t[:payload] }
end end
def interfaces def interfaces
...@@ -191,7 +195,7 @@ module Gitlab ...@@ -191,7 +195,7 @@ module Gitlab
arguments = input_type[:input_fields] arguments = input_type[:input_fields]
seen_type(input_type_name) seen_type(input_type_name)
t.merge(arguments: arguments, fields: t[:return_fields]) t.merge(arguments: arguments)
end end
end end
...@@ -213,21 +217,35 @@ module Gitlab ...@@ -213,21 +217,35 @@ module Gitlab
# We are ignoring connections and built in types for now, # We are ignoring connections and built in types for now,
# they should be added when queries are generated. # they should be added when queries are generated.
def objects def objects
@objects ||= graphql_object_types strong_memoize(:objects) do
.reject { |object_type| object_type[:name]["__"] } # We ignore introspection types. mutations = schema.mutation&.fields&.keys&.to_set || []
.map do |type|
type.merge( graphql_object_types
edge: type[:name].ends_with?('Edge'), .reject { |object_type| object_type[:name]["__"] } # We ignore introspection types.
connection: type[:name].ends_with?('Connection'), .map do |type|
fields: type[:fields] + type[:connections] name = type[:name]
) type.merge(
end edge: name.ends_with?('Edge'),
connection: name.ends_with?('Connection'),
payload: name.ends_with?('Payload') && mutations.include?(name.chomp('Payload').camelcase(:lower)),
fields: type[:fields] + type[:connections]
)
end
end
end end
private # DO NOT CALL THESE METHODS IN TEMPLATES private # DO NOT CALL THESE METHODS IN TEMPLATES
# Template methods # Template methods
def render_return_fields(mutation, owner:)
fields = mutation[:return_fields]
return if fields.blank?
name = owner.to_s + mutation[:name]
render_object_fields(fields, owner: { name: name })
end
def render_connection_note(field) def render_connection_note(field)
return unless connection?(field) return unless connection?(field)
......
...@@ -206,7 +206,7 @@ ...@@ -206,7 +206,7 @@
\ \
:plain :plain
### Input types ## Input types
Types that may be used as arguments. (All scalar types may also Types that may be used as arguments. (All scalar types may also
be used as arguments). be used as arguments).
......
# frozen_string_literal: true # frozen_string_literal: true
require 'fast_spec_helper' require 'spec_helper'
RSpec.describe Gitlab::Graphql::Docs::Renderer do RSpec.describe Gitlab::Graphql::Docs::Renderer do
describe '#contents' do describe '#contents' do
...@@ -14,6 +14,7 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do ...@@ -14,6 +14,7 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do
let(:template) { Rails.root.join('lib/gitlab/graphql/docs/templates/default.md.haml') } let(:template) { Rails.root.join('lib/gitlab/graphql/docs/templates/default.md.haml') }
let(:field_description) { 'List of objects.' } let(:field_description) { 'List of objects.' }
let(:type) { ::GraphQL::INT_TYPE }
let(:query_type) do let(:query_type) do
Class.new(Types::BaseObject) { graphql_name 'Query' }.tap do |t| Class.new(Types::BaseObject) { graphql_name 'Query' }.tap do |t|
...@@ -24,6 +25,13 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do ...@@ -24,6 +25,13 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do
end end
end end
let(:mutation_root) do
Class.new(::Types::BaseObject) do
include ::Gitlab::Graphql::MountMutation
graphql_name 'Mutation'
end
end
let(:mock_schema) do let(:mock_schema) do
Class.new(GraphQL::Schema) do Class.new(GraphQL::Schema) do
def resolve_type(obj, ctx) def resolve_type(obj, ctx)
...@@ -34,6 +42,7 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do ...@@ -34,6 +42,7 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do
subject(:contents) do subject(:contents) do
mock_schema.query(query_type) mock_schema.query(query_type)
mock_schema.mutation(mutation_root) if mutation_root.fields.any?
described_class.new( described_class.new(
mock_schema, mock_schema,
...@@ -43,8 +52,6 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do ...@@ -43,8 +52,6 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do
end end
describe 'headings' do describe 'headings' do
let(:type) { ::GraphQL::INT_TYPE }
it 'contains the expected sections' do it 'contains the expected sections' do
expect(contents.lines.map(&:chomp)).to include( expect(contents.lines.map(&:chomp)).to include(
'## `Query` type', '## `Query` type',
...@@ -56,7 +63,7 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do ...@@ -56,7 +63,7 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do
'## Abstract types', '## Abstract types',
'### Unions', '### Unions',
'### Interfaces', '### Interfaces',
'### Input types' '## Input types'
) )
end end
end end
...@@ -319,6 +326,97 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do ...@@ -319,6 +326,97 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do
end end
end end
context 'when there is a mutation' do
let(:mutation) do
mutation = Class.new(::Mutations::BaseMutation)
mutation.graphql_name 'MakeItPretty'
mutation.description 'Make everything very pretty.'
mutation.argument :prettiness_factor,
type: GraphQL::FLOAT_TYPE,
required: true,
description: 'How much prettier?'
mutation.field :everything,
type: GraphQL::STRING_TYPE,
null: true,
description: 'What we made prettier.'
mutation
end
before do
mutation_root.mount_mutation mutation
end
it_behaves_like 'renders correctly as GraphQL documentation' do
let(:section) do
<<~DOC
### `Mutation.makeItPretty`
Make everything very pretty.
Input type: `MakeItPrettyInput`.
#### arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationmakeitprettyclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationmakeitprettyprettinessfactor"></a>`prettinessFactor` | [`Float!`](#float) | How much prettier?. |
#### fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationmakeitprettyclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationmakeitprettyerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationmakeitprettyeverything"></a>`everything` | [`String`](#string) | What we made prettier. |
DOC
end
end
it 'does not render the automatically generated payload type' do
expect(contents).not_to include('MakeItPrettyPayload')
end
it 'does not render the automatically generated input type as its own section' do
expect(contents).not_to include('# `MakeItPrettyInput`')
end
end
context 'when there is an input type' do
let(:type) do
Class.new(::Types::BaseObject) do
graphql_name 'Foo'
field :wibble, type: ::GraphQL::INT_TYPE, null: true do
argument :date_range,
type: ::Types::TimeframeInputType,
required: true,
description: 'When the foo happened.'
end
end
end
let(:section) do
<<~DOC
### `Timeframe`
A time-frame defined as a closed inclusive range of two dates.
#### arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="timeframeend"></a>`end` | [`Date!`](#date) | The end of the range. |
| <a id="timeframestart"></a>`start` | [`Date!`](#date) | The start of the range. |
DOC
end
it_behaves_like 'renders correctly as GraphQL documentation'
end
context 'when there is an interface and a union' do context 'when there is an interface and a union' do
let(:type) do let(:type) do
user = Class.new(::Types::BaseObject) user = Class.new(::Types::BaseObject)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment