graphql_rails 0.8.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|