graphql_rails 0.6.0 → 1.2.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.
Files changed (87) 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 +38 -0
  7. data/Gemfile +3 -2
  8. data/Gemfile.lock +181 -71
  9. data/docs/README.md +48 -9
  10. data/docs/_sidebar.md +5 -0
  11. data/docs/components/controller.md +294 -8
  12. data/docs/components/decorator.md +69 -0
  13. data/docs/components/model.md +349 -11
  14. data/docs/components/routes.md +43 -1
  15. data/docs/getting_started/quick_start.md +9 -2
  16. data/docs/index.html +1 -1
  17. data/docs/logging_and_monitoring/logging_and_monitoring.md +35 -0
  18. data/docs/other_tools/query_runner.md +49 -0
  19. data/docs/other_tools/schema_dump.md +29 -0
  20. data/docs/testing/testing.md +3 -1
  21. data/graphql_rails.gemspec +5 -4
  22. data/lib/generators/graphql_rails/install_generator.rb +50 -0
  23. data/lib/generators/graphql_rails/templates/example_users_controller.erb +19 -0
  24. data/lib/generators/graphql_rails/templates/graphql_application_controller.erb +8 -0
  25. data/lib/generators/graphql_rails/templates/graphql_controller.erb +20 -0
  26. data/lib/generators/graphql_rails/templates/graphql_router.erb +19 -0
  27. data/lib/generators/graphql_rails/templates/graphql_router_spec.erb +21 -0
  28. data/lib/graphql_rails.rb +7 -1
  29. data/lib/graphql_rails/attributes.rb +13 -0
  30. data/lib/graphql_rails/{attribute → attributes}/attributable.rb +25 -20
  31. data/lib/graphql_rails/attributes/attribute.rb +94 -0
  32. data/lib/graphql_rails/{attribute → attributes}/attribute_name_parser.rb +2 -2
  33. data/lib/graphql_rails/attributes/input_attribute.rb +65 -0
  34. data/lib/graphql_rails/attributes/input_type_parser.rb +62 -0
  35. data/lib/graphql_rails/attributes/type_name_info.rb +38 -0
  36. data/lib/graphql_rails/attributes/type_parseable.rb +128 -0
  37. data/lib/graphql_rails/attributes/type_parser.rb +121 -0
  38. data/lib/graphql_rails/concerns/service.rb +19 -0
  39. data/lib/graphql_rails/controller.rb +42 -21
  40. data/lib/graphql_rails/controller/action.rb +12 -67
  41. data/lib/graphql_rails/controller/action_configuration.rb +71 -31
  42. data/lib/graphql_rails/controller/build_controller_action_resolver.rb +52 -0
  43. data/lib/graphql_rails/controller/build_controller_action_resolver/controller_action_resolver.rb +28 -0
  44. data/lib/graphql_rails/controller/configuration.rb +56 -4
  45. data/lib/graphql_rails/controller/log_controller_action.rb +71 -0
  46. data/lib/graphql_rails/controller/request.rb +29 -8
  47. data/lib/graphql_rails/controller/request/format_errors.rb +58 -0
  48. data/lib/graphql_rails/decorator.rb +41 -0
  49. data/lib/graphql_rails/decorator/relation_decorator.rb +75 -0
  50. data/lib/graphql_rails/errors/custom_execution_error.rb +22 -0
  51. data/lib/graphql_rails/errors/execution_error.rb +8 -7
  52. data/lib/graphql_rails/errors/system_error.rb +14 -0
  53. data/lib/graphql_rails/errors/validation_error.rb +1 -5
  54. data/lib/graphql_rails/input_configurable.rb +47 -0
  55. data/lib/graphql_rails/integrations.rb +19 -0
  56. data/lib/graphql_rails/integrations/lograge.rb +39 -0
  57. data/lib/graphql_rails/integrations/sentry.rb +34 -0
  58. data/lib/graphql_rails/model.rb +26 -4
  59. data/lib/graphql_rails/model/add_fields_to_graphql_type.rb +45 -0
  60. data/lib/graphql_rails/model/build_connection_type.rb +52 -0
  61. data/lib/graphql_rails/model/{configuration → build_connection_type}/count_items.rb +5 -5
  62. data/lib/graphql_rails/model/build_enum_type.rb +68 -0
  63. data/lib/graphql_rails/model/{graphql_input_type_builder.rb → build_graphql_input_type.rb} +10 -2
  64. data/lib/graphql_rails/model/call_graphql_model_method.rb +72 -0
  65. data/lib/graphql_rails/model/configurable.rb +6 -2
  66. data/lib/graphql_rails/model/configuration.rb +34 -19
  67. data/lib/graphql_rails/model/find_or_build_graphql_type.rb +64 -0
  68. data/lib/graphql_rails/model/find_or_build_graphql_type_class.rb +46 -0
  69. data/lib/graphql_rails/model/input.rb +25 -11
  70. data/lib/graphql_rails/query_runner.rb +68 -0
  71. data/lib/graphql_rails/railtie.rb +10 -0
  72. data/lib/graphql_rails/router.rb +40 -13
  73. data/lib/graphql_rails/router/resource_routes_builder.rb +19 -11
  74. data/lib/graphql_rails/router/route.rb +21 -6
  75. data/lib/graphql_rails/router/schema_builder.rb +36 -11
  76. data/lib/graphql_rails/rspec_controller_helpers.rb +6 -4
  77. data/lib/graphql_rails/tasks/dump_graphql_schema.rb +57 -0
  78. data/lib/graphql_rails/tasks/schema.rake +14 -0
  79. data/lib/graphql_rails/version.rb +1 -1
  80. metadata +78 -26
  81. data/README.md +0 -194
  82. data/lib/graphql_rails/attribute.rb +0 -28
  83. data/lib/graphql_rails/attribute/type_parser.rb +0 -115
  84. data/lib/graphql_rails/controller/controller_function.rb +0 -50
  85. data/lib/graphql_rails/controller/format_results.rb +0 -36
  86. data/lib/graphql_rails/model/graphql_type_builder.rb +0 -33
  87. data/lib/graphql_rails/model/input_attribute.rb +0 -47
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'graphql'
4
+ require 'graphql_rails/attributes/attributable'
5
+ require 'graphql_rails/input_configurable'
6
+
7
+ module GraphqlRails
8
+ module Attributes
9
+ # contains info about single graphql attribute
10
+ class Attribute
11
+ include Attributable
12
+ include InputConfigurable
13
+
14
+ attr_reader :attributes
15
+
16
+ # rubocop:disable Metrics/ParameterLists
17
+ def initialize(name, type = nil, description: nil, property: name, required: nil, options: {})
18
+ @initial_type = type
19
+ @initial_name = name
20
+ @options = options
21
+ @description = description
22
+ @property = property.to_s
23
+ @required = required
24
+ @attributes ||= {}
25
+ end
26
+ # rubocop:enable Metrics/ParameterLists
27
+
28
+ def type(new_type = nil)
29
+ return @initial_type if new_type.nil?
30
+
31
+ @initial_type = new_type
32
+ self
33
+ end
34
+
35
+ def description(new_description = nil)
36
+ return @description if new_description.nil?
37
+
38
+ @description = new_description
39
+ self
40
+ end
41
+
42
+ def property(new_property = nil)
43
+ return @property if new_property.nil?
44
+
45
+ @property = new_property.to_s
46
+ self
47
+ end
48
+
49
+ def options(new_options = {})
50
+ return @options if new_options.blank?
51
+
52
+ @options = new_options
53
+ self
54
+ end
55
+
56
+ def field_args
57
+ [
58
+ field_name,
59
+ type_parser.type_arg,
60
+ *description
61
+ ]
62
+ end
63
+
64
+ def field_options
65
+ {
66
+ method: property.to_sym,
67
+ null: optional?,
68
+ camelize: camelize?
69
+ }
70
+ end
71
+
72
+ def argument_args
73
+ [
74
+ field_name,
75
+ type_parser.type_arg,
76
+ {
77
+ description: description,
78
+ required: required?
79
+ }
80
+ ]
81
+ end
82
+
83
+ protected
84
+
85
+ attr_reader :initial_type, :initial_name
86
+
87
+ private
88
+
89
+ def camelize?
90
+ options[:input_format] != :original && options[:attribute_name_format] != :original
91
+ end
92
+ end
93
+ end
94
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GraphqlRails
4
- class Attribute
4
+ module Attributes
5
5
  # Parses attribute name and can generates graphql scalar type,
