graphql 1.4.5 → 1.5.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (139) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/enum_generator.rb +33 -0
  3. data/lib/generators/graphql/function_generator.rb +15 -0
  4. data/lib/generators/graphql/install_generator.rb +118 -0
  5. data/lib/generators/graphql/interface_generator.rb +27 -0
  6. data/lib/generators/graphql/loader_generator.rb +17 -0
  7. data/lib/generators/graphql/mutation_generator.rb +19 -0
  8. data/lib/generators/graphql/object_generator.rb +34 -0
  9. data/lib/generators/graphql/templates/enum.erb +4 -0
  10. data/lib/generators/graphql/templates/function.erb +17 -0
  11. data/lib/generators/graphql/templates/graphql_controller.erb +32 -0
  12. data/lib/generators/graphql/templates/interface.erb +4 -0
  13. data/lib/generators/graphql/templates/loader.erb +15 -0
  14. data/lib/generators/graphql/templates/mutation.erb +12 -0
  15. data/lib/generators/graphql/templates/object.erb +5 -0
  16. data/lib/generators/graphql/templates/query_type.erb +15 -0
  17. data/lib/generators/graphql/templates/schema.erb +34 -0
  18. data/lib/generators/graphql/templates/union.erb +4 -0
  19. data/lib/generators/graphql/type_generator.rb +78 -0
  20. data/lib/generators/graphql/union_generator.rb +33 -0
  21. data/lib/graphql.rb +10 -0
  22. data/lib/graphql/analysis/analyze_query.rb +1 -1
  23. data/lib/graphql/analysis/query_complexity.rb +6 -50
  24. data/lib/graphql/analysis/query_depth.rb +1 -1
  25. data/lib/graphql/argument.rb +21 -0
  26. data/lib/graphql/compatibility/execution_specification/counter_schema.rb +3 -3
  27. data/lib/graphql/define.rb +1 -0
  28. data/lib/graphql/define/assign_argument.rb +3 -19
  29. data/lib/graphql/define/assign_mutation_function.rb +34 -0
  30. data/lib/graphql/define/assign_object_field.rb +26 -14
  31. data/lib/graphql/define/defined_object_proxy.rb +21 -0
  32. data/lib/graphql/define/instance_definable.rb +61 -11
  33. data/lib/graphql/directive.rb +6 -1
  34. data/lib/graphql/execution/directive_checks.rb +1 -0
  35. data/lib/graphql/execution/execute.rb +14 -9
  36. data/lib/graphql/execution/field_result.rb +1 -0
  37. data/lib/graphql/execution/lazy.rb +8 -17
  38. data/lib/graphql/execution/lazy/lazy_method_map.rb +2 -0
  39. data/lib/graphql/execution/lazy/resolve.rb +1 -0
  40. data/lib/graphql/execution/selection_result.rb +1 -0
  41. data/lib/graphql/execution/typecast.rb +39 -26
  42. data/lib/graphql/field.rb +15 -3
  43. data/lib/graphql/field/resolve.rb +3 -3
  44. data/lib/graphql/function.rb +134 -0
  45. data/lib/graphql/id_type.rb +1 -1
  46. data/lib/graphql/input_object_type.rb +1 -1
  47. data/lib/graphql/internal_representation.rb +1 -1
  48. data/lib/graphql/internal_representation/node.rb +35 -107
  49. data/lib/graphql/internal_representation/rewrite.rb +189 -183
  50. data/lib/graphql/internal_representation/visit.rb +38 -0
  51. data/lib/graphql/introspection/input_value_type.rb +10 -1
  52. data/lib/graphql/introspection/schema_type.rb +1 -1
  53. data/lib/graphql/language/lexer.rb +6 -3
  54. data/lib/graphql/language/lexer.rl +6 -3
  55. data/lib/graphql/object_type.rb +53 -13
  56. data/lib/graphql/query.rb +30 -14
  57. data/lib/graphql/query/arguments.rb +2 -0
  58. data/lib/graphql/query/context.rb +2 -2
  59. data/lib/graphql/query/literal_input.rb +9 -0
  60. data/lib/graphql/query/serial_execution/field_resolution.rb +2 -2
  61. data/lib/graphql/query/serial_execution/selection_resolution.rb +1 -1
  62. data/lib/graphql/relay.rb +1 -0
  63. data/lib/graphql/relay/array_connection.rb +1 -1
  64. data/lib/graphql/relay/base_connection.rb +34 -15
  65. data/lib/graphql/relay/connection_resolve.rb +7 -2
  66. data/lib/graphql/relay/mutation.rb +45 -4
  67. data/lib/graphql/relay/node.rb +18 -6
  68. data/lib/graphql/relay/range_add.rb +45 -0
  69. data/lib/graphql/relay/relation_connection.rb +17 -2
  70. data/lib/graphql/runtime_type_error.rb +1 -0
  71. data/lib/graphql/schema.rb +40 -5
  72. data/lib/graphql/schema/base_64_encoder.rb +1 -0
  73. data/lib/graphql/schema/build_from_definition.rb +56 -21
  74. data/lib/graphql/schema/default_parse_error.rb +10 -0
  75. data/lib/graphql/schema/loader.rb +8 -1
  76. data/lib/graphql/schema/null_mask.rb +1 -0
  77. data/lib/graphql/schema/validation.rb +35 -0
  78. data/lib/graphql/static_validation.rb +1 -0
  79. data/lib/graphql/static_validation/all_rules.rb +1 -0
  80. data/lib/graphql/static_validation/arguments_validator.rb +7 -4
  81. data/lib/graphql/static_validation/definition_dependencies.rb +183 -0
  82. data/lib/graphql/static_validation/rules/fields_will_merge.rb +28 -96
  83. data/lib/graphql/static_validation/rules/fragment_names_are_unique.rb +23 -0
  84. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +8 -5
  85. data/lib/graphql/static_validation/rules/fragments_are_finite.rb +6 -31
  86. data/lib/graphql/static_validation/rules/fragments_are_used.rb +11 -41
  87. data/lib/graphql/static_validation/rules/operation_names_are_valid.rb +2 -2
  88. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +19 -7
  89. data/lib/graphql/static_validation/validation_context.rb +22 -1
  90. data/lib/graphql/static_validation/validator.rb +4 -1
  91. data/lib/graphql/string_type.rb +5 -1
  92. data/lib/graphql/version.rb +1 -1
  93. data/readme.md +12 -3
  94. data/spec/generators/graphql/enum_generator_spec.rb +29 -0
  95. data/spec/generators/graphql/function_generator_spec.rb +33 -0
  96. data/spec/generators/graphql/install_generator_spec.rb +185 -0
  97. data/spec/generators/graphql/interface_generator_spec.rb +32 -0
  98. data/spec/generators/graphql/loader_generator_spec.rb +31 -0
  99. data/spec/generators/graphql/mutation_generator_spec.rb +28 -0
  100. data/spec/generators/graphql/object_generator_spec.rb +42 -0
  101. data/spec/generators/graphql/union_generator_spec.rb +50 -0
  102. data/spec/graphql/analysis/query_complexity_spec.rb +2 -1
  103. data/spec/graphql/define/instance_definable_spec.rb +38 -0
  104. data/spec/graphql/directive/skip_directive_spec.rb +1 -0
  105. data/spec/graphql/directive_spec.rb +18 -0
  106. data/spec/graphql/execution/typecast_spec.rb +41 -46
  107. data/spec/graphql/field_spec.rb +1 -1
  108. data/spec/graphql/function_spec.rb +128 -0
  109. data/spec/graphql/internal_representation/rewrite_spec.rb +166 -129
  110. data/spec/graphql/introspection/type_type_spec.rb +1 -1
  111. data/spec/graphql/language/lexer_spec.rb +6 -0
  112. data/spec/graphql/object_type_spec.rb +73 -2
  113. data/spec/graphql/query/arguments_spec.rb +28 -0
  114. data/spec/graphql/query/variables_spec.rb +7 -1
  115. data/spec/graphql/query_spec.rb +30 -0
  116. data/spec/graphql/relay/base_connection_spec.rb +26 -8
  117. data/spec/graphql/relay/connection_resolve_spec.rb +45 -0
  118. data/spec/graphql/relay/connection_type_spec.rb +21 -0
  119. data/spec/graphql/relay/node_spec.rb +30 -2
  120. data/spec/graphql/relay/range_add_spec.rb +113 -0
  121. data/spec/graphql/schema/build_from_definition_spec.rb +114 -0
  122. data/spec/graphql/schema/loader_spec.rb +1 -0
  123. data/spec/graphql/schema/printer_spec.rb +2 -2
  124. data/spec/graphql/schema/validation_spec.rb +80 -11
  125. data/spec/graphql/schema/warden_spec.rb +10 -10
  126. data/spec/graphql/schema_spec.rb +18 -1
  127. data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +16 -0
  128. data/spec/graphql/static_validation/rules/fields_will_merge_spec.rb +50 -3
  129. data/spec/graphql/static_validation/rules/fragment_names_are_unique_spec.rb +27 -0
  130. data/spec/graphql/static_validation/rules/fragments_are_finite_spec.rb +57 -0
  131. data/spec/graphql/static_validation/rules/fragments_are_used_spec.rb +1 -1
  132. data/spec/graphql/static_validation/rules/variables_are_input_types_spec.rb +14 -0
  133. data/spec/graphql/string_type_spec.rb +7 -0
  134. data/spec/spec_helper.rb +3 -3
  135. data/spec/support/base_generator_test.rb +7 -0
  136. data/spec/support/dummy/schema.rb +32 -30
  137. data/spec/support/star_wars/schema.rb +81 -23
  138. metadata +98 -20
  139. data/lib/graphql/internal_representation/selection.rb +0 -85
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: dbc1c5f1e53ac27a08d6ffa5956514b54d1fb547
4
- data.tar.gz: 4614d534b6cf6b55140909b0b04af5ce9560b3cd
3
+ metadata.gz: f8a07faba9f807e0d1a1cb2d658dbb03aebd5aff
4
+ data.tar.gz: 7cc789d4ff25519e433770bab5fbdff0421338c4
5
5
  SHA512:
