graphql_rails 0.8.0 → 1.0.0
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/CHANGELOG.md +13 -0
- data/Gemfile.lock +20 -23
- data/docs/README.md +21 -5
- data/docs/_sidebar.md +3 -0
- data/docs/components/controller.md +174 -17
- data/docs/components/model.md +151 -3
- data/docs/components/routes.md +28 -0
- data/docs/getting_started/quick_start.md +9 -2
- 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 +1 -1
- 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 +18 -0
- data/lib/graphql_rails.rb +2 -0
- data/lib/graphql_rails/attributes/attributable.rb +16 -13
- data/lib/graphql_rails/attributes/attribute.rb +20 -3
- data/lib/graphql_rails/attributes/input_attribute.rb +20 -10
- data/lib/graphql_rails/attributes/input_type_parser.rb +24 -46
- data/lib/graphql_rails/attributes/type_parseable.rb +128 -0
- data/lib/graphql_rails/attributes/type_parser.rb +58 -54
- data/lib/graphql_rails/concerns/service.rb +15 -0
- data/lib/graphql_rails/controller.rb +20 -16
- data/lib/graphql_rails/controller/action.rb +10 -69
- 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 +4 -4
- 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 -1
- 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/build_connection_type.rb +48 -0
- data/lib/graphql_rails/model/{configuration → build_connection_type}/count_items.rb +4 -4
- data/lib/graphql_rails/model/build_enum_type.rb +39 -10
- data/lib/graphql_rails/model/build_graphql_input_type.rb +7 -3
- data/lib/graphql_rails/model/build_graphql_type.rb +23 -7
- data/lib/graphql_rails/model/call_graphql_model_method.rb +59 -0
- data/lib/graphql_rails/model/configuration.rb +2 -6
- 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 +2 -1
- data/lib/graphql_rails/router/route.rb +25 -6
- data/lib/graphql_rails/router/schema_builder.rb +26 -11
- data/lib/graphql_rails/rspec_controller_helpers.rb +4 -2
- 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 +29 -9
- data/lib/graphql_rails/controller/controller_function.rb +0 -50
- data/lib/graphql_rails/controller/format_results.rb +0 -36
@@ -5,9 +5,13 @@ require 'graphql'
|
|
5
5
|
module GraphqlRails
|
6
6
|
module Attributes
|
7
7
|
# converts string value in to GraphQL type
|
8
|
-
class InputTypeParser
|
8
|
+
class InputTypeParser
|
9
|
+
require_relative './type_parseable'
|
10
|
+
|
11
|
+
include TypeParseable
|
12
|
+
|
9
13
|
def initialize(unparsed_type, subtype:)
|
10
|
-
|
14
|
+
@unparsed_type = unparsed_type
|
11
15
|
@subtype = subtype
|
12
16
|
end
|
13
17
|
|
@@ -17,68 +21,42 @@ module GraphqlRails
|
|
17
21
|
partly_parsed_type || parsed_type
|
18
22
|
end
|
19
23
|
|
20
|
-
def
|
21
|
-
return nil if unparsed_type.nil?
|
22
|
-
|
23
|
-
partly_parsed_type || parsed_nullable_type
|
24
|
-
end
|
25
|
-
|
26
|
-
protected
|
27
|
-
|
28
|
-
def partly_parsed_type
|
29
|
-
return unparsed_type if raw_graphql_type?
|
30
|
-
return unparsed_type.graphql_input_type if unparsed_type.is_a?(GraphqlRails::Model::Input)
|
31
|
-
end
|
32
|
-
|
33
|
-
def parsed_nullable_type
|
34
|
-
if list?
|
35
|
-
parsed_inner_type.to_list_type
|
36
|
-
else
|
37
|
-
type_by_name
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def parsed_type
|
24
|
+
def input_type_arg
|
42
25
|
if list?
|
43
|
-
|
26
|
+
list_type_arg
|
44
27
|
else
|
45
|
-
|
28
|
+
unwrapped_type
|
46
29
|
end
|
47
30
|
end
|
48
31
|
|
49
|
-
|
50
|
-
unparsed_type.is_a?(GraphQL::InputObjectType) || super
|
51
|
-
end
|
32
|
+
private
|
52
33
|
|
53
|
-
|
54
|
-
type_class = graphql_model
|
55
|
-
return unless type_class
|
34
|
+
attr_reader :unparsed_type, :subtype
|
56
35
|
|
57
|
-
|
36
|
+
def unwrapped_type
|
37
|
+
raw_unwrapped_type || unwrapped_scalar_type || unwrapped_model_input_type || raise_not_supported_type_error
|
58
38
|
end
|
59
39
|
|
60
|
-
def
|
61
|
-
|
40
|
+
def raw_unwrapped_type
|
41
|
+
return nil unless raw_graphql_type?
|
62
42
|
|
63
|
-
|
64
|
-
list_type = list_type.to_graphql if list_type.respond_to?(:to_graphql)
|
65
|
-
list_type.to_non_null_type
|
66
|
-
else
|
67
|
-
list_type
|
68
|
-
end
|
43
|
+
unwrap_type(unparsed_type)
|
69
44
|
end
|
70
45
|
|
71
|
-
def
|
46
|
+
def list_type_arg
|
72
47
|
if required_inner_type?
|
73
|
-
|
48
|
+
[unwrapped_type]
|
74
49
|
else
|
75
|
-
|
50
|
+
[unwrapped_type, null: true]
|
76
51
|
end
|
77
52
|
end
|
78
53
|
|
79
|
-
|
54
|
+
def unwrapped_model_input_type
|
55
|
+
type_class = graphql_model
|
56
|
+
return unless type_class
|
80
57
|
|
81
|
-
|
58
|
+
type_class.graphql.input(subtype).graphql_input_type
|
59
|
+
end
|
82
60
|
end
|
83
61
|
end
|
84
62
|
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphqlRails
|
4
|
+
module Attributes
|
5
|
+
# Contains shared parsing logic.
|
6
|
+
# Expects that including class has:
|
7
|
+
# * method "unparsed_type" which might be Instance of String, Symbol, GraphQL type or so
|
8
|
+
module TypeParseable
|
9
|
+
require_relative './type_name_info'
|
10
|
+
|
11
|
+
class UnknownTypeError < ArgumentError; end
|
12
|
+
|
13
|
+
TYPE_MAPPING = {
|
14
|
+
'id' => GraphQL::ID_TYPE,
|
15
|
+
|
16
|
+
'int' => GraphQL::INT_TYPE,
|
17
|
+
'integer' => GraphQL::INT_TYPE,
|
18
|
+
|
19
|
+
'string' => GraphQL::STRING_TYPE,
|
20
|
+
'str' => GraphQL::STRING_TYPE,
|
21
|
+
'text' => GraphQL::STRING_TYPE,
|
22
|
+
'time' => GraphQL::STRING_TYPE,
|
23
|
+
'date' => GraphQL::STRING_TYPE,
|
24
|
+
|
25
|
+
'bool' => GraphQL::BOOLEAN_TYPE,
|
26
|
+
'boolean' => GraphQL::BOOLEAN_TYPE,
|
27
|
+
|
28
|
+
'float' => GraphQL::FLOAT_TYPE,
|
29
|
+
'double' => GraphQL::FLOAT_TYPE,
|
30
|
+
'decimal' => GraphQL::FLOAT_TYPE
|
31
|
+
}.freeze
|
32
|
+
|
33
|
+
WRAPPER_TYPES = [
|
34
|
+
GraphQL::Schema::List,
|
35
|
+
GraphQL::Schema::NonNull,
|
36
|
+
GraphQL::NonNullType,
|
37
|
+
GraphQL::ListType
|
38
|
+
].freeze
|
39
|
+
|
40
|
+
GRAPHQL_BASE_TYPES = [
|
41
|
+
GraphQL::BaseType,
|
42
|
+
GraphQL::ObjectType,
|
43
|
+
GraphQL::InputObjectType
|
44
|
+
].freeze
|
45
|
+
|
46
|
+
RAW_GRAPHQL_TYPES = (WRAPPER_TYPES + GRAPHQL_BASE_TYPES).freeze
|
47
|
+
|
48
|
+
def unwrapped_scalar_type
|
49
|
+
TYPE_MAPPING[nullable_inner_name.downcase.downcase]
|
50
|
+
end
|
51
|
+
|
52
|
+
def raw_graphql_type?
|
53
|
+
return true if RAW_GRAPHQL_TYPES.detect { |raw_type| unparsed_type.is_a?(raw_type) }
|
54
|
+
|
55
|
+
defined?(GraphQL::Schema::Member) &&
|
56
|
+
unparsed_type.is_a?(Class) &&
|
57
|
+
unparsed_type < GraphQL::Schema::Member
|
58
|
+
end
|
59
|
+
|
60
|
+
def graphql_model
|
61
|
+
type_class = \
|
62
|
+
if unparsed_type.is_a?(Class) && unparsed_type < GraphqlRails::Model
|
63
|
+
unparsed_type
|
64
|
+
else
|
65
|
+
nullable_inner_name.safe_constantize
|
66
|
+
end
|
67
|
+
|
68
|
+
return if type_class.nil?
|
69
|
+
return unless type_class < GraphqlRails::Model
|
70
|
+
|
71
|
+
type_class
|
72
|
+
end
|
73
|
+
|
74
|
+
protected
|
75
|
+
|
76
|
+
def unwrap_type(type)
|
77
|
+
unwrappable = type
|
78
|
+
unwrappable = unwrappable.of_type while wrapped_type?(unwrappable)
|
79
|
+
unwrappable
|
80
|
+
end
|
81
|
+
|
82
|
+
def wrapped_type?(type)
|
83
|
+
WRAPPER_TYPES.any? { |wrapper| type.is_a?(wrapper) }
|
84
|
+
end
|
85
|
+
|
86
|
+
def nullable_inner_name
|
87
|
+
type_name_info.nullable_inner_name
|
88
|
+
end
|
89
|
+
|
90
|
+
def list?
|
91
|
+
type_name_info.list?
|
92
|
+
end
|
93
|
+
|
94
|
+
def required_inner_type?
|
95
|
+
type_name_info.required_inner_type?
|
96
|
+
end
|
97
|
+
|
98
|
+
def required_list?
|
99
|
+
type_name_info.required_list?
|
100
|
+
end
|
101
|
+
|
102
|
+
def required?
|
103
|
+
type_name_info.required?
|
104
|
+
end
|
105
|
+
|
106
|
+
def type_name_info
|
107
|
+
@type_name_info ||= begin
|
108
|
+
type_name = \
|
109
|
+
if unparsed_type.respond_to?(:to_type_signature)
|
110
|
+
unparsed_type.to_type_signature
|
111
|
+
else
|
112
|
+
unparsed_type.to_s
|
113
|
+
end
|
114
|
+
TypeNameInfo.new(type_name)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def raise_not_supported_type_error
|
119
|
+
error_message = \
|
120
|
+
"Type #{unparsed_type.inspect} is not supported. " \
|
121
|
+
"Supported scalar types are: #{TypeParseable::TYPE_MAPPING.keys}. " \
|
122
|
+
'All the classes that includes `GraphqlRails::Model` are also supported as types.'
|
123
|
+
|
124
|
+
raise UnknownTypeError, error_message
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -1,44 +1,29 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'graphql'
|
4
|
+
require 'graphql_rails/model/build_connection_type'
|
5
|
+
require 'graphql_rails/errors/error'
|
4
6
|
|
5
7
|
module GraphqlRails
|
6
8
|
module Attributes
|
7
9
|
# converts string value in to GraphQL type
|
8
10
|
class TypeParser
|
9
11
|
require_relative './type_name_info'
|
12
|
+
require_relative './type_parseable'
|
10
13
|
|
11
|
-
class
|
14
|
+
class NotSupportedFeature < GraphqlRails::Error; end
|
12
15
|
|
13
|
-
|
14
|
-
'id' => GraphQL::ID_TYPE,
|
16
|
+
include TypeParseable
|
15
17
|
|
16
|
-
|
17
|
-
'integer' => GraphQL::INT_TYPE,
|
18
|
+
delegate :list?, :required_inner_type?, :required_list?, :required?, to: :type_name_info
|
18
19
|
|
19
|
-
|
20
|
-
'str' => GraphQL::STRING_TYPE,
|
21
|
-
'text' => GraphQL::STRING_TYPE,
|
22
|
-
'time' => GraphQL::STRING_TYPE,
|
23
|
-
'date' => GraphQL::STRING_TYPE,
|
24
|
-
|
25
|
-
'bool' => GraphQL::BOOLEAN_TYPE,
|
26
|
-
'boolean' => GraphQL::BOOLEAN_TYPE,
|
27
|
-
|
28
|
-
'float' => GraphQL::FLOAT_TYPE,
|
29
|
-
'double' => GraphQL::FLOAT_TYPE,
|
30
|
-
'decimal' => GraphQL::FLOAT_TYPE
|
31
|
-
}.freeze
|
32
|
-
|
33
|
-
RAW_GRAPHQL_TYPES = [
|
34
|
-
GraphQL::Schema::List,
|
35
|
-
GraphQL::BaseType,
|
36
|
-
GraphQL::ObjectType,
|
37
|
-
GraphQL::InputObjectType
|
38
|
-
].freeze
|
39
|
-
|
40
|
-
def initialize(unparsed_type)
|
20
|
+
def initialize(unparsed_type, paginated: false)
|
41
21
|
@unparsed_type = unparsed_type
|
22
|
+
@paginated = paginated
|
23
|
+
end
|
24
|
+
|
25
|
+
def paginated?
|
26
|
+
@paginated
|
42
27
|
end
|
43
28
|
|
44
29
|
def graphql_type
|
@@ -51,25 +36,43 @@ module GraphqlRails
|
|
51
36
|
end
|
52
37
|
end
|
53
38
|
|
54
|
-
def
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
39
|
+
def type_arg
|
40
|
+
if paginated?
|
41
|
+
paginated_type_arg
|
42
|
+
elsif list?
|
43
|
+
list_type_arg
|
44
|
+
else
|
45
|
+
raw_unwrapped_type
|
46
|
+
end
|
59
47
|
end
|
60
48
|
|
61
49
|
protected
|
62
50
|
|
63
|
-
def
|
64
|
-
|
65
|
-
return unless type_class
|
51
|
+
def paginated_type_arg
|
52
|
+
return graphql_model.graphql.connection_type if graphql_model
|
66
53
|
|
67
|
-
|
54
|
+
raise NotSupportedFeature, 'pagination is only supported for models which include GraphqlRails::Model'
|
68
55
|
end
|
69
56
|
|
70
|
-
|
57
|
+
def list_type_arg
|
58
|
+
if required_inner_type?
|
59
|
+
[raw_unwrapped_type]
|
60
|
+
else
|
61
|
+
[raw_unwrapped_type, null: true]
|
62
|
+
end
|
63
|
+
end
|
71
64
|
|
72
|
-
|
65
|
+
def parsed_type
|
66
|
+
return unparsed_type if raw_graphql_type?
|
67
|
+
|
68
|
+
type_by_name
|
69
|
+
end
|
70
|
+
|
71
|
+
def raw_unwrapped_type
|
72
|
+
@raw_unwrapped_type ||= unwrap_type(parsed_type)
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
73
76
|
|
74
77
|
attr_reader :unparsed_type
|
75
78
|
|
@@ -91,26 +94,27 @@ module GraphqlRails
|
|
91
94
|
end
|
92
95
|
end
|
93
96
|
|
94
|
-
def raw_graphql_type?
|
95
|
-
return true if RAW_GRAPHQL_TYPES.detect { |raw_type| unparsed_type.is_a?(raw_type) }
|
96
|
-
|
97
|
-
defined?(GraphQL::Schema::Member) &&
|
98
|
-
unparsed_type.is_a?(Class) &&
|
99
|
-
unparsed_type < GraphQL::Schema::Member
|
100
|
-
end
|
101
|
-
|
102
97
|
def type_name_info
|
103
|
-
@type_name_info ||=
|
98
|
+
@type_name_info ||= begin
|
99
|
+
type_name = \
|
100
|
+
if unparsed_type.respond_to?(:to_type_signature)
|
101
|
+
unparsed_type.to_type_signature
|
102
|
+
else
|
103
|
+
unparsed_type.to_s
|
104
|
+
end
|
105
|
+
TypeNameInfo.new(type_name)
|
106
|
+
end
|
104
107
|
end
|
105
108
|
|
106
109
|
def type_by_name
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
110
|
+
unwrapped_scalar_type || unwrapped_model_type || raise_not_supported_type_error
|
111
|
+
end
|
112
|
+
|
113
|
+
def unwrapped_model_type
|
114
|
+
type_class = graphql_model
|
115
|
+
return unless type_class
|
116
|
+
|
117
|
+
type_class.graphql.graphql_type
|
114
118
|
end
|
115
119
|
end
|
116
120
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphqlRails
|
4
|
+
# includes all service object related logic
|
5
|
+
module Service
|
6
|
+
require 'active_support/concern'
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
class_methods do
|
10
|
+
def call(*args, &block)
|
11
|
+
new(*args).call(&block)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -4,17 +4,18 @@ require 'active_support/hash_with_indifferent_access'
|
|
4
4
|
require 'active_support/core_ext/hash'
|
5
5
|
require 'graphql_rails/controller/configuration'
|
6
6
|
require 'graphql_rails/controller/request'
|
7
|
-
require 'graphql_rails/controller/format_results'
|
8
7
|
require 'graphql_rails/controller/action_hooks_runner'
|
9
8
|
require 'graphql_rails/controller/log_controller_action'
|
9
|
+
require 'graphql_rails/errors/system_error'
|
10
10
|
|
11
11
|
module GraphqlRails
|
12
12
|
# base class for all graphql_rails controllers
|
13
13
|
class Controller
|
14
14
|
class << self
|
15
|
-
def inherited(
|
15
|
+
def inherited(subclass)
|
16
16
|
super
|
17
|
-
|
17
|
+
new_config = controller_configuration.dup_with(controller: subclass)
|
18
|
+
subclass.instance_variable_set(:@controller_configuration, new_config)
|
18
19
|
end
|
19
20
|
|
20
21
|
def before_action(*args, &block)
|
@@ -33,8 +34,16 @@ module GraphqlRails
|
|
33
34
|
controller_configuration.action(action_name)
|
34
35
|
end
|
35
36
|
|
37
|
+
def action_default
|
38
|
+
controller_configuration.action_default
|
39
|
+
end
|
40
|
+
|
41
|
+
def model(*args)
|
42
|
+
controller_configuration.model(*args)
|
43
|
+
end
|
44
|
+
|
36
45
|
def controller_configuration
|
37
|
-
@controller_configuration ||= Controller::Configuration.new
|
46
|
+
@controller_configuration ||= Controller::Configuration.new(self)
|
38
47
|
end
|
39
48
|
end
|
40
49
|
|
@@ -48,7 +57,7 @@ module GraphqlRails
|
|
48
57
|
@action_name = method_name
|
49
58
|
with_controller_action_logging do
|
50
59
|
call_with_rendering
|
51
|
-
|
60
|
+
graphql_request.object_to_return
|
52
61
|
end
|
53
62
|
ensure
|
54
63
|
@action_name = nil
|
@@ -78,7 +87,11 @@ module GraphqlRails
|
|
78
87
|
|
79
88
|
render response if graphql_request.no_object_to_return?
|
80
89
|
rescue StandardError => error
|
81
|
-
|
90
|
+
if error.is_a?(GraphQL::ExecutionError)
|
91
|
+
render error: error
|
92
|
+
else
|
93
|
+
render error: SystemError.new(error.message)
|
94
|
+
end
|
82
95
|
end
|
83
96
|
|
84
97
|
def graphql_errors_from_render_params(rendering_params)
|
@@ -86,7 +99,7 @@ module GraphqlRails
|
|
86
99
|
return [] if rendering_params.keys.count != 1
|
87
100
|
|
88
101
|
errors = rendering_params[:error] || rendering_params[:errors]
|
89
|
-
Array(errors)
|
102
|
+
errors.is_a?(Enumerable) ? errors : Array(errors)
|
90
103
|
end
|
91
104
|
|
92
105
|
def with_controller_action_logging(&block)
|
@@ -98,14 +111,5 @@ module GraphqlRails
|
|
98
111
|
&block
|
99
112
|
)
|
100
113
|
end
|
101
|
-
|
102
|
-
def format_controller_results
|
103
|
-
FormatResults.new(
|
104
|
-
graphql_request.object_to_return,
|
105
|
-
action_config: self.class.action(action_name),
|
106
|
-
params: params,
|
107
|
-
graphql_context: graphql_request.context
|
108
|
-
).call
|
109
|
-
end
|
110
114
|
end
|
111
115
|
end
|