6
6
  # grapqhl name and etc. based on that
7
7
  class AttributeNameParser
@@ -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
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphqlRails
4
+ module Attributes
5
+ # contains info about single graphql input attribute
6
+ class InputAttribute
7
+ require_relative './input_type_parser'
8
+ require_relative './attribute_name_parser'
9
+ include Attributable
10
+
11
+ attr_reader :description
12
+
13
+ # rubocop:disable Metrics/ParameterLists
14
+ def initialize(name, type: nil, description: nil, subtype: nil, required: nil, options: {})
15
+ @initial_name = name
16
+ @initial_type = type
17
+ @description = description
18
+ @options = options
19
+ @subtype = subtype
20
+ @required = required
21
+ end
22
+ # rubocop:enable Metrics/ParameterLists
23
+
24
+ def input_argument_args
25
+ type = raw_input_type || input_type_parser.input_type_arg
26
+
27
+ [field_name, type]
28
+ end
29
+
30
+ def input_argument_options
31
+ { required: required?, description: description, camelize: false }
32
+ end
33
+
34
+ def paginated?
35
+ false
36
+ end
37
+
38
+ private
39
+
40
+ attr_reader :initial_name, :initial_type, :options, :subtype
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
+
52
+ def 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
57
+ end
58
+
59
+ def raw_input_type
60
+ return initial_type if initial_type.is_a?(GraphQL::InputObjectType)
61
+ return initial_type.graphql_input_type if initial_type.is_a?(Model::Input)
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'graphql'
4
+
5
+ module GraphqlRails
6
+ module Attributes
7
+ # converts string value in to GraphQL type
8
+ class InputTypeParser
9
+ require_relative './type_parseable'
10
+
11
+ include TypeParseable
12
+
13
+ def initialize(unparsed_type, subtype:)
14
+ @unparsed_type = unparsed_type
15
+ @subtype = subtype
16
+ end
17
+
18
+ def graphql_type
19
+ return nil if unparsed_type.nil?
20
+
21
+ partly_parsed_type || parsed_type
22
+ end
23
+
24
+ def input_type_arg
25
+ if list?
26
+ list_type_arg
27
+ else
28
+ unwrapped_type
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ attr_reader :unparsed_type, :subtype
35
+
36
+ def unwrapped_type
37
+ raw_unwrapped_type || unwrapped_scalar_type || unwrapped_model_input_type || raise_not_supported_type_error
38
+ end
39
+
40
+ def raw_unwrapped_type
41
+ return nil unless raw_graphql_type?
42
+
43
+ unwrap_type(unparsed_type)
44
+ end
45
+
46
+ def list_type_arg
47
+ if required_inner_type?
48
+ [unwrapped_type]
49
+ else
50
+ [unwrapped_type, null: true]
51
+ end
52
+ end
53
+
54
+ def unwrapped_model_input_type
55
+ type_class = graphql_model
56
+ return unless type_class
57
+
58
+ type_class.graphql.input(subtype).graphql_input_type
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphqlRails
4
+ module Attributes
5
+ # checks various attributes based on graphql type name
6
+ class TypeNameInfo
7
+ attr_reader :name
8
+
9
+ def initialize(name)
10
+ @name = name
11
+ end
12
+
13
+ def nullable_inner_name
14
+ inner_name[/[^!]+/]
15
+ end
16
+
17
+ def inner_name
18
+ name[/[^!\[\]]+!?/]
19
+ end
20
+
21
+ def required_inner_type?
22
+ inner_name.include?('!')
23
+ end
24
+
25
+ def list?
26
+ name.include?(']')
27
+ end
28
+
29
+ def required?
30
+ name.end_with?('!')
31
+ end
32
+
33
+ def required_list?
34
+ required? && list?
35
+ end
36
+ end
37
+ end
38
+ 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
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'graphql'
4
+ require 'graphql_rails/model/build_connection_type'
5
+ require 'graphql_rails/errors/error'
6
+
7
+ module GraphqlRails
8
+ module Attributes
9
+ # converts string value in to GraphQL type
10
+ class TypeParser
11
+ require_relative './type_name_info'
12
+ require_relative './type_parseable'
13
+
14
+ class NotSupportedFeature < GraphqlRails::Error; end
15
+
16
+ include TypeParseable
17
+
18
+ delegate :list?, :required_inner_type?, :required_list?, :required?, to: :type_name_info
19
+
20
+ def initialize(unparsed_type, paginated: false)
21
+ @unparsed_type = unparsed_type
22
+ @paginated = paginated
23
+ end
24
+
25
+ def paginated?
26
+ @paginated
27
+ end
28
+
29
+ def graphql_type
30
+ return unparsed_type if raw_graphql_type?
31
+
32
+ if list?
33
+ parsed_list_type
34
+ else
35
+ parsed_inner_type
36
+ end
37
+ end
38
+
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
47
+ end
48
+
49
+ protected
50
+
51
+ def paginated_type_arg
52
+ return graphql_model.graphql.connection_type if graphql_model
53
+
54
+ raise NotSupportedFeature, 'pagination is only supported for models which include GraphqlRails::Model'
55
+ end
56
+
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
64
+
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
76
+
77
+ attr_reader :unparsed_type
78
+
79
+ def parsed_list_type
80
+ list_type = parsed_inner_type.to_list_type
81
+
82
+ if required_list?
83
+ list_type.to_non_null_type
84
+ else
85
+ list_type
86
+ end
87
+ end
88
+
89
+ def parsed_inner_type
90
+ if required_inner_type?
91
+ type_by_name.to_non_null_type
92
+ else
93
+ type_by_name
94
+ end
95
+ end
96
+
97
+ def 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
107
+ end
108
+
109
+ def type_by_name
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
118
+ end
119
+ end
120
+ end
121
+ end