graphql 1.4.5 → 1.5.3
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/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
|