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
# You can check the included module on: https://github.com/gjtorikian/graphql-docs/blob/v1.6.0/lib/graphql-docs/helpers.rb
module Helper
include GraphQLDocs::Helpers
include Gitlab::Utils::StrongMemoize
def auto_generated_comment
<<-MD.strip_heredoc
......@@ -58,7 +59,8 @@ module Gitlab
render_return_type(field),
render_input_type(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")
end
......@@ -87,7 +89,7 @@ module Gitlab
end
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) }
type_name = owner[:name] if owner
......@@ -124,6 +126,8 @@ module Gitlab
end
def render_return_type(query)
return unless query[:type] # for example, mutations
"Returns #{render_field_type(query[:type])}."
end
......@@ -158,7 +162,7 @@ module Gitlab
end
def object_types
objects.reject { |t| t[:edge] || t[:connection] }
objects.reject { |t| t[:edge] || t[:connection] || t[:payload] }
end
def interfaces
......@@ -191,7 +195,7 @@ module Gitlab
arguments = input_type[:input_fields]
seen_type(input_type_name)
t.merge(arguments: arguments, fields: t[:return_fields])
t.merge(arguments: arguments)
end
end
......@@ -213,21 +217,35 @@ module Gitlab
# We are ignoring connections and built in types for now,
# they should be added when queries are generated.
def objects
@objects ||= graphql_object_types
.reject { |object_type| object_type[:name]["__"] } # We ignore introspection types.
.map do |type|
type.merge(
edge: type[:name].ends_with?('Edge'),
connection: type[:name].ends_with?('Connection'),
fields: type[:fields] + type[:connections]
)
end
strong_memoize(:objects) do
mutations = schema.mutation&.fields&.keys&.to_set || []
graphql_object_types
.reject { |object_type| object_type[:name]["__"] } # We ignore introspection types.
.map do |type|
name = type[:name]
type.merge(
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
private # DO NOT CALL THESE METHODS IN TEMPLATES
# 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)
return unless connection?(field)
......
......@@ -206,7 +206,7 @@
\
:plain
### Input types
## Input types
Types that may be used as arguments. (All scalar types may also
be used as arguments).
......
# frozen_string_literal: true
require 'fast_spec_helper'
require 'spec_helper'
RSpec.describe Gitlab::Graphql::Docs::Renderer do
describe '#contents' 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(:field_description) { 'List of objects.' }
let(:type) { ::GraphQL::INT_TYPE }
let(:query_type) do
Class.new(Types::BaseObject) { graphql_name 'Query' }.tap do |t|
......@@ -24,6 +25,13 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do
end
end
let(:mutation_root) do
Class.new(::Types::BaseObject) do
include ::Gitlab::Graphql::MountMutation
graphql_name 'Mutation'
end
end
let(:mock_schema) do
Class.new(GraphQL::Schema) do
def resolve_type(obj, ctx)
......@@ -34,6 +42,7 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do
subject(:contents) do
mock_schema.query(query_type)
mock_schema.mutation(mutation_root) if mutation_root.fields.any?
described_class.new(
mock_schema,
......@@ -43,8 +52,6 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do
end
describe 'headings' do
let(:type) { ::GraphQL::INT_TYPE }
it 'contains the expected sections' do
expect(contents.lines.map(&:chomp)).to include(
'## `Query` type',
......@@ -56,7 +63,7 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do
'## Abstract types',
'### Unions',
'### Interfaces',
'### Input types'
'## Input types'
)
end
end
......@@ -319,6 +326,97 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do
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
let(:type) do
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