graphql 1.8.1 → 1.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|