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.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/.hound.yml +1 -0
  3. data/.rubocop.yml +3 -3
  4. data/.ruby-version +1 -1
  5. data/.travis.yml +2 -2
  6. data/CHANGELOG.md +31 -0
  7. data/Gemfile +3 -2
  8. data/Gemfile.lock +147 -124
  9. data/docs/README.md +24 -8
  10. data/docs/_sidebar.md +3 -0
  11. data/docs/components/controller.md +194 -21
  12. data/docs/components/model.md +193 -5
  13. data/docs/components/routes.md +28 -0
  14. data/docs/getting_started/quick_start.md +10 -3
  15. data/docs/index.html +1 -1
  16. data/docs/other_tools/query_runner.md +49 -0
  17. data/docs/other_tools/schema_dump.md +29 -0
  18. data/docs/testing/testing.md +3 -1
  19. data/graphql_rails.gemspec +5 -5
  20. data/lib/generators/graphql_rails/install_generator.rb +50 -0
  21. data/lib/generators/graphql_rails/templates/example_users_controller.erb +19 -0
  22. data/lib/generators/graphql_rails/templates/graphql_application_controller.erb +8 -0
  23. data/lib/generators/graphql_rails/templates/graphql_controller.erb +20 -0
  24. data/lib/generators/graphql_rails/templates/graphql_router.erb +19 -0
  25. data/lib/generators/graphql_rails/templates/graphql_router_spec.erb +21 -0
  26. data/lib/graphql_rails.rb +2 -0
  27. data/lib/graphql_rails/attributes/attributable.rb +20 -21
  28. data/lib/graphql_rails/attributes/attribute.rb +41 -4
  29. data/lib/graphql_rails/attributes/attribute_name_parser.rb +4 -4
  30. data/lib/graphql_rails/attributes/input_attribute.rb +24 -10
  31. data/lib/graphql_rails/attributes/input_type_parser.rb +24 -46
  32. data/lib/graphql_rails/attributes/type_parseable.rb +132 -0
  33. data/lib/graphql_rails/attributes/type_parser.rb +58 -54
  34. data/lib/graphql_rails/concerns/service.rb +19 -0
  35. data/lib/graphql_rails/controller.rb +26 -22
  36. data/lib/graphql_rails/controller/action.rb +12 -67
  37. data/lib/graphql_rails/controller/action_configuration.rb +70 -34
  38. data/lib/graphql_rails/controller/build_controller_action_resolver.rb +52 -0
  39. data/lib/graphql_rails/controller/build_controller_action_resolver/controller_action_resolver.rb +28 -0
  40. data/lib/graphql_rails/controller/configuration.rb +56 -5
  41. data/lib/graphql_rails/controller/log_controller_action.rb +11 -6
  42. data/lib/graphql_rails/controller/request.rb +29 -8
  43. data/lib/graphql_rails/controller/request/format_errors.rb +58 -0
  44. data/lib/graphql_rails/decorator/relation_decorator.rb +1 -5
  45. data/lib/graphql_rails/errors/custom_execution_error.rb +22 -0
  46. data/lib/graphql_rails/errors/execution_error.rb +6 -7
  47. data/lib/graphql_rails/errors/system_error.rb +14 -0
  48. data/lib/graphql_rails/errors/validation_error.rb +1 -5
  49. data/lib/graphql_rails/input_configurable.rb +47 -0
  50. data/lib/graphql_rails/model.rb +19 -4
  51. data/lib/graphql_rails/model/add_fields_to_graphql_type.rb +45 -0
  52. data/lib/graphql_rails/model/build_connection_type.rb +52 -0
  53. data/lib/graphql_rails/model/{configuration → build_connection_type}/count_items.rb +5 -5
  54. data/lib/graphql_rails/model/build_enum_type.rb +39 -10
  55. data/lib/graphql_rails/model/build_graphql_input_type.rb +8 -4
  56. data/lib/graphql_rails/model/call_graphql_model_method.rb +72 -0
  57. data/lib/graphql_rails/model/configurable.rb +6 -2
  58. data/lib/graphql_rails/model/configuration.rb +11 -10
  59. data/lib/graphql_rails/model/find_or_build_graphql_type.rb +64 -0
  60. data/lib/graphql_rails/model/find_or_build_graphql_type_class.rb +46 -0
  61. data/lib/graphql_rails/model/input.rb +10 -6
  62. data/lib/graphql_rails/query_runner.rb +68 -0
  63. data/lib/graphql_rails/railtie.rb +10 -0
  64. data/lib/graphql_rails/router.rb +40 -13
  65. data/lib/graphql_rails/router/resource_routes_builder.rb +10 -9
  66. data/lib/graphql_rails/router/route.rb +21 -6
  67. data/lib/graphql_rails/router/schema_builder.rb +30 -11
  68. data/lib/graphql_rails/rspec_controller_helpers.rb +6 -4
  69. data/lib/graphql_rails/tasks/dump_graphql_schema.rb +57 -0
  70. data/lib/graphql_rails/tasks/schema.rake +14 -0
  71. data/lib/graphql_rails/version.rb +1 -1
  72. metadata +48 -21
  73. data/lib/graphql_rails/controller/controller_function.rb +0 -50
  74. data/lib/graphql_rails/controller/format_results.rb +0 -36
  75. 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::ID_TYPE
