graphql_rails 0.8.0 → 1.2.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -27,11 +27,11 @@ module GraphqlRails
|
|
27
27
|
@graphql_type ||= \
|
28
28
|
case name
|
29
29
|
when 'id', /_id\Z/
|
30
|
-
GraphQL::
|
30
|
+
GraphQL::Types::ID
|
31
31
|
when /\?\Z/
|
32
|
-
GraphQL::
|
32
|
+
GraphQL::Types::Boolean
|
33
33
|
else
|
34
|
-
GraphQL::
|
34
|
+
GraphQL::Types::String
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
@@ -44,7 +44,7 @@ module GraphqlRails
|
|
44
44
|
attr_reader :options
|
45
45
|
|
46
46
|
def original_format?
|
47
|
-
options[:input_format] == :original
|
47
|
+
options[:input_format] == :original || options[:attribute_name_format] == :original
|
48
48
|
end
|
49
49
|
|
50
50
|
def preprocesed_name
|
@@ -5,12 +5,13 @@ module GraphqlRails
|
|
5
5
|
# contains info about single graphql input attribute
|
6
6
|
class InputAttribute
|
7
7
|
require_relative './input_type_parser'
|
8
|
+
require_relative './attribute_name_parser'
|
8
9
|
include Attributable
|
9
10
|
|
10
11
|
attr_reader :description
|
11
12
|
|
12
13
|
# rubocop:disable Metrics/ParameterLists
|
13
|
-
def initialize(name, type
|
14
|
+
def initialize(name, type: nil, description: nil, subtype: nil, required: nil, options: {})
|
14
15
|
@initial_name = name
|
15
16
|
@initial_type = type
|
16
17
|
@description = description
|
@@ -20,26 +21,39 @@ module GraphqlRails
|
|
20
21
|
end
|
21
22
|
# rubocop:enable Metrics/ParameterLists
|
22
23
|
|
23
|
-
def function_argument_args
|
24
|
-
[field_name, graphql_input_type, { description: description }]
|
25
|
-
end
|
26
|
-
|
27
24
|
def input_argument_args
|
28
|
-
type = raw_input_type || input_type_parser.
|
25
|
+
type = raw_input_type || input_type_parser.input_type_arg
|
26
|
+
|
27
|
+
[field_name, type]
|
28
|
+
end
|
29
29
|
|
30
|
-
|
30
|
+
def input_argument_options
|
31
|
+
{ required: required?, description: description, camelize: false }
|
31
32
|
end
|
32
33
|
|
33
|
-
def
|
34
|
-
|
34
|
+
def paginated?
|
35
|
+
false
|
35
36
|
end
|
36
37
|
|
37
38
|
private
|
38
39
|
|
39
40
|
attr_reader :initial_name, :initial_type, :options, :subtype
|
40
41
|
|
42
|
+
def attribute_name_parser
|
43
|
+
@attribute_name_parser ||= AttributeNameParser.new(
|
44
|
+
initial_name, options: attribute_naming_options
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
def attribute_naming_options
|
49
|
+
options.slice(:input_format)
|
50
|
+
end
|
51
|
+
|
41
52
|
def input_type_parser
|
42
|
-
@input_type_parser ||=
|
53
|
+
@input_type_parser ||= begin
|
54
|
+
initial_parseable_type = initial_type || attribute_name_parser.graphql_type
|
55
|
+
InputTypeParser.new(initial_parseable_type, subtype: subtype)
|
56
|
+
end
|
43
57
|
end
|
44
58
|
|
45
59
|
def raw_input_type
|
@@ -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,132 @@
|
|
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::Types::ID,
|
15
|
+
|
16
|
+
'int' => GraphQL::Types::Int,
|
17
|
+
'integer' => GraphQL::Types::Int,
|
18
|
+
|
19
|
+
'string' => GraphQL::Types::String,
|
20
|
+
'str' => GraphQL::Types::String,
|
21
|
+
'text' => GraphQL::Types::String,
|
22
|
+
'time' => GraphQL::Types::String,
|
23
|
+
'date' => GraphQL::Types::String,
|
24
|
+
|
25
|
+
'bool' => GraphQL::Types::Boolean,
|
26
|
+
'boolean' => GraphQL::Types::Boolean,
|
27
|
+
|
28
|
+
'float' => GraphQL::Types::Float,
|
29
|
+
'double' => GraphQL::Types::Float,
|
30
|
+
'decimal' => GraphQL::Types::Float
|
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 core_scalar_type?
|
61
|
+
unwrapped_scalar_type.in?(TYPE_MAPPING.values)
|
62
|
+
end
|
63
|
+
|
64
|
+
def graphql_model
|
65
|
+
type_class = \
|
66
|
+
if unparsed_type.is_a?(Class) && unparsed_type < GraphqlRails::Model
|
67
|
+
unparsed_type
|
68
|
+
else
|
69
|
+
nullable_inner_name.safe_constantize
|
70
|
+
end
|
71
|
+
|
72
|
+
return if type_class.nil?
|
73
|
+
return unless type_class < GraphqlRails::Model
|
74
|
+
|
75
|
+
type_class
|
76
|
+
end
|
77
|
+
|
78
|
+
protected
|
79
|
+
|
80
|
+
def unwrap_type(type)
|
81
|
+
unwrappable = type
|
82
|
+
unwrappable = unwrappable.of_type while wrapped_type?(unwrappable)
|
83
|
+
unwrappable
|
84
|
+
end
|
85
|
+
|
86
|
+
def wrapped_type?(type)
|
87
|
+
WRAPPER_TYPES.any? { |wrapper| type.is_a?(wrapper) }
|
88
|
+
end
|
89
|
+
|
90
|
+
def nullable_inner_name
|
91
|
+
type_name_info.nullable_inner_name
|
92
|
+
end
|
93
|
+
|
94
|
+
def list?
|
95
|
+
type_name_info.list?
|
96
|
+
end
|
97
|
+
|
98
|
+
def required_inner_type?
|
99
|
+
type_name_info.required_inner_type?
|
100
|
+
end
|
101
|
+
|
102
|
+
def required_list?
|
103
|
+
type_name_info.required_list?
|
104
|
+
end
|
105
|
+
|
106
|
+
def required?
|
107
|
+
type_name_info.required?
|
108
|
+
end
|
109
|
+
|
110
|
+
def type_name_info
|
111
|
+
@type_name_info ||= begin
|
112
|
+
type_name = \
|
113
|
+
if unparsed_type.respond_to?(:to_type_signature)
|
114
|
+
unparsed_type.to_type_signature
|
115
|
+
else
|
116
|
+
unparsed_type.to_s
|
117
|
+
end
|
118
|
+
TypeNameInfo.new(type_name)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def raise_not_supported_type_error
|
123
|
+
error_message = \
|
124
|
+
"Type #{unparsed_type.inspect} is not supported. " \
|
125
|
+
"Supported scalar types are: #{TypeParseable::TYPE_MAPPING.keys}. " \
|
126
|
+
'All the classes that includes `GraphqlRails::Model` are also supported as types.'
|
127
|
+
|
128
|
+
raise UnknownTypeError, error_message
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
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
|