graphql_rails 0.8.0 → 1.2.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/.hound.yml +1 -0
- data/.rubocop.yml +3 -3
- data/.ruby-version +1 -1
- data/.travis.yml +2 -2
- data/CHANGELOG.md +31 -0
- data/Gemfile +3 -2
- data/Gemfile.lock +147 -124
- data/docs/README.md +24 -8
- data/docs/_sidebar.md +3 -0
- data/docs/components/controller.md +194 -21
- data/docs/components/model.md +193 -5
- data/docs/components/routes.md +28 -0
- data/docs/getting_started/quick_start.md +10 -3
- data/docs/index.html +1 -1
- data/docs/other_tools/query_runner.md +49 -0
- data/docs/other_tools/schema_dump.md +29 -0
- data/docs/testing/testing.md +3 -1
- data/graphql_rails.gemspec +5 -5
- data/lib/generators/graphql_rails/install_generator.rb +50 -0
- data/lib/generators/graphql_rails/templates/example_users_controller.erb +19 -0
- data/lib/generators/graphql_rails/templates/graphql_application_controller.erb +8 -0
- data/lib/generators/graphql_rails/templates/graphql_controller.erb +20 -0
- data/lib/generators/graphql_rails/templates/graphql_router.erb +19 -0
- data/lib/generators/graphql_rails/templates/graphql_router_spec.erb +21 -0
- data/lib/graphql_rails.rb +2 -0
- data/lib/graphql_rails/attributes/attributable.rb +20 -21
- data/lib/graphql_rails/attributes/attribute.rb +41 -4
- data/lib/graphql_rails/attributes/attribute_name_parser.rb +4 -4
- data/lib/graphql_rails/attributes/input_attribute.rb +24 -10
- data/lib/graphql_rails/attributes/input_type_parser.rb +24 -46
- data/lib/graphql_rails/attributes/type_parseable.rb +132 -0
- data/lib/graphql_rails/attributes/type_parser.rb +58 -54
- data/lib/graphql_rails/concerns/service.rb +19 -0
- data/lib/graphql_rails/controller.rb +26 -22
- data/lib/graphql_rails/controller/action.rb +12 -67
- data/lib/graphql_rails/controller/action_configuration.rb +70 -34
- data/lib/graphql_rails/controller/build_controller_action_resolver.rb +52 -0
- data/lib/graphql_rails/controller/build_controller_action_resolver/controller_action_resolver.rb +28 -0
- data/lib/graphql_rails/controller/configuration.rb +56 -5
- data/lib/graphql_rails/controller/log_controller_action.rb +11 -6
- data/lib/graphql_rails/controller/request.rb +29 -8
- data/lib/graphql_rails/controller/request/format_errors.rb +58 -0
- data/lib/graphql_rails/decorator/relation_decorator.rb +1 -5
- data/lib/graphql_rails/errors/custom_execution_error.rb +22 -0
- data/lib/graphql_rails/errors/execution_error.rb +6 -7
- data/lib/graphql_rails/errors/system_error.rb +14 -0
- data/lib/graphql_rails/errors/validation_error.rb +1 -5
- data/lib/graphql_rails/input_configurable.rb +47 -0
- data/lib/graphql_rails/model.rb +19 -4
- data/lib/graphql_rails/model/add_fields_to_graphql_type.rb +45 -0
- data/lib/graphql_rails/model/build_connection_type.rb +52 -0
- data/lib/graphql_rails/model/{configuration → build_connection_type}/count_items.rb +5 -5
- data/lib/graphql_rails/model/build_enum_type.rb +39 -10
- data/lib/graphql_rails/model/build_graphql_input_type.rb +8 -4
- data/lib/graphql_rails/model/call_graphql_model_method.rb +72 -0
- data/lib/graphql_rails/model/configurable.rb +6 -2
- data/lib/graphql_rails/model/configuration.rb +11 -10
- data/lib/graphql_rails/model/find_or_build_graphql_type.rb +64 -0
- data/lib/graphql_rails/model/find_or_build_graphql_type_class.rb +46 -0
- data/lib/graphql_rails/model/input.rb +10 -6
- data/lib/graphql_rails/query_runner.rb +68 -0
- data/lib/graphql_rails/railtie.rb +10 -0
- data/lib/graphql_rails/router.rb +40 -13
- data/lib/graphql_rails/router/resource_routes_builder.rb +10 -9
- data/lib/graphql_rails/router/route.rb +21 -6
- data/lib/graphql_rails/router/schema_builder.rb +30 -11
- data/lib/graphql_rails/rspec_controller_helpers.rb +6 -4
- data/lib/graphql_rails/tasks/dump_graphql_schema.rb +57 -0
- data/lib/graphql_rails/tasks/schema.rake +14 -0
- data/lib/graphql_rails/version.rb +1 -1
- metadata +48 -21
- data/lib/graphql_rails/controller/controller_function.rb +0 -50
- data/lib/graphql_rails/controller/format_results.rb +0 -36
- data/lib/graphql_rails/model/build_graphql_type.rb +0 -37
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphqlRails
|
4
|
+
module Model
|
5
|
+
# Initializes class to define graphql type and fields.
|
6
|
+
class FindOrBuildGraphqlTypeClass
|
7
|
+
require 'graphql_rails/concerns/service'
|
8
|
+
|
9
|
+
include ::GraphqlRails::Service
|
10
|
+
|
11
|
+
def initialize(name:, type_name:, description: nil)
|
12
|
+
@name = name
|
13
|
+
@type_name = type_name
|
14
|
+
@description = description
|
15
|
+
@new_class = false
|
16
|
+
end
|
17
|
+
|
18
|
+
def klass
|
19
|
+
@klass ||= Object.const_defined?(type_name) && Object.const_get(type_name) || build_graphql_type_klass
|
20
|
+
end
|
21
|
+
|
22
|
+
def new_class?
|
23
|
+
new_class
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
attr_accessor :new_class
|
29
|
+
attr_reader :name, :type_name, :description
|
30
|
+
|
31
|
+
def build_graphql_type_klass
|
32
|
+
graphql_type_name = name
|
33
|
+
graphql_type_description = description
|
34
|
+
|
35
|
+
graphql_type_klass = Class.new(GraphQL::Schema::Object) do
|
36
|
+
graphql_name(graphql_type_name)
|
37
|
+
description(graphql_type_description)
|
38
|
+
end
|
39
|
+
|
40
|
+
self.new_class = true
|
41
|
+
|
42
|
+
Object.const_set(type_name, graphql_type_klass)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -14,6 +14,11 @@ module GraphqlRails
|
|
14
14
|
@input_name_suffix = input_name_suffix
|
15
15
|
end
|
16
16
|
|
17
|
+
def initialize_copy(other)
|
18
|
+
super
|
19
|
+
@attributes = other.instance_variable_get(:@attributes)&.transform_values(&:dup)
|
20
|
+
end
|
21
|
+
|
17
22
|
def graphql_input_type
|
18
23
|
@graphql_input_type ||= BuildGraphqlInputType.call(
|
19
24
|
name: name, description: description, attributes: attributes
|
@@ -21,12 +26,11 @@ module GraphqlRails
|
|
21
26
|
end
|
22
27
|
|
23
28
|
def attribute(attribute_name, type: nil, enum: nil, **attribute_options)
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
)
|
29
|
+
input_type = attribute_type(attribute_name, type: type, enum: enum, **attribute_options)
|
30
|
+
|
31
|
+
attributes[attribute_name.to_s] = Attributes::InputAttribute.new(
|
32
|
+
attribute_name, type: input_type, **attribute_options
|
33
|
+
)
|
30
34
|
end
|
31
35
|
|
32
36
|
private
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphqlRails
|
4
|
+
# executes GraphQL queries and returns json
|
5
|
+
class QueryRunner
|
6
|
+
require 'graphql_rails/router'
|
7
|
+
require 'graphql_rails/concerns/service'
|
8
|
+
|
9
|
+
include ::GraphqlRails::Service
|
10
|
+
|
11
|
+
def initialize(group: nil, params:, schema: nil, router: nil, **schema_options)
|
12
|
+
@group = group
|
13
|
+
@graphql_schema = schema
|
14
|
+
@params = params
|
15
|
+
@schema_options = schema_options
|
16
|
+
@router = router
|
17
|
+
end
|
18
|
+
|
19
|
+
def call
|
20
|
+
graphql_schema.execute(
|
21
|
+
params[:query],
|
22
|
+
variables: variables,
|
23
|
+
operation_name: params[:operationName],
|
24
|
+
**schema_options
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
attr_reader :schema_options, :params, :group
|
31
|
+
|
32
|
+
def variables
|
33
|
+
ensure_hash(params[:variables])
|
34
|
+
end
|
35
|
+
|
36
|
+
def graphql_schema
|
37
|
+
@graphql_schema ||= router_schema
|
38
|
+
end
|
39
|
+
|
40
|
+
def router
|
41
|
+
@router ||= ::GraphqlRouter
|
42
|
+
end
|
43
|
+
|
44
|
+
def router_schema
|
45
|
+
router.graphql_schema(group)
|
46
|
+
end
|
47
|
+
|
48
|
+
def ensure_hash(ambiguous_param)
|
49
|
+
if ambiguous_param.blank?
|
50
|
+
{}
|
51
|
+
elsif ambiguous_param.is_a?(String)
|
52
|
+
ensure_hash(JSON.parse(ambiguous_param))
|
53
|
+
elsif kind_of_hash?(ambiguous_param)
|
54
|
+
ambiguous_param
|
55
|
+
else
|
56
|
+
raise ArgumentError, "Unexpected parameter: #{ambiguous_param.inspect}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def kind_of_hash?(object)
|
61
|
+
return true if object.is_a?(Hash)
|
62
|
+
|
63
|
+
defined?(ActionController) &&
|
64
|
+
defined?(ActionController::Parameters) &&
|
65
|
+
object.is_a?(ActionController::Parameters)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/lib/graphql_rails/router.rb
CHANGED
@@ -11,32 +11,39 @@ module GraphqlRails
|
|
11
11
|
# graphql router that mimics Rails.application.routes
|
12
12
|
class Router
|
13
13
|
RAW_ACTION_NAMES = %i[
|
14
|
-
rescue_from query_analyzer instrument cursor_encoder default_max_page_size
|
14
|
+
use rescue_from query_analyzer instrument cursor_encoder default_max_page_size
|
15
15
|
].freeze
|
16
16
|
|
17
17
|
def self.draw(&block)
|
18
|
-
|
19
|
-
|
20
|
-
|
18
|
+
new.tap do |router|
|
19
|
+
router.instance_eval(&block)
|
20
|
+
end
|
21
21
|
end
|
22
22
|
|
23
23
|
attr_reader :routes, :namespace_name, :raw_graphql_actions
|
24
24
|
|
25
|
-
def initialize(module_name: '')
|
25
|
+
def initialize(module_name: '', group_names: [])
|
26
26
|
@module_name = module_name
|
27
|
+
@group_names = group_names
|
27
28
|
@routes ||= Set.new
|
28
29
|
@raw_graphql_actions ||= []
|
29
30
|
end
|
30
31
|
|
32
|
+
def group(*group_names, &block)
|
33
|
+
scoped_router = router_with(group_names: group_names)
|
34
|
+
scoped_router.instance_eval(&block)
|
35
|
+
routes.merge(scoped_router.routes)
|
36
|
+
end
|
37
|
+
|
31
38
|
def scope(**options, &block)
|
32
39
|
full_module_name = [module_name, options[:module]].reject(&:empty?).join('/')
|
33
|
-
scoped_router =
|
40
|
+
scoped_router = router_with(module_name: full_module_name)
|
34
41
|
scoped_router.instance_eval(&block)
|
35
42
|
routes.merge(scoped_router.routes)
|
36
43
|
end
|
37
44
|
|
38
45
|
def resources(name, **options, &block)
|
39
|
-
builder_options =
|
46
|
+
builder_options = full_route_options(options)
|
40
47
|
routes_builder = ResourceRoutesBuilder.new(name, **builder_options)
|
41
48
|
routes_builder.instance_eval(&block) if block
|
42
49
|
routes.merge(routes_builder.routes)
|
@@ -56,25 +63,45 @@ module GraphqlRails
|
|
56
63
|
end
|
57
64
|
end
|
58
65
|
|
59
|
-
def graphql_schema
|
60
|
-
|
66
|
+
def graphql_schema(group = nil)
|
67
|
+
@graphql_schema ||= {}
|
68
|
+
@graphql_schema[group&.to_sym] ||= SchemaBuilder.new(
|
61
69
|
queries: routes.select(&:query?),
|
62
70
|
mutations: routes.select(&:mutation?),
|
63
|
-
raw_actions: raw_graphql_actions
|
71
|
+
raw_actions: raw_graphql_actions,
|
72
|
+
group: group
|
64
73
|
).call
|
65
74
|
end
|
66
75
|
|
76
|
+
def reload_schema
|
77
|
+
@graphql_schema = nil
|
78
|
+
end
|
79
|
+
|
67
80
|
private
|
68
81
|
|
69
|
-
attr_reader :module_name
|
82
|
+
attr_reader :module_name, :group_names
|
83
|
+
|
84
|
+
def router_with(new_router_options = {})
|
85
|
+
default_options = { module_name: module_name, group_names: group_names }
|
86
|
+
full_options = default_options.merge(new_router_options)
|
87
|
+
|
88
|
+
self.class.new(**full_options)
|
89
|
+
end
|
70
90
|
|
71
91
|
def add_raw_action(name, *args, &block)
|
72
92
|
raw_graphql_actions << { name: name, args: args, block: block }
|
73
93
|
end
|
74
94
|
|
75
95
|
def build_route(route_builder, name, **options)
|
76
|
-
|
77
|
-
|
96
|
+
route_builder.new(name, **full_route_options(options))
|
97
|
+
end
|
98
|
+
|
99
|
+
def full_route_options(extra_options)
|
100
|
+
extra_groups = Array(extra_options[:group]) + Array(extra_options[:groups])
|
101
|
+
extra_options = extra_options.except(:group, :groups)
|
102
|
+
groups = (group_names + extra_groups).uniq
|
103
|
+
|
104
|
+
default_route_options.merge(extra_options).merge(groups: groups)
|
78
105
|
end
|
79
106
|
|
80
107
|
def default_route_options
|
@@ -20,12 +20,12 @@ module GraphqlRails
|
|
20
20
|
@routes ||= initial_routes
|
21
21
|
end
|
22
22
|
|
23
|
-
def query(*args)
|
24
|
-
routes << build_query(*args)
|
23
|
+
def query(*args, **kwargs)
|
24
|
+
routes << build_query(*args, **kwargs)
|
25
25
|
end
|
26
26
|
|
27
|
-
def mutation(*args)
|
28
|
-
routes << build_mutation(*args)
|
27
|
+
def mutation(*args, **kwargs)
|
28
|
+
routes << build_mutation(*args, **kwargs)
|
29
29
|
end
|
30
30
|
|
31
31
|
private
|
@@ -54,12 +54,12 @@ module GraphqlRails
|
|
54
54
|
routes
|
55
55
|
end
|
56
56
|
|
57
|
-
def build_mutation(*args)
|
58
|
-
build_route(MutationRoute, *args)
|
57
|
+
def build_mutation(*args, **kwargs)
|
58
|
+
build_route(MutationRoute, *args, **kwargs)
|
59
59
|
end
|
60
60
|
|
61
|
-
def build_query(*args)
|
62
|
-
build_route(QueryRoute, *args)
|
61
|
+
def build_query(*args, **kwargs)
|
62
|
+
build_route(QueryRoute, *args, **kwargs)
|
63
63
|
end
|
64
64
|
|
65
65
|
# rubocop:disable Metrics/ParameterLists
|
@@ -70,8 +70,9 @@ module GraphqlRails
|
|
70
70
|
end
|
71
71
|
|
72
72
|
action_options = options.merge(custom_options).merge(on: on)
|
73
|
+
controller_method_name = action.to_s.underscore
|
73
74
|
action_name = [prefix, resource_name(on), suffix_name].map(&:to_s).reject(&:empty?).join('_')
|
74
|
-
builder.new(action_name, to: "#{name}##{
|
75
|
+
builder.new(action_name, to: "#{name}##{controller_method_name}", **action_options)
|
75
76
|
end
|
76
77
|
# rubocop:enable Metrics/ParameterLists
|
77
78
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '../controller/
|
3
|
+
require_relative '../controller/build_controller_action_resolver'
|
4
4
|
|
5
5
|
module GraphqlRails
|
6
6
|
class Router
|
@@ -8,10 +8,11 @@ module GraphqlRails
|
|
8
8
|
class Route
|
9
9
|
attr_reader :name, :module_name, :on, :relative_path
|
10
10
|
|
11
|
-
def initialize(name, to: '', on:, **options)
|
11
|
+
def initialize(name, to: '', on:, groups: nil, **options)
|
12
12
|
@name = name.to_s.camelize(:lower)
|
13
13
|
@module_name = options[:module].to_s
|
14
14
|
@function = options[:function]
|
15
|
+
@groups = groups
|
15
16
|
@relative_path = to
|
16
17
|
@on = on.to_sym
|
17
18
|
end
|
@@ -26,12 +27,26 @@ module GraphqlRails
|
|
26
27
|
on == :collection
|
27
28
|
end
|
28
29
|
|
29
|
-
def
|
30
|
-
|
30
|
+
def show_in_group?(group_name)
|
31
|
+
return true if groups.nil? || groups.empty?
|
32
|
+
|
33
|
+
groups.include?(group_name&.to_sym)
|
34
|
+
end
|
35
|
+
|
36
|
+
def field_options
|
37
|
+
if function
|
38
|
+
{ function: function }
|
39
|
+
else
|
40
|
+
{ resolver: resolver }
|
41
|
+
end
|
31
42
|
end
|
32
43
|
|
33
|
-
|
34
|
-
|
44
|
+
private
|
45
|
+
|
46
|
+
attr_reader :function, :groups
|
47
|
+
|
48
|
+
def resolver
|
49
|
+
@resolver ||= Controller::BuildControllerActionResolver.call(route: self)
|
35
50
|
end
|
36
51
|
end
|
37
52
|
end
|
@@ -8,34 +8,53 @@ module GraphqlRails
|
|
8
8
|
|
9
9
|
attr_reader :queries, :mutations, :raw_actions
|
10
10
|
|
11
|
-
def initialize(queries:, mutations:, raw_actions:)
|
11
|
+
def initialize(queries:, mutations:, raw_actions:, group: nil)
|
12
12
|
@queries = queries
|
13
13
|
@mutations = mutations
|
14
14
|
@raw_actions = raw_actions
|
15
|
+
@group = group
|
15
16
|
end
|
16
17
|
|
17
18
|
def call
|
18
|
-
query_type =
|
19
|
-
mutation_type =
|
19
|
+
query_type = build_group_type('Query', queries)
|
20
|
+
mutation_type = build_group_type('Mutation', mutations)
|
20
21
|
raw = raw_actions
|
21
22
|
|
22
|
-
GraphQL::Schema
|
23
|
+
Class.new(GraphQL::Schema) do
|
24
|
+
connections.add(
|
25
|
+
GraphqlRails::Decorator::RelationDecorator,
|
26
|
+
GraphQL::Pagination::ActiveRecordRelationConnection
|
27
|
+
)
|
23
28
|
cursor_encoder(Router::PlainCursorEncoder)
|
24
29
|
raw.each { |action| send(action[:name], *action[:args], &action[:block]) }
|
25
30
|
|
26
|
-
query(query_type)
|
27
|
-
mutation(mutation_type)
|
31
|
+
query(query_type) if query_type
|
32
|
+
mutation(mutation_type) if mutation_type
|
28
33
|
end
|
29
34
|
end
|
30
35
|
|
31
36
|
private
|
32
37
|
|
33
|
-
|
34
|
-
GraphQL::ObjectType.define do
|
35
|
-
name type_name
|
38
|
+
attr_reader :group
|
36
39
|
|
37
|
-
|
38
|
-
|
40
|
+
def build_group_type(type_name, routes)
|
41
|
+
group_name = group
|
42
|
+
group_routes = routes.select { |route| route.show_in_group?(group_name) }
|
43
|
+
return if group_routes.empty?
|
44
|
+
|
45
|
+
build_type(type_name, group_routes)
|
46
|
+
end
|
47
|
+
|
48
|
+
def build_type(type_name, group_routes)
|
49
|
+
Class.new(GraphQL::Schema::Object) do
|
50
|
+
graphql_name(type_name)
|
51
|
+
|
52
|
+
group_routes.each do |route|
|
53
|
+
field(*route.name, **route.field_options)
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.inspect
|
57
|
+
"#{GraphQL::Schema::Object}(#{graphql_name})"
|
39
58
|
end
|
40
59
|
end
|
41
60
|
end
|
@@ -82,11 +82,13 @@ module GraphqlRails
|
|
82
82
|
action_by_name = config.action_by_name
|
83
83
|
controller_path = controller.name.underscore.sub(/_controller\Z/, '')
|
84
84
|
|
85
|
-
Router.draw do
|
85
|
+
router = Router.draw do
|
86
86
|
action_by_name.keys.each do |action_name|
|
87
|
-
query("#{action_name}_test", to: "#{controller_path}##{action_name}")
|
87
|
+
query("#{action_name}_test", to: "#{controller_path}##{action_name}", group: :graphql_rspec_helpers)
|
88
88
|
end
|
89
89
|
end
|
90
|
+
|
91
|
+
router.graphql_schema(:graphql_rspec_helpers)
|
90
92
|
end
|
91
93
|
end
|
92
94
|
|
@@ -107,8 +109,8 @@ module GraphqlRails
|
|
107
109
|
@response
|
108
110
|
end
|
109
111
|
|
110
|
-
def mutation(*args)
|
111
|
-
query(*args)
|
112
|
+
def mutation(*args, **kwargs)
|
113
|
+
query(*args, **kwargs)
|
112
114
|
end
|
113
115
|
|
114
116
|
def response
|