graphql 1.8.1 → 1.8.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/generators/graphql/core.rb +1 -1
- data/lib/generators/graphql/enum_generator.rb +3 -3
- data/lib/generators/graphql/install_generator.rb +12 -1
- data/lib/generators/graphql/mutation_generator.rb +4 -4
- data/lib/generators/graphql/templates/base_enum.erb +2 -0
- data/lib/generators/graphql/templates/base_input_object.erb +2 -0
- data/lib/generators/graphql/templates/base_interface.erb +3 -0
- data/lib/generators/graphql/templates/base_object.erb +2 -0
- data/lib/generators/graphql/templates/base_union.erb +2 -0
- data/lib/generators/graphql/templates/enum.erb +1 -2
- data/lib/generators/graphql/templates/interface.erb +2 -3
- data/lib/generators/graphql/templates/mutation.erb +4 -5
- data/lib/generators/graphql/templates/mutation_type.erb +6 -9
- data/lib/generators/graphql/templates/object.erb +2 -3
- data/lib/generators/graphql/templates/query_type.erb +6 -8
- data/lib/generators/graphql/templates/schema.erb +7 -7
- data/lib/generators/graphql/templates/union.erb +1 -2
- data/lib/generators/graphql/type_generator.rb +33 -18
- data/lib/generators/graphql/union_generator.rb +1 -1
- data/lib/graphql/execution/execute.rb +3 -0
- data/lib/graphql/schema.rb +1 -0
- data/lib/graphql/schema/input_object.rb +22 -1
- data/lib/graphql/schema/relay_classic_mutation.rb +6 -2
- data/lib/graphql/static_validation/all_rules.rb +1 -0
- data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +31 -0
- data/lib/graphql/subscriptions/instrumentation.rb +3 -0
- data/lib/graphql/version.rb +1 -1
- data/spec/generators/graphql/enum_generator_spec.rb +1 -2
- data/spec/generators/graphql/install_generator_spec.rb +23 -24
- data/spec/generators/graphql/interface_generator_spec.rb +5 -6
- data/spec/generators/graphql/mutation_generator_spec.rb +10 -27
- data/spec/generators/graphql/object_generator_spec.rb +7 -10
- data/spec/generators/graphql/union_generator_spec.rb +3 -6
- data/spec/graphql/execution/execute_spec.rb +97 -0
- data/spec/graphql/schema/input_object_spec.rb +32 -0
- data/spec/graphql/schema/relay_classic_mutation_spec.rb +16 -0
- data/spec/graphql/static_validation/rules/argument_names_are_unique_spec.rb +44 -0
- data/spec/graphql/subscriptions_spec.rb +21 -1
- data/spec/support/jazz.rb +15 -2
- metadata +10 -6
- data/lib/generators/graphql/function_generator.rb +0 -18
- data/lib/generators/graphql/templates/function.erb +0 -17
- data/spec/generators/graphql/function_generator_spec.rb +0 -59
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7cbf4bc1aa62d7f5ea7cec15027db39ca2162fe1
|
4
|
+
data.tar.gz: f622a8c446b557fb4fcdfb50e8868a808cb0b94b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '0948dbb7c4dca8399a2c20a5e59bd823223c0662fb687e6062cdeb76709f0f5ad097bd6d0e77425d19125ddbd0ed4a1ba3ee88a3f5da61f2eae1a4dd34d8e7d7'
|
7
|
+
data.tar.gz: 409291f67651736ac996e6971b53fea7aa854983a69ad89887e6d71b68cef3550022317f83f268e23f83242c8e13ad71e65e5658da99aefa2e09c0c485ebda70
|
@@ -16,7 +16,7 @@ module Graphql
|
|
16
16
|
|
17
17
|
def insert_root_type(type, name)
|
18
18
|
log :add_root_type, type
|
19
|
-
sentinel =
|
19
|
+
sentinel = /< GraphQL::Schema\s*\n/m
|
20
20
|
|
21
21
|
in_root do
|
22
22
|
inject_into_file schema_file_path, " #{type}(Types::#{name})\n", after: sentinel, verbose: false, force: false
|
@@ -3,11 +3,11 @@ require 'generators/graphql/type_generator'
|
|
3
3
|
|
4
4
|
module Graphql
|
5
5
|
module Generators
|
6
|
-
# Generate an
|
7
|
-
#
|
6
|
+
# Generate an enum type by name, with the given values.
|
7
|
+
# To add a `value:` option, add another value after a `:`.
|
8
8
|
#
|
9
9
|
# ```
|
10
|
-
# rails g graphql:
|
10
|
+
# rails g graphql:enum ProgrammingLanguage RUBY PYTHON PERL PERL6:"PERL"
|
11
11
|
# ```
|
12
12
|
class EnumGenerator < TypeGeneratorBase
|
13
13
|
desc "Create a GraphQL::EnumType with the given name and values"
|
@@ -13,6 +13,11 @@ module Graphql
|
|
13
13
|
# - graphql/
|
14
14
|
# - resolvers/
|
15
15
|
# - types/
|
16
|
+
# - base_enum.rb
|
17
|
+
# - base_input_object.rb
|
18
|
+
# - base_interface.rb
|
19
|
+
# - base_object.rb
|
20
|
+
# - base_union.rb
|
16
21
|
# - query_type.rb
|
17
22
|
# - loaders/
|
18
23
|
# - mutations/
|
@@ -40,6 +45,8 @@ module Graphql
|
|
40
45
|
# Accept a `--batch` option which adds `GraphQL::Batch` setup.
|
41
46
|
#
|
42
47
|
# Use `--no-graphiql` to skip `graphiql-rails` installation.
|
48
|
+
#
|
49
|
+
# TODO: also add base classes
|
43
50
|
class InstallGenerator < Rails::Generators::Base
|
44
51
|
include Core
|
45
52
|
|
@@ -85,7 +92,11 @@ module Graphql
|
|
85
92
|
create_dir("#{options[:directory]}/types")
|
86
93
|
template("schema.erb", schema_file_path)
|
87
94
|
|
88
|
-
|
95
|
+
["base_object", "base_enum", "base_input_object", "base_interface", "base_union"].each do |base_type|
|
96
|
+
template("#{base_type}.erb", "#{options[:directory]}/types/#{base_type}.rb")
|
97
|
+
end
|
98
|
+
|
99
|
+
# Note: You can't have a schema without the query type, otherwise introspection breaks
|
89
100
|
template("query_type.erb", "#{options[:directory]}/types/query_type.rb")
|
90
101
|
insert_root_type('query', 'QueryType')
|
91
102
|
|
@@ -6,12 +6,12 @@ module Graphql
|
|
6
6
|
module Generators
|
7
7
|
# TODO: What other options should be supported?
|
8
8
|
#
|
9
|
-
# @example Generate a `
|
9
|
+
# @example Generate a `GraphQL::Schema::RelayClassicMutation` by name
|
10
10
|
# rails g graphql:mutation CreatePostMutation
|
11
11
|
class MutationGenerator < Rails::Generators::Base
|
12
12
|
include Core
|
13
13
|
|
14
|
-
desc "Create a Relay mutation by name"
|
14
|
+
desc "Create a Relay Classic mutation by name"
|
15
15
|
source_root File.expand_path('../templates', __FILE__)
|
16
16
|
|
17
17
|
argument :name, type: :string
|
@@ -32,10 +32,10 @@ module Graphql
|
|
32
32
|
else
|
33
33
|
log :gsub, "#{options[:directory]}/types/mutation_type.rb"
|
34
34
|
end
|
35
|
-
|
35
|
+
|
36
36
|
template "mutation.erb", "#{options[:directory]}/mutations/#{file_name}.rb"
|
37
37
|
|
38
|
-
sentinel = /
|
38
|
+
sentinel = /class .*MutationType/m
|
39
39
|
in_root do
|
40
40
|
gsub_file "#{options[:directory]}/types/mutation_type.rb", / \# TODO\: Add Mutations as fields\s*\n/m, ""
|
41
41
|
inject_into_file "#{options[:directory]}/types/mutation_type.rb", " field :#{field_name}, Mutations::#{mutation_name}.field\n", after: sentinel, verbose: false, force: false
|
@@ -1,4 +1,3 @@
|
|
1
|
-
<%= type_ruby_name %>
|
2
|
-
|
3
|
-
<% normalized_fields.each do |f| %> field :<%= f[0] %>, <%= f[1] %>
|
1
|
+
class <%= type_ruby_name %> < Types::BaseInterface
|
2
|
+
<% normalized_fields.each do |f| %> <%= f.to_ruby %>
|
4
3
|
<% end %>end
|
@@ -1,12 +1,11 @@
|
|
1
|
-
Mutations::<%= mutation_name %>
|
2
|
-
name "<%= mutation_name %>"
|
1
|
+
class Mutations::<%= mutation_name %> < GraphQL::Schema::RelayClassicMutation
|
3
2
|
# TODO: define return fields
|
4
3
|
# return_field :post, Types::PostType
|
5
4
|
|
6
5
|
# TODO: define arguments
|
7
6
|
# input_field :name, !types.String
|
8
7
|
|
9
|
-
resolve
|
10
|
-
# TODO: define resolve
|
11
|
-
|
8
|
+
def resolve(**inputs)
|
9
|
+
# TODO: define resolve method
|
10
|
+
end
|
12
11
|
end
|
@@ -1,11 +1,8 @@
|
|
1
|
-
Types::MutationType
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
resolve ->(obj, args, ctx) {
|
8
|
-
"Hello World!"
|
9
|
-
}
|
1
|
+
class Types::MutationType < Types::BaseObject
|
2
|
+
# TODO: remove me
|
3
|
+
field :test_field, String, null: false,
|
4
|
+
description: "An example field added by the generator"
|
5
|
+
def test_field
|
6
|
+
"Hello World"
|
10
7
|
end
|
11
8
|
end
|
@@ -1,5 +1,4 @@
|
|
1
|
-
<%= type_ruby_name %>
|
2
|
-
name "<%= type_graphql_name %>"
|
1
|
+
class <%= type_ruby_name %> < Types::BaseObject
|
3
2
|
<% if options.node %> implements GraphQL::Relay::Node.interface
|
4
|
-
<% end %><% normalized_fields.each do |f| %>
|
3
|
+
<% end %><% normalized_fields.each do |f| %> <%= f.to_ruby %>
|
5
4
|
<% end %>end
|
@@ -1,15 +1,13 @@
|
|
1
|
-
Types::QueryType
|
2
|
-
name "Query"
|
1
|
+
class Types::QueryType < Types::BaseObject
|
3
2
|
# Add root-level fields here.
|
4
3
|
# They will be entry points for queries on your schema.
|
5
4
|
|
6
5
|
# TODO: remove me
|
7
|
-
field :
|
8
|
-
description "An example field added by the generator"
|
9
|
-
|
10
|
-
|
11
|
-
}
|
6
|
+
field :test_field, String, null: false,
|
7
|
+
description: "An example field added by the generator"
|
8
|
+
def test_field
|
9
|
+
"Hello World!"
|
12
10
|
end
|
13
11
|
<% if options[:relay] %>
|
14
|
-
field :node, GraphQL::Relay::Node.field
|
12
|
+
field :node, field: GraphQL::Relay::Node.field
|
15
13
|
<% end %>end
|
@@ -1,31 +1,31 @@
|
|
1
|
-
<%= schema_name %>
|
1
|
+
class <%= schema_name %> < GraphQL::Schema
|
2
2
|
<% if options[:relay] %>
|
3
3
|
# Relay Object Identification:
|
4
4
|
|
5
5
|
# Return a string UUID for `object`
|
6
|
-
id_from_object
|
6
|
+
def self.id_from_object(object, type_definition, query_ctx)
|
7
7
|
# Here's a simple implementation which:
|
8
8
|
# - joins the type name & object.id
|
9
9
|
# - encodes it with base64:
|
10
10
|
# GraphQL::Schema::UniqueWithinType.encode(type_definition.name, object.id)
|
11
|
-
|
11
|
+
end
|
12
12
|
|
13
13
|
# Given a string UUID, find the object
|
14
|
-
object_from_id
|
14
|
+
def self.object_from_id(id, query_ctx)
|
15
15
|
# For example, to decode the UUIDs generated above:
|
16
16
|
# type_name, item_id = GraphQL::Schema::UniqueWithinType.decode(id)
|
17
17
|
#
|
18
18
|
# Then, based on `type_name` and `id`
|
19
19
|
# find an object in your application
|
20
20
|
# ...
|
21
|
-
|
21
|
+
end
|
22
22
|
|
23
23
|
# Object Resolution
|
24
|
-
resolve_type
|
24
|
+
def self.resolve_type(type, obj, ctx)
|
25
25
|
# TODO: Implement this function
|
26
26
|
# to return the correct type for `obj`
|
27
27
|
raise(NotImplementedError)
|
28
|
-
|
28
|
+
end
|
29
29
|
<% end %><% if options[:batch] %>
|
30
30
|
# GraphQL::Batch setup:
|
31
31
|
use GraphQL::Batch
|
@@ -21,33 +21,35 @@ module Graphql
|
|
21
21
|
# TODO: nullability / list with `mode: :graphql` doesn't work
|
22
22
|
# @param type_expresson [String]
|
23
23
|
# @param mode [Symbol]
|
24
|
-
# @
|
25
|
-
|
24
|
+
# @param null [Boolean]
|
25
|
+
# @return [(String, Boolean)] The type expression, followed by `null:` value
|
26
|
+
def self.normalize_type_expression(type_expression, mode:, null: true)
|
26
27
|
if type_expression.start_with?("!")
|
27
|
-
|
28
|
+
normalize_type_expression(type_expression[1..-1], mode: mode, null: false)
|
28
29
|
elsif type_expression.end_with?("!")
|
29
|
-
|
30
|
+
normalize_type_expression(type_expression[0..-2], mode: mode, null: false)
|
30
31
|
elsif type_expression.start_with?("[") && type_expression.end_with?("]")
|
31
|
-
|
32
|
-
|
33
|
-
"types[#{normalize_type_expression(type_expression[6..-2], mode: mode)}]"
|
32
|
+
name, is_null = normalize_type_expression(type_expression[1..-2], mode: mode, null: null)
|
33
|
+
["[#{name}]", is_null]
|
34
34
|
elsif type_expression.end_with?("Type")
|
35
|
-
normalize_type_expression(type_expression[0..-5], mode: mode)
|
35
|
+
normalize_type_expression(type_expression[0..-5], mode: mode, null: null)
|
36
36
|
elsif type_expression.start_with?("Types::")
|
37
|
-
normalize_type_expression(type_expression[7..-1], mode: mode)
|
37
|
+
normalize_type_expression(type_expression[7..-1], mode: mode, null: null)
|
38
38
|
elsif type_expression.start_with?("types.")
|
39
|
-
normalize_type_expression(type_expression[6..-1], mode: mode)
|
39
|
+
normalize_type_expression(type_expression[6..-1], mode: mode, null: null)
|
40
40
|
else
|
41
41
|
case mode
|
42
42
|
when :ruby
|
43
43
|
case type_expression
|
44
|
-
when "Int"
|
45
|
-
"
|
44
|
+
when "Int"
|
45
|
+
["Integer", null]
|
46
|
+
when "Integer", "Float", "Boolean", "String", "ID"
|
47
|
+
[type_expression, null]
|
46
48
|
else
|
47
|
-
"Types::#{type_expression.camelize}Type"
|
49
|
+
["Types::#{type_expression.camelize}Type", null]
|
48
50
|
end
|
49
51
|
when :graphql
|
50
|
-
type_expression.camelize
|
52
|
+
[type_expression.camelize, null]
|
51
53
|
else
|
52
54
|
raise "Unexpected normalize mode: #{mode}"
|
53
55
|
end
|
@@ -58,12 +60,12 @@ module Graphql
|
|
58
60
|
|
59
61
|
# @return [String] The user-provided type name, normalized to Ruby code
|
60
62
|
def type_ruby_name
|
61
|
-
@type_ruby_name ||= self.class.normalize_type_expression(type_name, mode: :ruby)
|
63
|
+
@type_ruby_name ||= self.class.normalize_type_expression(type_name, mode: :ruby)[0]
|
62
64
|
end
|
63
65
|
|
64
66
|
# @return [String] The user-provided type name, as a GraphQL name
|
65
67
|
def type_graphql_name
|
66
|
-
@type_graphql_name ||= self.class.normalize_type_expression(type_name, mode: :graphql)
|
68
|
+
@type_graphql_name ||= self.class.normalize_type_expression(type_name, mode: :graphql)[0]
|
67
69
|
end
|
68
70
|
|
69
71
|
# @return [String] The user-provided type name, as a file name (without extension)
|
@@ -71,13 +73,26 @@ module Graphql
|
|
71
73
|
@type_file_name ||= "#{type_graphql_name}Type".underscore
|
72
74
|
end
|
73
75
|
|
74
|
-
# @return [Array<
|
76
|
+
# @return [Array<NormalizedField>] User-provided fields, in `(name, Ruby type name)` pairs
|
75
77
|
def normalized_fields
|
76
78
|
@normalized_fields ||= fields.map { |f|
|
77
79
|
name, raw_type = f.split(":", 2)
|
78
|
-
|
80
|
+
type_expr, null = self.class.normalize_type_expression(raw_type, mode: :ruby)
|
81
|
+
NormalizedField.new(name, type_expr, null)
|
79
82
|
}
|
80
83
|
end
|
84
|
+
|
85
|
+
class NormalizedField
|
86
|
+
def initialize(name, type_expr, null)
|
87
|
+
@name = name
|
88
|
+
@type_expr = type_expr
|
89
|
+
@null = null
|
90
|
+
end
|
91
|
+
|
92
|
+
def to_ruby
|
93
|
+
"field :#{@name}, #{@type_expr}, null: #{@null}"
|
94
|
+
end
|
95
|
+
end
|
81
96
|
end
|
82
97
|
end
|
83
98
|
end
|
@@ -211,10 +211,13 @@ module GraphQL
|
|
211
211
|
inner_ctx,
|
212
212
|
)
|
213
213
|
|
214
|
+
return PROPAGATE_NULL if inner_result == PROPAGATE_NULL
|
215
|
+
|
214
216
|
inner_ctx.value = inner_result
|
215
217
|
result << inner_ctx
|
216
218
|
i += 1
|
217
219
|
end
|
220
|
+
|
218
221
|
result
|
219
222
|
when GraphQL::TypeKinds::NON_NULL
|
220
223
|
inner_type = field_type.of_type
|
data/lib/graphql/schema.rb
CHANGED
@@ -27,7 +27,28 @@ module GraphQL
|
|
27
27
|
attr_reader :arguments
|
28
28
|
|
29
29
|
# Ruby-like hash behaviors, read-only
|
30
|
-
def_delegators :@ruby_style_hash, :
|
30
|
+
def_delegators :@ruby_style_hash, :keys, :values, :each, :any?
|
31
|
+
|
32
|
+
def to_h
|
33
|
+
@ruby_style_hash.inject({}) do |h, (key, value)|
|
34
|
+
h.merge(key => unwrap_value(value))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def unwrap_value(value)
|
39
|
+
case value
|
40
|
+
when Array
|
41
|
+
value.map { |item| unwrap_value(item) }
|
42
|
+
when Hash
|
43
|
+
value.inject({}) do |h, (key, value)|
|
44
|
+
h.merge(key => unwrap_value(value))
|
45
|
+
end
|
46
|
+
when InputObject
|
47
|
+
value.to_h
|
48
|
+
else
|
49
|
+
value
|
50
|
+
end
|
51
|
+
end
|
31
52
|
|
32
53
|
# Lookup a key on this object, it accepts new-style underscored symbols
|
33
54
|
# Or old-style camelized identifiers.
|
@@ -27,10 +27,14 @@ module GraphQL
|
|
27
27
|
|
28
28
|
# Override {GraphQL::Schema::Mutation#resolve_mutation} to
|
29
29
|
# delete `client_mutation_id` from the kwargs.
|
30
|
-
def resolve_mutation(kwargs)
|
30
|
+
def resolve_mutation(**kwargs)
|
31
31
|
# This is handled by Relay::Mutation::Resolve, a bit hacky, but here we are.
|
32
32
|
kwargs.delete(:client_mutation_id)
|
33
|
-
|
33
|
+
if kwargs.any?
|
34
|
+
resolve(**kwargs)
|
35
|
+
else
|
36
|
+
resolve
|
37
|
+
end
|
34
38
|
end
|
35
39
|
|
36
40
|
resolve_method(:resolve_mutation)
|
@@ -24,6 +24,7 @@ module GraphQL
|
|
24
24
|
GraphQL::StaticValidation::ArgumentsAreDefined,
|
25
25
|
GraphQL::StaticValidation::ArgumentLiteralsAreCompatible,
|
26
26
|
GraphQL::StaticValidation::RequiredArgumentsArePresent,
|
27
|
+
GraphQL::StaticValidation::ArgumentNamesAreUnique,
|
27
28
|
GraphQL::StaticValidation::VariableNamesAreUnique,
|
28
29
|
GraphQL::StaticValidation::VariablesAreInputTypes,
|
29
30
|
GraphQL::StaticValidation::VariableDefaultValuesAreCorrectlyTyped,
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
module StaticValidation
|
4
|
+
class ArgumentNamesAreUnique
|
5
|
+
include GraphQL::StaticValidation::Message::MessageHelper
|
6
|
+
|
7
|
+
def validate(context)
|
8
|
+
context.visitor[GraphQL::Language::Nodes::Field] << ->(node, parent) {
|
9
|
+
validate_arguments(node, context)
|
10
|
+
}
|
11
|
+
|
12
|
+
context.visitor[GraphQL::Language::Nodes::Directive] << ->(node, parent) {
|
13
|
+
validate_arguments(node, context)
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
def validate_arguments(node, context)
|
18
|
+
argument_defns = node.arguments
|
19
|
+
if argument_defns.any?
|
20
|
+
args_by_name = Hash.new { |h, k| h[k] = [] }
|
21
|
+
argument_defns.each { |a| args_by_name[a.name] << a }
|
22
|
+
args_by_name.each do |name, defns|
|
23
|
+
if defns.size > 1
|
24
|
+
context.errors << message("There can be only one argument named \"#{name}\"", defns, context: context)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|