graphql 1.4.5 → 1.5.3
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/enum_generator.rb +33 -0
- data/lib/generators/graphql/function_generator.rb +15 -0
- data/lib/generators/graphql/install_generator.rb +118 -0
- data/lib/generators/graphql/interface_generator.rb +27 -0
- data/lib/generators/graphql/loader_generator.rb +17 -0
- data/lib/generators/graphql/mutation_generator.rb +19 -0
- data/lib/generators/graphql/object_generator.rb +34 -0
- data/lib/generators/graphql/templates/enum.erb +4 -0
- data/lib/generators/graphql/templates/function.erb +17 -0
- data/lib/generators/graphql/templates/graphql_controller.erb +32 -0
- data/lib/generators/graphql/templates/interface.erb +4 -0
- data/lib/generators/graphql/templates/loader.erb +15 -0
- data/lib/generators/graphql/templates/mutation.erb +12 -0
- data/lib/generators/graphql/templates/object.erb +5 -0
- data/lib/generators/graphql/templates/query_type.erb +15 -0
- data/lib/generators/graphql/templates/schema.erb +34 -0
- data/lib/generators/graphql/templates/union.erb +4 -0
- data/lib/generators/graphql/type_generator.rb +78 -0
- data/lib/generators/graphql/union_generator.rb +33 -0
- data/lib/graphql.rb +10 -0
- data/lib/graphql/analysis/analyze_query.rb +1 -1
- data/lib/graphql/analysis/query_complexity.rb +6 -50
- data/lib/graphql/analysis/query_depth.rb +1 -1
- data/lib/graphql/argument.rb +21 -0
- data/lib/graphql/compatibility/execution_specification/counter_schema.rb +3 -3
- data/lib/graphql/define.rb +1 -0
- data/lib/graphql/define/assign_argument.rb +3 -19
- data/lib/graphql/define/assign_mutation_function.rb +34 -0
- data/lib/graphql/define/assign_object_field.rb +26 -14
- data/lib/graphql/define/defined_object_proxy.rb +21 -0
- data/lib/graphql/define/instance_definable.rb +61 -11
- data/lib/graphql/directive.rb +6 -1
- data/lib/graphql/execution/directive_checks.rb +1 -0
- data/lib/graphql/execution/execute.rb +14 -9
- data/lib/graphql/execution/field_result.rb +1 -0
- data/lib/graphql/execution/lazy.rb +8 -17
- data/lib/graphql/execution/lazy/lazy_method_map.rb +2 -0
- data/lib/graphql/execution/lazy/resolve.rb +1 -0
- data/lib/graphql/execution/selection_result.rb +1 -0
- data/lib/graphql/execution/typecast.rb +39 -26
- data/lib/graphql/field.rb +15 -3
- data/lib/graphql/field/resolve.rb +3 -3
- data/lib/graphql/function.rb +134 -0
- data/lib/graphql/id_type.rb +1 -1
- data/lib/graphql/input_object_type.rb +1 -1
- data/lib/graphql/internal_representation.rb +1 -1
- data/lib/graphql/internal_representation/node.rb +35 -107
- data/lib/graphql/internal_representation/rewrite.rb +189 -183
- data/lib/graphql/internal_representation/visit.rb +38 -0
- data/lib/graphql/introspection/input_value_type.rb +10 -1
- data/lib/graphql/introspection/schema_type.rb +1 -1
- data/lib/graphql/language/lexer.rb +6 -3
- data/lib/graphql/language/lexer.rl +6 -3
- data/lib/graphql/object_type.rb +53 -13
- data/lib/graphql/query.rb +30 -14
- data/lib/graphql/query/arguments.rb +2 -0
- data/lib/graphql/query/context.rb +2 -2
- data/lib/graphql/query/literal_input.rb +9 -0
- data/lib/graphql/query/serial_execution/field_resolution.rb +2 -2
- data/lib/graphql/query/serial_execution/selection_resolution.rb +1 -1
- data/lib/graphql/relay.rb +1 -0
- data/lib/graphql/relay/array_connection.rb +1 -1
- data/lib/graphql/relay/base_connection.rb +34 -15
- data/lib/graphql/relay/connection_resolve.rb +7 -2
- data/lib/graphql/relay/mutation.rb +45 -4
- data/lib/graphql/relay/node.rb +18 -6
- data/lib/graphql/relay/range_add.rb +45 -0
- data/lib/graphql/relay/relation_connection.rb +17 -2
- data/lib/graphql/runtime_type_error.rb +1 -0
- data/lib/graphql/schema.rb +40 -5
- data/lib/graphql/schema/base_64_encoder.rb +1 -0
- data/lib/graphql/schema/build_from_definition.rb +56 -21
- data/lib/graphql/schema/default_parse_error.rb +10 -0
- data/lib/graphql/schema/loader.rb +8 -1
- data/lib/graphql/schema/null_mask.rb +1 -0
- data/lib/graphql/schema/validation.rb +35 -0
- data/lib/graphql/static_validation.rb +1 -0
- data/lib/graphql/static_validation/all_rules.rb +1 -0
- data/lib/graphql/static_validation/arguments_validator.rb +7 -4
- data/lib/graphql/static_validation/definition_dependencies.rb +183 -0
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +28 -96
- data/lib/graphql/static_validation/rules/fragment_names_are_unique.rb +23 -0
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +8 -5
- data/lib/graphql/static_validation/rules/fragments_are_finite.rb +6 -31
- data/lib/graphql/static_validation/rules/fragments_are_used.rb +11 -41
- data/lib/graphql/static_validation/rules/operation_names_are_valid.rb +2 -2
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +19 -7
- data/lib/graphql/static_validation/validation_context.rb +22 -1
- data/lib/graphql/static_validation/validator.rb +4 -1
- data/lib/graphql/string_type.rb +5 -1
- data/lib/graphql/version.rb +1 -1
- data/readme.md +12 -3
- data/spec/generators/graphql/enum_generator_spec.rb +29 -0
- data/spec/generators/graphql/function_generator_spec.rb +33 -0
- data/spec/generators/graphql/install_generator_spec.rb +185 -0
- data/spec/generators/graphql/interface_generator_spec.rb +32 -0
- data/spec/generators/graphql/loader_generator_spec.rb +31 -0
- data/spec/generators/graphql/mutation_generator_spec.rb +28 -0
- data/spec/generators/graphql/object_generator_spec.rb +42 -0
- data/spec/generators/graphql/union_generator_spec.rb +50 -0
- data/spec/graphql/analysis/query_complexity_spec.rb +2 -1
- data/spec/graphql/define/instance_definable_spec.rb +38 -0
- data/spec/graphql/directive/skip_directive_spec.rb +1 -0
- data/spec/graphql/directive_spec.rb +18 -0
- data/spec/graphql/execution/typecast_spec.rb +41 -46
- data/spec/graphql/field_spec.rb +1 -1
- data/spec/graphql/function_spec.rb +128 -0
- data/spec/graphql/internal_representation/rewrite_spec.rb +166 -129
- data/spec/graphql/introspection/type_type_spec.rb +1 -1
- data/spec/graphql/language/lexer_spec.rb +6 -0
- data/spec/graphql/object_type_spec.rb +73 -2
- data/spec/graphql/query/arguments_spec.rb +28 -0
- data/spec/graphql/query/variables_spec.rb +7 -1
- data/spec/graphql/query_spec.rb +30 -0
- data/spec/graphql/relay/base_connection_spec.rb +26 -8
- data/spec/graphql/relay/connection_resolve_spec.rb +45 -0
- data/spec/graphql/relay/connection_type_spec.rb +21 -0
- data/spec/graphql/relay/node_spec.rb +30 -2
- data/spec/graphql/relay/range_add_spec.rb +113 -0
- data/spec/graphql/schema/build_from_definition_spec.rb +114 -0
- data/spec/graphql/schema/loader_spec.rb +1 -0
- data/spec/graphql/schema/printer_spec.rb +2 -2
- data/spec/graphql/schema/validation_spec.rb +80 -11
- data/spec/graphql/schema/warden_spec.rb +10 -10
- data/spec/graphql/schema_spec.rb +18 -1
- data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +16 -0
- data/spec/graphql/static_validation/rules/fields_will_merge_spec.rb +50 -3
- data/spec/graphql/static_validation/rules/fragment_names_are_unique_spec.rb +27 -0
- data/spec/graphql/static_validation/rules/fragments_are_finite_spec.rb +57 -0
- data/spec/graphql/static_validation/rules/fragments_are_used_spec.rb +1 -1
- data/spec/graphql/static_validation/rules/variables_are_input_types_spec.rb +14 -0
- data/spec/graphql/string_type_spec.rb +7 -0
- data/spec/spec_helper.rb +3 -3
- data/spec/support/base_generator_test.rb +7 -0
- data/spec/support/dummy/schema.rb +32 -30
- data/spec/support/star_wars/schema.rb +81 -23
- metadata +98 -20
- data/lib/graphql/internal_representation/selection.rb +0 -85
@@ -23,11 +23,14 @@ module GraphQL
|
|
23
23
|
|
24
24
|
context.visitor[GraphQL::Language::Nodes::Document].leave << ->(doc_node, parent) {
|
25
25
|
spreads_to_validate.each do |frag_spread|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
26
|
+
frag_node = context.fragments[frag_spread.node.name]
|
27
|
+
if frag_node
|
28
|
+
fragment_child_name = frag_node.type.name
|
29
|
+
fragment_child = context.warden.get_type(fragment_child_name)
|
30
|
+
# Might be non-existent type name
|
31
|
+
if fragment_child
|
32
|
+
validate_fragment_in_scope(frag_spread.parent_type, fragment_child, frag_spread.node, context, frag_spread.path)
|
33
|
+
end
|
31
34
|
end
|
32
35
|
end
|
33
36
|
}
|
@@ -5,38 +5,13 @@ module GraphQL
|
|
5
5
|
include GraphQL::StaticValidation::Message::MessageHelper
|
6
6
|
|
7
7
|
def validate(context)
|
8
|
-
context.visitor[GraphQL::Language::Nodes::
|
9
|
-
|
10
|
-
|
8
|
+
context.visitor[GraphQL::Language::Nodes::Document].leave << ->(_n, _p) do
|
9
|
+
dependency_map = context.dependencies
|
10
|
+
dependency_map.cyclical_definitions.each do |defn|
|
11
|
+
if defn.node.is_a?(GraphQL::Language::Nodes::FragmentDefinition)
|
12
|
+
context.errors << message("Fragment #{defn.name} contains an infinite loop", defn.node, path: defn.path)
|
13
|
+
end
|
11
14
|
end
|
12
|
-
}
|
13
|
-
end
|
14
|
-
|
15
|
-
private
|
16
|
-
|
17
|
-
def has_nested_spread?(fragment_def, parent_fragment_names, context)
|
18
|
-
nested_spreads = get_spreads(fragment_def.selections)
|
19
|
-
|
20
|
-
nested_spreads.any? do |spread|
|
21
|
-
nested_def = context.fragments[spread.name]
|
22
|
-
if nested_def.nil?
|
23
|
-
# this is another kind of error
|
24
|
-
false
|
25
|
-
else
|
26
|
-
parent_fragment_names.include?(spread.name) || has_nested_spread?(nested_def, parent_fragment_names + [fragment_def.name], context)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
# Find spreads contained in this selection & return them in a flat array
|
32
|
-
def get_spreads(selection)
|
33
|
-
case selection
|
34
|
-
when GraphQL::Language::Nodes::FragmentSpread
|
35
|
-
[selection]
|
36
|
-
when GraphQL::Language::Nodes::Field, GraphQL::Language::Nodes::InlineFragment
|
37
|
-
get_spreads(selection.selections)
|
38
|
-
when Array
|
39
|
-
selection.map { |s| get_spreads(s) }.flatten
|
40
15
|
end
|
41
16
|
end
|
42
17
|
end
|
@@ -5,49 +5,19 @@ module GraphQL
|
|
5
5
|
include GraphQL::StaticValidation::Message::MessageHelper
|
6
6
|
|
7
7
|
def validate(context)
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
.select { |defn| defn.is_a?(GraphQL::Language::Nodes::FragmentDefinition) }
|
15
|
-
.map { |node| FragmentInstance.new(node: node, path: context.path) }
|
16
|
-
}
|
17
|
-
|
18
|
-
v[GraphQL::Language::Nodes::FragmentSpread] << ->(node, parent) {
|
19
|
-
used_fragments << FragmentInstance.new(node: node, path: context.path)
|
20
|
-
if defined_fragments.none? { |defn| defn.name == node.name }
|
21
|
-
GraphQL::Language::Visitor::SKIP
|
8
|
+
context.visitor[GraphQL::Language::Nodes::Document].leave << ->(_n, _p) do
|
9
|
+
dependency_map = context.dependencies
|
10
|
+
dependency_map.unmet_dependencies.each do |op_defn, spreads|
|
11
|
+
spreads.each do |fragment_spread|
|
12
|
+
context.errors << message("Fragment #{fragment_spread.name} was used, but not defined", fragment_spread.node, path: fragment_spread.path)
|
13
|
+
end
|
22
14
|
end
|
23
|
-
}
|
24
|
-
v[GraphQL::Language::Nodes::Document].leave << ->(node, parent) { add_errors(context, used_fragments, defined_fragments) }
|
25
|
-
end
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
def add_errors(context, used_fragments, defined_fragments)
|
30
|
-
undefined_fragments = find_difference(used_fragments, defined_fragments.map(&:name))
|
31
|
-
undefined_fragments.each do |fragment|
|
32
|
-
context.errors << message("Fragment #{fragment.name} was used, but not defined", fragment.node, path: fragment.path)
|
33
|
-
end
|
34
15
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
def find_difference(fragments, allowed_fragment_names)
|
42
|
-
fragments.select {|f| f.name && !allowed_fragment_names.include?(f.name) }
|
43
|
-
end
|
44
|
-
|
45
|
-
class FragmentInstance
|
46
|
-
attr_reader :name, :node, :path
|
47
|
-
def initialize(node:, path:)
|
48
|
-
@node = node
|
49
|
-
@name = node.name
|
50
|
-
@path = path
|
16
|
+
dependency_map.unused_dependencies.each do |fragment|
|
17
|
+
if !fragment.name.nil?
|
18
|
+
context.errors << message("Fragment #{fragment.name} was defined, but not used", fragment.node, path: fragment.path)
|
19
|
+
end
|
20
|
+
end
|
51
21
|
end
|
52
22
|
end
|
53
23
|
end
|
@@ -7,11 +7,11 @@ module GraphQL
|
|
7
7
|
def validate(context)
|
8
8
|
op_names = Hash.new { |h, k| h[k] = [] }
|
9
9
|
|
10
|
-
context.visitor[GraphQL::Language::Nodes::OperationDefinition].enter << ->
|
10
|
+
context.visitor[GraphQL::Language::Nodes::OperationDefinition].enter << ->(node, _parent) {
|
11
11
|
op_names[node.name] << node
|
12
12
|
}
|
13
13
|
|
14
|
-
context.visitor[GraphQL::Language::Nodes::Document].leave << ->
|
14
|
+
context.visitor[GraphQL::Language::Nodes::Document].leave << ->(node, _parent) {
|
15
15
|
op_count = op_names.values.inject(0) { |m, v| m += v.size }
|
16
16
|
|
17
17
|
op_names.each do |name, nodes|
|
@@ -33,7 +33,10 @@ module GraphQL
|
|
33
33
|
private
|
34
34
|
|
35
35
|
def validate_usage(arguments, arg_node, ast_var, context)
|
36
|
-
var_type = to_query_type(ast_var.type, context.
|
36
|
+
var_type = to_query_type(ast_var.type, context.query.warden)
|
37
|
+
if var_type.nil?
|
38
|
+
return
|
39
|
+
end
|
37
40
|
if !ast_var.default_value.nil?
|
38
41
|
var_type = GraphQL::NonNullType.new(of_type: var_type)
|
39
42
|
end
|
@@ -53,13 +56,22 @@ module GraphQL
|
|
53
56
|
end
|
54
57
|
end
|
55
58
|
|
56
|
-
def to_query_type(ast_type,
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
59
|
+
def to_query_type(ast_type, warden)
|
60
|
+
case ast_type
|
61
|
+
when GraphQL::Language::Nodes::NonNullType
|
62
|
+
wrap_query_type(to_query_type(ast_type.of_type, warden), GraphQL::NonNullType)
|
63
|
+
when GraphQL::Language::Nodes::ListType
|
64
|
+
wrap_query_type(to_query_type(ast_type.of_type, warden), GraphQL::ListType)
|
65
|
+
else
|
66
|
+
warden.get_type(ast_type.name)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def wrap_query_type(type, wrapper)
|
71
|
+
if type.nil?
|
72
|
+
nil
|
61
73
|
else
|
62
|
-
|
74
|
+
wrapper.new(of_type: type)
|
63
75
|
end
|
64
76
|
end
|
65
77
|
|
@@ -12,7 +12,11 @@ module GraphQL
|
|
12
12
|
# It also provides limited access to the {TypeStack} instance,
|
13
13
|
# which tracks state as you climb in and out of different fields.
|
14
14
|
class ValidationContext
|
15
|
-
attr_reader :query, :schema,
|
15
|
+
attr_reader :query, :schema,
|
16
|
+
:document, :errors, :visitor,
|
17
|
+
:fragments, :operations, :warden,
|
18
|
+
:dependencies, :each_irep_node_handlers
|
19
|
+
|
16
20
|
def initialize(query)
|
17
21
|
@query = query
|
18
22
|
@schema = query.schema
|
@@ -33,12 +37,29 @@ module GraphQL
|
|
33
37
|
@errors = []
|
34
38
|
@visitor = GraphQL::Language::Visitor.new(document)
|
35
39
|
@type_stack = GraphQL::StaticValidation::TypeStack.new(schema, visitor)
|
40
|
+
definition_dependencies = DefinitionDependencies.mount(self)
|
41
|
+
@on_dependency_resolve_handlers = []
|
42
|
+
@each_irep_node_handlers = []
|
43
|
+
visitor[GraphQL::Language::Nodes::Document].leave << ->(_n, _p) {
|
44
|
+
@dependencies = definition_dependencies.dependency_map { |defn, spreads, frag|
|
45
|
+
@on_dependency_resolve_handlers.each { |h| h.call(defn, spreads, frag) }
|
46
|
+
}
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
def on_dependency_resolve(&handler)
|
52
|
+
@on_dependency_resolve_handlers << handler
|
36
53
|
end
|
37
54
|
|
38
55
|
def object_types
|
39
56
|
@type_stack.object_types
|
40
57
|
end
|
41
58
|
|
59
|
+
def each_irep_node(&handler)
|
60
|
+
@each_irep_node_handlers << handler
|
61
|
+
end
|
62
|
+
|
42
63
|
# @return [GraphQL::BaseType] The current object type
|
43
64
|
def type_definition
|
44
65
|
object_types.last
|
@@ -32,11 +32,14 @@ module GraphQL
|
|
32
32
|
end
|
33
33
|
|
34
34
|
context.visitor.visit
|
35
|
+
# Post-validation: allow validators to register handlers on rewritten query nodes
|
36
|
+
rewrite_result = rewrite.operations
|
37
|
+
GraphQL::InternalRepresentation::Visit.visit_each_node(rewrite_result, context.each_irep_node_handlers)
|
35
38
|
|
36
39
|
{
|
37
40
|
errors: context.errors,
|
38
41
|
# If there were errors, the irep is garbage
|
39
|
-
irep: context.errors.
|
42
|
+
irep: context.errors.any? ? nil : rewrite_result,
|
40
43
|
}
|
41
44
|
end
|
42
45
|
end
|
data/lib/graphql/string_type.rb
CHANGED
@@ -3,7 +3,11 @@ GraphQL::STRING_TYPE = GraphQL::ScalarType.define do
|
|
3
3
|
name "String"
|
4
4
|
description "Represents textual data as UTF-8 character sequences. This type is most often used by GraphQL to represent free-form human-readable text."
|
5
5
|
|
6
|
-
coerce_result ->(value) {
|
6
|
+
coerce_result ->(value) {
|
7
|
+
str = value.to_s
|
8
|
+
str.encoding == Encoding::US_ASCII || str.encoding == Encoding::UTF_8 ? str : nil
|
9
|
+
}
|
10
|
+
|
7
11
|
coerce_input ->(value) { value.is_a?(String) ? value : nil }
|
8
12
|
default_scalar true
|
9
13
|
end
|
data/lib/graphql/version.rb
CHANGED
data/readme.md
CHANGED
@@ -8,8 +8,9 @@
|
|
8
8
|
|
9
9
|
A Ruby implementation of [GraphQL](http://graphql.org/).
|
10
10
|
|
11
|
-
|
12
|
-
|
11
|
+
- [Website](https://rmosolgo.github.io/graphql-ruby)
|
12
|
+
- [API Documentation](http://www.rubydoc.info/github/rmosolgo/graphql-ruby)
|
13
|
+
- [Newsletter](https://tinyletter.com/graphql-ruby)
|
13
14
|
|
14
15
|
## Installation
|
15
16
|
|
@@ -26,7 +27,15 @@ $ bundle install
|
|
26
27
|
|
27
28
|
## Getting Started
|
28
29
|
|
29
|
-
|
30
|
+
```
|
31
|
+
$ rails generate graphql:install
|
32
|
+
```
|
33
|
+
|
34
|
+
Or, see ["Getting Started"](https://rmosolgo.github.io/graphql-ruby/).
|
35
|
+
|
36
|
+
## Upgrade
|
37
|
+
|
38
|
+
I also sell [GraphQL::Pro](http://graphql.pro) which provides several features on top of the GraphQL runtime, including [authorization](http://rmosolgo.github.io/graphql-ruby/pro/authorization), [monitoring](http://rmosolgo.github.io/graphql-ruby/pro/monitoring) plugins and [static queries](http://rmosolgo.github.io/graphql-ruby/pro/persisted_queries). Besides that, Pro customers get email support and an opportunity to support graphql-ruby's development!
|
30
39
|
|
31
40
|
## Goals
|
32
41
|
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "spec_helper"
|
3
|
+
require "generators/graphql/enum_generator"
|
4
|
+
|
5
|
+
class GraphQLGeneratorsEnumGeneratorTest < BaseGeneratorTest
|
6
|
+
tests Graphql::Generators::EnumGenerator
|
7
|
+
|
8
|
+
test "it generate enums with values" do
|
9
|
+
expected_content = <<-RUBY
|
10
|
+
Types::FamilyType = GraphQL::EnumType.define do
|
11
|
+
name "Family"
|
12
|
+
value "NIGHTSHADE"
|
13
|
+
value "BRASSICA", value: Family::COLE
|
14
|
+
value "UMBELLIFER", value: :umbellifer
|
15
|
+
value "LEGUME", value: "bean & friends"
|
16
|
+
value "CURCURBITS", value: 5
|
17
|
+
end
|
18
|
+
RUBY
|
19
|
+
|
20
|
+
run_generator(["Family",
|
21
|
+
"NIGHTSHADE",
|
22
|
+
"BRASSICA:Family::COLE",
|
23
|
+
"UMBELLIFER::umbellifer",
|
24
|
+
'LEGUME:"bean & friends"',
|
25
|
+
"CURCURBITS:5"
|
26
|
+
])
|
27
|
+
assert_file "app/graphql/types/family_type.rb", expected_content
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "spec_helper"
|
3
|
+
require "generators/graphql/function_generator"
|
4
|
+
|
5
|
+
class GraphQLGeneratorsFunctionGeneratorTest < BaseGeneratorTest
|
6
|
+
tests Graphql::Generators::FunctionGenerator
|
7
|
+
|
8
|
+
test "it generates an empty function by name" do
|
9
|
+
run_generator(["FindRecord"])
|
10
|
+
|
11
|
+
expected_content = <<-RUBY
|
12
|
+
class Functions::FindRecord < GraphQL::Function
|
13
|
+
# Define `initialize` to store field-level options, eg
|
14
|
+
#
|
15
|
+
# field :myField, function: Functions::FindRecord.new(type: MyType)
|
16
|
+
#
|
17
|
+
# attr_reader :type
|
18
|
+
# def initialize(type:)
|
19
|
+
# @type = type
|
20
|
+
# end
|
21
|
+
|
22
|
+
# add arguments by type:
|
23
|
+
# argument :id, !GraphQL::ID_TYPE
|
24
|
+
|
25
|
+
# Resolve function:
|
26
|
+
def call(obj, args, ctx)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
RUBY
|
30
|
+
|
31
|
+
assert_file "app/graphql/functions/find_record.rb", expected_content
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,185 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "spec_helper"
|
3
|
+
require "generators/graphql/install_generator"
|
4
|
+
|
5
|
+
class GraphQLGeneratorsInstallGeneratorTest < Rails::Generators::TestCase
|
6
|
+
|
7
|
+
tests Graphql::Generators::InstallGenerator
|
8
|
+
destination File.expand_path("../../../tmp/dummy", File.dirname(__FILE__))
|
9
|
+
|
10
|
+
setup do
|
11
|
+
prepare_destination
|
12
|
+
FileUtils.cd(File.expand_path("../../../tmp", File.dirname(__FILE__))) do
|
13
|
+
`rm -rf dummy`
|
14
|
+
`rails new dummy --skip-active-record --skip-test-unit --skip-spring --skip-bundle`
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
test "it generates a folder structure" do
|
19
|
+
run_generator([])
|
20
|
+
|
21
|
+
assert_file "app/graphql/types/.keep"
|
22
|
+
assert_file "app/graphql/mutations/.keep"
|
23
|
+
expected_query_route = %|post "/graphql", to: "graphql#execute"|
|
24
|
+
expected_graphiql_route = %|
|
25
|
+
if Rails.env.development?
|
26
|
+
mount GraphiQL::Rails::Engine, at: "/graphiql", graphql_path: "/graphql"
|
27
|
+
end
|
28
|
+
|
|
29
|
+
|
30
|
+
assert_file "config/routes.rb" do |contents|
|
31
|
+
assert_includes contents, expected_query_route
|
32
|
+
assert_includes contents, expected_graphiql_route
|
33
|
+
end
|
34
|
+
|
35
|
+
assert_file "Gemfile" do |contents|
|
36
|
+
assert_match %r{gem ('|")graphiql-rails('|"), :?group(:| =>) :development}, contents
|
37
|
+
end
|
38
|
+
|
39
|
+
expected_schema = <<-RUBY
|
40
|
+
DummySchema = GraphQL::Schema.define do
|
41
|
+
query(Types::QueryType)
|
42
|
+
end
|
43
|
+
RUBY
|
44
|
+
assert_file "app/graphql/dummy_schema.rb", expected_schema
|
45
|
+
|
46
|
+
|
47
|
+
expected_query_type = <<-RUBY
|
48
|
+
Types::QueryType = GraphQL::ObjectType.define do
|
49
|
+
name "Query"
|
50
|
+
# Add root-level fields here.
|
51
|
+
# They will be entry points for queries on your schema.
|
52
|
+
|
53
|
+
# TODO: remove me
|
54
|
+
field :testField, types.String do
|
55
|
+
description \"An example field added by the generator\"
|
56
|
+
resolve ->(obj, args, ctx) {
|
57
|
+
\"Hello World!\"
|
58
|
+
}
|
59
|
+
end
|
60
|
+
end
|
61
|
+
RUBY
|
62
|
+
|
63
|
+
assert_file "app/graphql/types/query_type.rb", expected_query_type
|
64
|
+
assert_file "app/controllers/graphql_controller.rb", EXPECTED_GRAPHQLS_CONTROLLER
|
65
|
+
end
|
66
|
+
|
67
|
+
test "it generates graphql-batch and relay boilerplate" do
|
68
|
+
run_generator(["--batch", "--relay"])
|
69
|
+
assert_file "app/graphql/loaders/.keep"
|
70
|
+
assert_file "Gemfile" do |contents|
|
71
|
+
assert_match %r{gem ('|")graphql-batch('|")}, contents
|
72
|
+
end
|
73
|
+
|
74
|
+
expected_query_type = <<-RUBY
|
75
|
+
Types::QueryType = GraphQL::ObjectType.define do
|
76
|
+
name "Query"
|
77
|
+
# Add root-level fields here.
|
78
|
+
# They will be entry points for queries on your schema.
|
79
|
+
|
80
|
+
# TODO: remove me
|
81
|
+
field :testField, types.String do
|
82
|
+
description \"An example field added by the generator\"
|
83
|
+
resolve ->(obj, args, ctx) {
|
84
|
+
\"Hello World!\"
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
field :node, GraphQL::Relay::Node.field
|
89
|
+
end
|
90
|
+
RUBY
|
91
|
+
|
92
|
+
assert_file "app/graphql/types/query_type.rb", expected_query_type
|
93
|
+
assert_file "app/graphql/dummy_schema.rb", EXPECTED_RELAY_BATCH_SCHEMA
|
94
|
+
end
|
95
|
+
|
96
|
+
test "it can skip keeps, skip graphiql and customize schema name" do
|
97
|
+
run_generator(["--skip-keeps", "--skip-graphiql", "--schema=CustomSchema"])
|
98
|
+
assert_no_file "app/graphql/types/.keep"
|
99
|
+
assert_no_file "app/graphql/mutations/.keep"
|
100
|
+
assert_file "app/graphql/types"
|
101
|
+
assert_file "app/graphql/mutations"
|
102
|
+
assert_file "Gemfile" do |contents|
|
103
|
+
refute_includes contents, "graphiql-rails"
|
104
|
+
end
|
105
|
+
|
106
|
+
assert_file "config/routes.rb" do |contents|
|
107
|
+
refute_includes contents, "GraphiQL::Rails"
|
108
|
+
end
|
109
|
+
|
110
|
+
assert_file "app/graphql/custom_schema.rb", /CustomSchema = GraphQL::Schema\.define/
|
111
|
+
assert_file "app/controllers/graphql_controller.rb", /CustomSchema\.execute/
|
112
|
+
end
|
113
|
+
|
114
|
+
EXPECTED_GRAPHQLS_CONTROLLER = <<-RUBY
|
115
|
+
class GraphqlController < ApplicationController
|
116
|
+
def execute
|
117
|
+
variables = ensure_hash(params[:variables])
|
118
|
+
query = params[:query]
|
119
|
+
context = {
|
120
|
+
# Query context goes here, for example:
|
121
|
+
# current_user: current_user,
|
122
|
+
}
|
123
|
+
result = DummySchema.execute(query, variables: variables, context: context)
|
124
|
+
render json: result
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
# Handle form data, JSON body, or a blank value
|
130
|
+
def ensure_hash(ambiguous_param)
|
131
|
+
case ambiguous_param
|
132
|
+
when String
|
133
|
+
if ambiguous_param.present?
|
134
|
+
ensure_hash(JSON.parse(ambiguous_param))
|
135
|
+
else
|
136
|
+
{}
|
137
|
+
end
|
138
|
+
when Hash, ActionController::Parameters
|
139
|
+
ambiguous_param
|
140
|
+
when nil
|
141
|
+
{}
|
142
|
+
else
|
143
|
+
raise ArgumentError, "Unexpected parameter: \#{ambiguous_param}"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
RUBY
|
148
|
+
|
149
|
+
EXPECTED_RELAY_BATCH_SCHEMA = <<-RUBY
|
150
|
+
DummySchema = GraphQL::Schema.define do
|
151
|
+
query(Types::QueryType)
|
152
|
+
|
153
|
+
# Relay Object Identification:
|
154
|
+
|
155
|
+
# Return a string UUID for `object`
|
156
|
+
id_from_object ->(object, type_definition, query_ctx) {
|
157
|
+
# Here's a simple implementation which:
|
158
|
+
# - joins the type name & object.id
|
159
|
+
# - encodes it with base64:
|
160
|
+
# GraphQL::Schema::UniqueWithinType.encode(type_definition.name, object.id)
|
161
|
+
}
|
162
|
+
|
163
|
+
# Given a string UUID, find the object
|
164
|
+
object_from_id ->(id, query_ctx) {
|
165
|
+
# For example, to decode the UUIDs generated above:
|
166
|
+
# type_name, item_id = GraphQL::Schema::UniqueWithinType.decode(id)
|
167
|
+
#
|
168
|
+
# Then, based on `type_name` and `id`
|
169
|
+
# find an object in your application
|
170
|
+
# ...
|
171
|
+
}
|
172
|
+
|
173
|
+
# Object Resolution
|
174
|
+
resolve_type -> (obj, ctx) {
|
175
|
+
# TODO: Implement this function
|
176
|
+
# to return the correct type for `obj`
|
177
|
+
raise(NotImplementedError)
|
178
|
+
}
|
179
|
+
|
180
|
+
# GraphQL::Batch setup:
|
181
|
+
lazy_resolve(Promise, :sync)
|
182
|
+
instrument(:query, GraphQL::Batch::Setup)
|
183
|
+
end
|
184
|
+
RUBY
|
185
|
+
end
|