6
- metadata.gz: 39b333cf8e34be84ab666461a8609c11468783a906f927998b31f5a1ff96c4ca40f114769871bb421b9d7f51b21fa4c0aa6ab018ef543713da431ff0983fca75
7
- data.tar.gz: 2d0a5d0e3aaf5d53735ae687a6486ed3c1387bd325c51eb482cc6bb7dbb275ba880e7d5680229118309b2e2056ad23422473f43a4167ae580947e0120ea89d0d
6
+ metadata.gz: c4699ee8d911f2a26824b08f938bec2d3b564f28864491d355d242e91dd79b17de8b301d91f8016409cf76da93e811364620bb24637a81a616093df5a16a2035
7
+ data.tar.gz: f805fd9332370ceb6876dc9e68178a5f1753edb6993e14881eba5b36bb7935502a7419c7a0d03fd0b405533be943f4cc4db50f430b7f61cafd331a3ab97a7858
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+ require 'generators/graphql/type_generator'
3
+
4
+ module Graphql
5
+ module Generators
6
+ # Generate an interface type by name,
7
+ # with the specified fields.
8
+ #
9
+ # ```
10
+ # rails g graphql:interface NamedEntityType name:String!
11
+ # ```
12
+ class EnumGenerator < TypeGeneratorBase
13
+ desc "Create a GraphQL::EnumType with the given name and values"
14
+ source_root File.expand_path('../templates', __FILE__)
15
+
16
+ argument :values,
17
+ type: :array,
18
+ default: [],
19
+ banner: "value{:ruby_value} value{:ruby_value} ...",
20
+ desc: "Values for this enum (if present, ruby_value will be inserted verbatim)"
21
+
22
+ def create_type_file
23
+ template "enum.erb", "app/graphql/types/#{type_file_name}.rb"
24
+ end
25
+
26
+ private
27
+
28
+ def prepared_values
29
+ values.map { |v| v.split(":", 2) }
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+ require "rails/generators/named_base"
3
+
4
+ module Graphql
5
+ module Generators
6
+ class FunctionGenerator < Rails::Generators::NamedBase
7
+ desc "Create a GraphQL::Function by name"
8
+ source_root File.expand_path('../templates', __FILE__)
9
+
10
+ def create_function_file
11
+ template "function.erb", "app/graphql/functions/#{file_name}.rb"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+ require 'rails/generators/base'
3
+
4
+ module Graphql
5
+ module Generators
6
+ # Add GraphQL to a Rails app with `rails g graphql:install`.
7
+ #
8
+ # Setup a folder structure for GraphQL:
9
+ #
10
+ # ```
11
+ # - app/
12
+ # - graphql/
13
+ # - resolvers/
14
+ # - types/
15
+ # - query_type.rb
16
+ # - loaders/
17
+ # - mutations/
18
+ # - {app_name}_schema.rb
19
+ # ```
20
+ #
21
+ # (Add `.gitkeep`s by default, support `--skip-keeps`)
22
+ #
23
+ # Add a controller for serving GraphQL queries:
24
+ #
25
+ # ```
26
+ # app/controllers/graphql_controller.rb
27
+ # ```
28
+ #
29
+ # Add a route for that controller:
30
+ #
31
+ # ```ruby
32
+ # # config/routes.rb
33
+ # post "/graphql", to: "graphql#execute"
34
+ # ```
35
+ #
36
+ # Accept a `--relay` option which adds
37
+ # The root `node(id: ID!)` field.
38
+ #
39
+ # Accept a `--batch` option which adds `GraphQL::Batch` setup.
40
+ #
41
+ # Use `--no-graphiql` to skip `graphiql-rails` installation.
42
+ class InstallGenerator < Rails::Generators::Base
43
+ desc "Install GraphQL folder structure and boilerplate code"
44
+ source_root File.expand_path('../templates', __FILE__)
45
+
46
+ class_option :schema,
47
+ type: :string,
48
+ default: nil,
49
+ desc: "Name for the schema constant (default: {app_name}Schema)"
50
+
51
+ class_option :skip_keeps,
52
+ type: :boolean,
53
+ default: false,
54
+ desc: "Skip .keep files for source control"
55
+
56
+ class_option :skip_graphiql,
57
+ type: :boolean,
58
+ default: false,
59
+ desc: "Skip graphiql-rails installation"
60
+
61
+ class_option :relay,
62
+ type: :boolean,
63
+ default: false,
64
+ desc: "Include GraphQL::Relay installation"
65
+
66
+ class_option :batch,
67
+ type: :boolean,
68
+ default: false,
69
+ desc: "Include GraphQL::Batch installation"
70
+
71
+
72
+ GRAPHIQL_ROUTE = <<-RUBY
73
+ if Rails.env.development?
74
+ mount GraphiQL::Rails::Engine, at: "/graphiql", graphql_path: "/graphql"
75
+ end
76
+ RUBY
77
+
78
+ def create_folder_structure
79
+ create_dir("app/graphql/mutations")
80
+ create_dir("app/graphql/types")
81
+ template("query_type.erb", "app/graphql/types/query_type.rb")
82
+ template("schema.erb", "app/graphql/#{schema_name.underscore}.rb")
83
+ template("graphql_controller.erb", "app/controllers/graphql_controller.rb")
84
+ route('post "/graphql", to: "graphql#execute"')
85
+
86
+ if !options[:skip_graphiql]
87
+ gem("graphiql-rails", group: :development)
88
+ route(GRAPHIQL_ROUTE)
89
+ end
90
+
91
+ if options[:batch]
92
+ gem("graphql-batch")
93
+ create_dir("app/graphql/loaders")
94
+ end
95
+ end
96
+
97
+ private
98
+
99
+ def create_dir(dir)
100
+ empty_directory(dir)
101
+ if !options[:skip_keeps]
102
+ create_file("#{dir}/.keep")
103
+ end
104
+ end
105
+
106
+ def schema_name
107
+ @schema_name ||= begin
108
+ if options[:schema]
109
+ options[:schema]
110
+ else
111
+ require File.expand_path("config/application", destination_root)
112
+ "#{Rails.application.class.parent_name}Schema"
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+ require 'generators/graphql/type_generator'
3
+
4
+ module Graphql
5
+ module Generators
6
+ # Generate an interface type by name,
7
+ # with the specified fields.
8
+ #
9
+ # ```
10
+ # rails g graphql:interface NamedEntityType name:String!
11
+ # ```
12
+ class InterfaceGenerator < TypeGeneratorBase
13
+ desc "Create a GraphQL::InterfaceType with the given name and fields"
14
+ source_root File.expand_path('../templates', __FILE__)
15
+
16
+ argument :fields,
17
+ type: :array,
18
+ default: [],
19
+ banner: "name:type name:type ...",
20
+ desc: "Fields for this interface (type may be expressed as Ruby or GraphQL)"
21
+
22
+ def create_type_file
23
+ template "interface.erb", "app/graphql/types/#{type_file_name}.rb"
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+ require "rails/generators/named_base"
3
+
4
+ module Graphql
5
+ module Generators
6
+ # @example Generate a `GraphQL::Batch` loader by name.
7
+ # rails g graphql:loader RecordLoader
8
+ class LoaderGenerator < Rails::Generators::NamedBase
9
+ desc "Create a GraphQL::Batch::Loader by name"
10
+ source_root File.expand_path('../templates', __FILE__)
11
+
12
+ def create_loader_file
13
+ template "loader.erb", "app/graphql/loaders/#{file_name}.rb"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+ require 'rails/generators/named_base'
3
+
4
+ module Graphql
5
+ module Generators
6
+ # TODO: What other options should be supported?
7
+ #
8
+ # @example Generate a `Relay::Mutation` by name
9
+ # rails g graphql:mutation CreatePostMutation
10
+ class MutationGenerator < Rails::Generators::NamedBase
11
+ desc "Create a Relay mutation by name"
12
+ source_root File.expand_path('../templates', __FILE__)
13
+
14
+ def create_mutation_file
15
+ template "mutation.erb", "app/graphql/mutations/#{file_name}.rb"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+ require 'generators/graphql/type_generator'
3
+
4
+ module Graphql
5
+ module Generators
6
+ # Generate an object type by name,
7
+ # with the specified fields.
8
+ #
9
+ # ```
10
+ # rails g graphql:object PostType name:String!
11
+ # ```
12
+ #
13
+ # Add the Node interface with `--node`.
14
+ class ObjectGenerator < TypeGeneratorBase
15
+ desc "Create a GraphQL::ObjectType with the given name and fields"
16
+ source_root File.expand_path('../templates', __FILE__)
17
+
18
+ argument :fields,
19
+ type: :array,
20
+ default: [],
21
+ banner: "name:type name:type ...",
22
+ desc: "Fields for this object (type may be expressed as Ruby or GraphQL)"
23
+
24
+ class_option :node,
25
+ type: :boolean,
26
+ default: false,
27
+ desc: "Include the Relay Node interface"
28
+
29
+ def create_type_file
30
+ template "object.erb", "app/graphql/types/#{type_file_name}.rb"
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,4 @@
1
+ <%= type_ruby_name %> = GraphQL::EnumType.define do
2
+ name "<%= type_graphql_name %>"
3
+ <% prepared_values.each do |v| %> value "<%= v[0] %>"<%= v.length > 1 ? ", value: #{v[1]}" : "" %>
4
+ <% end %>end
@@ -0,0 +1,17 @@
1
+ class Functions::<%= name %> < GraphQL::Function
2
+ # Define `initialize` to store field-level options, eg
3
+ #
4
+ # field :myField, function: Functions::<%= name %>.new(type: MyType)
5
+ #
6
+ # attr_reader :type
7
+ # def initialize(type:)
8
+ # @type = type
9
+ # end
10
+
11
+ # add arguments by type:
12
+ # argument :id, !GraphQL::ID_TYPE
13
+
14
+ # Resolve function:
15
+ def call(obj, args, ctx)
16
+ end
17
+ end
@@ -0,0 +1,32 @@
1
+ class GraphqlController < ApplicationController
2
+ def execute
3
+ variables = ensure_hash(params[:variables])
4
+ query = params[:query]
5
+ context = {
6
+ # Query context goes here, for example:
7
+ # current_user: current_user,
8
+ }
9
+ result = <%= schema_name %>.execute(query, variables: variables, context: context)
10
+ render json: result
11
+ end
12
+
13
+ private
14
+
15
+ # Handle form data, JSON body, or a blank value
16
+ def ensure_hash(ambiguous_param)
17
+ case ambiguous_param
18
+ when String
19
+ if ambiguous_param.present?
20
+ ensure_hash(JSON.parse(ambiguous_param))
21
+ else
22
+ {}
23
+ end
24
+ when Hash, ActionController::Parameters
25
+ ambiguous_param
26
+ when nil
27
+ {}
28
+ else
29
+ raise ArgumentError, "Unexpected parameter: #{ambiguous_param}"
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,4 @@
1
+ <%= type_ruby_name %> = GraphQL::InterfaceType.define do
2
+ name "<%= type_graphql_name %>"
3
+ <% normalized_fields.each do |f| %> field :<%= f[0] %>, <%= f[1] %>
4
+ <% end %>end
@@ -0,0 +1,15 @@
1
+ class Loaders::<%= name %> < GraphQL::Batch::Loader
2
+ # Define `initialize` to store grouping arguments, eg
3
+ #
4
+ # Loaders::<%= name %>.for(group).load(value)
5
+ #
6
+ # def initialize()
7
+ # end
8
+
9
+ # `keys` contains each key from `.load(key)`.
10
+ # Find the corresponding values, then
11
+ # call `fulfill(key, value)` or `fulfill(key, nil)`
12
+ # for each key.
13
+ def perform(keys)
14
+ end
15
+ end
@@ -0,0 +1,12 @@
1
+ Mutations::<%= name %> = GraphQL::Relay::Mutation.define do
2
+ name "<%= name %>"
3
+ # TODO: define return fields
4
+ # return_field :post, Types::PostType
5
+
6
+ # TODO: define arguments
7
+ # input_field :name, !types.String
8
+
9
+ resolve ->(obj, args, ctx) {
10
+ # TODO: define resolve function
11
+ }
12
+ end
@@ -0,0 +1,5 @@
1
+ <%= type_ruby_name %> = GraphQL::ObjectType.define do
2
+ name "<%= type_graphql_name %>"
3
+ <% if options.node %> implements GraphQL::Relay::Node.interface
4
+ <% end %><% normalized_fields.each do |f| %> field :<%= f[0] %>, <%= f[1] %>
5
+ <% end %>end
@@ -0,0 +1,15 @@
1
+ Types::QueryType = GraphQL::ObjectType.define do
2
+ name "Query"
3
+ # Add root-level fields here.
4
+ # They will be entry points for queries on your schema.
5
+
6
+ # TODO: remove me
7
+ field :testField, types.String do
8
+ description "An example field added by the generator"
9
+ resolve ->(obj, args, ctx) {
10
+ "Hello World!"
11
+ }
12
+ end
13
+ <% if options[:relay] %>
14
+ field :node, GraphQL::Relay::Node.field
15
+ <% end %>end
@@ -0,0 +1,34 @@
1
+ <%= schema_name %> = GraphQL::Schema.define do
2
+ query(Types::QueryType)
3
+ <% if options[:relay] %>
4
+ # Relay Object Identification:
5
+
6
+ # Return a string UUID for `object`
7
+ id_from_object ->(object, type_definition, query_ctx) {
8
+ # Here's a simple implementation which:
9
+ # - joins the type name & object.id
10
+ # - encodes it with base64:
11
+ # GraphQL::Schema::UniqueWithinType.encode(type_definition.name, object.id)
12
+ }
13
+
14
+ # Given a string UUID, find the object
15
+ object_from_id ->(id, query_ctx) {
16
+ # For example, to decode the UUIDs generated above:
17
+ # type_name, item_id = GraphQL::Schema::UniqueWithinType.decode(id)
18
+ #
19
+ # Then, based on `type_name` and `id`
20
+ # find an object in your application
21
+ # ...
22
+ }
23
+
24
+ # Object Resolution
25
+ resolve_type -> (obj, ctx) {
26
+ # TODO: Implement this function
27
+ # to return the correct type for `obj`
28
+ raise(NotImplementedError)
29
+ }
30
+ <% end %><% if options[:batch] %>
31
+ # GraphQL::Batch setup:
32
+ lazy_resolve(Promise, :sync)
33
+ instrument(:query, GraphQL::Batch::Setup)
34
+ <% end %>end
@@ -0,0 +1,4 @@
1
+ <%= type_ruby_name %> = GraphQL::UnionType.define do
2
+ name "<%= type_graphql_name %>"
3
+ <% if possible_types.any? %> possible_types [<%= normalized_possible_types.join(", ") %>]
4
+ <% end %>end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+ require 'rails/generators/base'
3
+ require 'graphql'
4
+
5
+ module Graphql
6
+ module Generators
7
+ class TypeGeneratorBase < Rails::Generators::Base
8
+ argument :type_name,
9
+ type: :string,
10
+ required: true,
11
+ banner: "TypeName",
12
+ desc: "Name of this object type (expressed as Ruby or GraphQL)"
13
+
14
+ # Take a type expression in any combination of GraphQL or Ruby styles
15
+ # and return it in a specified output style
16
+ # TODO: nullability / list with `mode: :graphql` doesn't work
17
+ # @param type_expresson [String]
18
+ # @param mode [Symbol]
19
+ # @return [String]
20
+ def self.normalize_type_expression(type_expression, mode:)
21
+ if type_expression.start_with?("!")
22
+ "!#{normalize_type_expression(type_expression[1..-1], mode: mode)}"
23
+ elsif type_expression.end_with?("!")
24
+ "!#{normalize_type_expression(type_expression[0..-2], mode: mode)}"
25
+ elsif type_expression.start_with?("[") && type_expression.end_with?("]")
26
+ "types[#{normalize_type_expression(type_expression[1..-2], mode: mode)}]"
27
+ elsif type_expression.start_with?("types[") && type_expression.end_with?("]")
28
+ "types[#{normalize_type_expression(type_expression[6..-2], mode: mode)}]"
29
+ elsif type_expression.end_with?("Type")
30
+ normalize_type_expression(type_expression[0..-5], mode: mode)
31
+ elsif type_expression.start_with?("Types::")
32
+ normalize_type_expression(type_expression[7..-1], mode: mode)
33
+ elsif type_expression.start_with?("types.")
34
+ normalize_type_expression(type_expression[6..-1], mode: mode)
35
+ else
36
+ case mode
37
+ when :ruby
38
+ case type_expression
39
+ when "Int", "Float", "Boolean", "String", "ID"
40
+ "types.#{type_expression}"
41
+ else
42
+ "Types::#{type_expression}Type"
43
+ end
44
+ when :graphql
45
+ type_expression
46
+ else
47
+ raise "Unexpected normalize mode: #{mode}"
48
+ end
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ # @return [String] The user-provided type name, normalized to Ruby code
55
+ def type_ruby_name
56
+ @type_ruby_name ||= self.class.normalize_type_expression(type_name, mode: :ruby)
57
+ end
58
+
59
+ # @return [String] The user-provided type name, as a GraphQL name
60
+ def type_graphql_name
61
+ @type_graphql_name ||= self.class.normalize_type_expression(type_name, mode: :graphql)
62
+ end
63
+
64
+ # @return [String] The user-provided type name, as a file name (without extension)
65
+ def type_file_name
66
+ @type_file_name ||= "#{type_graphql_name}Type".underscore
67
+ end
68
+
69
+ # @return [Array<Array(String, String)>>] User-provided fields, in `(name, Ruby type name)` pairs
70
+ def normalized_fields
71
+ @normalized_fields ||= fields.map { |f|
72
+ name, raw_type = f.split(":", 2)
73
+ [name, self.class.normalize_type_expression(raw_type, mode: :ruby)]
74
+ }
75
+ end
76
+ end
77
+ end
78
+ end