30
+ GraphQL::Types::ID
31
31
  when /\?\Z/
32
- GraphQL::BOOLEAN_TYPE
32
+ GraphQL::Types::Boolean
33
33
  else
34
- GraphQL::STRING_TYPE
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 = nil, description: nil, subtype: nil, required: nil, options: {})
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.nullable_type || nullable_type
25
+ type = raw_input_type || input_type_parser.input_type_arg
26
+
27
+ [field_name, type]
28
+ end
29
29
 
30
- [field_name, type, { required: required?, description: description }]
30
+ def input_argument_options
31
+ { required: required?, description: description, camelize: false }
31
32
  end
32
33
 
33
- def graphql_input_type
34
- raw_input_type || input_type_parser.graphql_type || graphql_field_type
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 ||= InputTypeParser.new(initial_type, subtype: subtype)
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 < TypeParser
8
+ class InputTypeParser
9
+ require_relative './type_parseable'
10
+
11
+ include TypeParseable
12
+
9
13
  def initialize(unparsed_type, subtype:)
10
- super(unparsed_type)
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 nullable_type
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
- parsed_list_type
26
+ list_type_arg
44
27
  else
45
- parsed_inner_type
28
+ unwrapped_type
46
29
  end
47
30
  end
48
31
 
49
- def raw_graphql_type?
50
- unparsed_type.is_a?(GraphQL::InputObjectType) || super
51
- end
32
+ private
52
33
 
53
- def dynamicly_defined_type
54
- type_class = graphql_model
55
- return unless type_class
34
+ attr_reader :unparsed_type, :subtype
56
35
 
57
- type_class.graphql.input(*subtype).graphql_input_type
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 parsed_list_type
61
- list_type = parsed_inner_type.to_list_type
40
+ def raw_unwrapped_type
41
+ return nil unless raw_graphql_type?
62
42
 
63
- if required_list?
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 parsed_inner_type
46
+ def list_type_arg
72
47
  if required_inner_type?
73
- type_by_name.to_non_null_type
48
+ [unwrapped_type]
74
49
  else
75
- type_by_name
50
+ [unwrapped_type, null: true]
76
51
  end
77
52
  end
78
53
 
79
- private
54
+ def unwrapped_model_input_type
55
+ type_class = graphql_model
56
+ return unless type_class
80
57
 
81
- attr_reader :subtype
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 UnknownTypeError < ArgumentError; end
14
+ class NotSupportedFeature < GraphqlRails::Error; end
12
15
 
13
- TYPE_MAPPING = {
14
- 'id' => GraphQL::ID_TYPE,
16
+ include TypeParseable
15
17
 
16
- 'int' => GraphQL::INT_TYPE,
17
- 'integer' => GraphQL::INT_TYPE,
18
+ delegate :list?, :required_inner_type?, :required_list?, :required?, to: :type_name_info
18
19
 
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
- 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 graphql_model
55
- type_class = nullable_inner_name.safe_constantize
56
- return unless type_class.respond_to?(:graphql)
57
-
58
- type_class
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 dynamicly_defined_type
64
- type_class = graphql_model
65
- return unless type_class
51
+ def paginated_type_arg
52
+ return graphql_model.graphql.connection_type if graphql_model
66
53
 
67
- type_class.graphql.graphql_type
54
+ raise NotSupportedFeature, 'pagination is only supported for models which include GraphqlRails::Model'
68
55
  end
69
56
 
70
- private
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
- delegate :list?, :required_inner_type?, :required_list?, :nullable_inner_name, to: :type_name_info
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 ||= TypeNameInfo.new(unparsed_type.to_s)
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
- TYPE_MAPPING.fetch(nullable_inner_name.downcase) do
108
- dynamicly_defined_type || raise(
109
- UnknownTypeError,
110
- "Type #{unparsed_type.inspect} is not supported. Supported scalar types are: #{TYPE_MAPPING.keys}." \
111
- ' All the classes that includes `GraphqlRails::Model` are also supported as types.'
112
- )
113
- end
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