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
data/README.md DELETED
@@ -1,194 +0,0 @@
1
- # GraphqlRails
2
-
3
- [![Build Status](https://travis-ci.org/samesystem/graphql_rails.svg?branch=master)](https://travis-ci.org/samesystem/graphql_rails)
4
- [![codecov](https://codecov.io/gh/samesystem/graphql_rails/branch/master/graph/badge.svg)](https://codecov.io/gh/samesystem/graphql_rails)
5
- [![Documentation](https://readthedocs.org/projects/ansicolortags/badge/?version=latest)](https://samesystem.github.io/graphql_rails)
6
-
7
- Rails style structure for GraphQL API.
8
-
9
- ## Installation
10
-
11
- Add this line to your application's Gemfile:
12
-
13
- ```ruby
14
- gem 'graphql_rails'
15
- ```
16
-
17
- And then execute:
18
-
19
- $ bundle
20
-
21
- Or install it yourself as:
22
-
23
- $ gem install graphql_rails
24
-
25
- ## Usage
26
-
27
- ### Define GraphQL schema as RoR routes
28
-
29
- ```ruby
30
- MyGraphqlSchema = GraphqlRails::Router.draw do
31
- # will create createUser, updateUser, deleteUser mutations and user, users queries.
32
- # expects that UsersController class exist
33
- resources :users
34
-
35
- # if you want custom queries or mutation
36
- query 'searchLogs', to: 'logs#search' # redirects request to LogsController
37
- mutation 'changeUserPassword', to: 'users#change_password'
38
- end
39
- ```
40
-
41
- ### Define your Graphql model
42
-
43
- ```ruby
44
- class User # works with any class including ActiveRecord
45
- include GraphqlRails::Model
46
-
47
- graphql do |c|
48
- # most common attributes, like :id, :name, :title has default type, so you don't have to specify it (but you can!)
49
- c.attribute :id
50
-
51
- c.attribute :email, type: :string
52
- c.attribute :surname, type: :string
53
- end
54
- end
55
- ```
56
-
57
- ### Define controller
58
-
59
- ```ruby
60
- class UsersController < GraphqlRails::Controller
61
- # graphql requires to describe which attributes controller action accepts and which returns
62
- action(:change_user_password)
63
- .permit(:password!, :id!) # Bang (!) indicates that attribute is required
64
-
65
- def change_user_password
66
- user = User.find(params[:id])
67
- user.update!(password: params[:password])
68
-
69
- # returned value needs to have all methods defined in model `graphql do` part
70
- user # or SomeDecorator.new(user)
71
- end
72
-
73
- action(:search).permit(search_fields!: SearchFieldsInput) # you can specify your own input fields
74
- def search
75
- end
76
- end
77
- ```
78
-
79
- ## Routes
80
-
81
- ```ruby
82
- MyGraphqlSchema = GraphqlRails::Router.draw do
83
- # generates `friend`, `createFriend`, `updateFriend`, `destroyFriend`, `friends` routes
84
- resources :friends
85
- resources :shops, only: [:show, :index] # generates `shop` and `shops` routes only
86
- resources :orders, except: :update # generates all routes except `updateOrder`
87
-
88
- resources :users do
89
- # generates `findUser` query
90
- query :find, on: :member
91
-
92
- # generates `searchUsers` query
93
- query :search, on: :collection
94
- end
95
-
96
- # you can use namespaced controllers too:
97
- scope module: 'admin' do
98
- # `updateTranslations` route will be handeled by `Admin::TranslationsController`
99
- mutation :updateTranslations, to: 'translations#update'
100
-
101
- # all :groups routes will be handeled by `Admin::GroupsController`
102
- resources :groups
103
- end
104
- end
105
- ```
106
-
107
- ## Testing your GraphqlRails::Controller in RSpec
108
-
109
- ### Setup
110
-
111
- Add those lines in your `spec/spec_helper.rb` file
112
-
113
- ```ruby
114
- # spec/spec_helper.rb
115
- require 'graphql_rails/rspec_controller_helpers'
116
-
117
- RSpec.configure do |config|
118
- config.include(GraphqlRails::RSpecControllerHelpers, type: :graphql_controller)
119
- # ... your other configuration ...
120
- end
121
- ```
122
-
123
- ### Helper methods
124
-
125
- There are 3 helper methods:
126
-
127
- * `mutation(:your_controller_action_name, params: {}, context: {})`. `params` and `context` are optional
128
- * `query(:your_controller_action_name, params: {}, context: {})`. `params` and `context` are optional
129
- * `response`. Response is set only after you call `mutation` or `query`
130
-
131
- ### Test examples
132
-
133
- ```ruby
134
- class MyGraphqlController
135
- def index
136
- "Called from index: #{params[:message]}"
137
- end
138
-
139
- action(:create_user).permit(:full_name, :email)
140
- def create_user
141
- User.create!(params)
142
- end
143
- end
144
-
145
- RSpec.describe MyGraphqlController, type: :graphql_controller do
146
- describe '#index' do
147
- it 'is successful' do
148
- query(:index)
149
- expect(response).to be_successful
150
- end
151
-
152
- it 'returns correct message' do
153
- query(:index, params: { message: 'Hello world!' })
154
- expect(response.result).to eq "Called from index: Hello world!"
155
- end
156
- end
157
-
158
- describe '#create_user' do
159
- context 'when bad email is given' do
160
- it 'fails' do
161
- mutation(:create_user, params { email: 'bad' })
162
- expect(response).to be_failure
163
- end
164
-
165
- it 'contains errors' do
166
- mutation(:create_user, params { email: 'bad' })
167
- expect(response.errors).not_to be_empty
168
- end
169
- end
170
- end
171
- end
172
- ```
173
-
174
- ## Detailed documentation
175
-
176
- Check https://samesystem.github.io/graphql_rails for more details
177
-
178
- ## Development
179
-
180
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
181
-
182
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
183
-
184
- ## Contributing
185
-
186
- Bug reports and pull requests are welcome on GitHub at https://github.com/samesystem/graphql_rails. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
187
-
188
- ## License
189
-
190
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
191
-
192
- ## Code of Conduct
193
-
194
- Everyone interacting in the GraphqlRails project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/samesystem/graphql_rails/blob/master/CODE_OF_CONDUCT.md).
@@ -1,28 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'graphql'
4
- require 'graphql_rails/attribute/attributable'
5
-
6
- module GraphqlRails
7
- # contains info about single graphql attribute
8
- class Attribute
9
- include Attributable
10
-
11
- attr_reader :property, :description
12
-
13
- def initialize(name, type = nil, description: nil, property: name)
14
- @initial_type = type
15
- @initial_name = name
16
- @description = description
17
- @property = property.to_s
18
- end
19
-
20
- def field_args
21
- [field_name, graphql_field_type, { property: property.to_sym, description: description }]
22
- end
23
-
24
- protected
25
-
26
- attr_reader :initial_type, :initial_name
27
- end
28
- end
@@ -1,115 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'graphql'
4
-
5
- module GraphqlRails
6
- class Attribute
7
- # converts string value in to GraphQL type
8
- class TypeParser
9
- class UnknownTypeError < ArgumentError; end
10
-
11
- TYPE_MAPPING = {
12
- 'id' => GraphQL::ID_TYPE,
13
-
14
- 'int' => GraphQL::INT_TYPE,
15
- 'integer' => GraphQL::INT_TYPE,
16
-
17
- 'string' => GraphQL::STRING_TYPE,
18
- 'str' => GraphQL::STRING_TYPE,
19
- 'text' => GraphQL::STRING_TYPE,
20
- 'time' => GraphQL::STRING_TYPE,
21
- 'date' => GraphQL::STRING_TYPE,
22
-
23
- 'bool' => GraphQL::BOOLEAN_TYPE,
24
- 'boolean' => GraphQL::BOOLEAN_TYPE,
25
-
26
- 'float' => GraphQL::FLOAT_TYPE,
27
- 'double' => GraphQL::FLOAT_TYPE,
28
- 'decimal' => GraphQL::FLOAT_TYPE
29
- }.freeze
30
-
31
- def initialize(unparsed_type)
32
- @unparsed_type = unparsed_type
33
- end
34
-
35
- def graphql_type
36
- return unparsed_type if raw_graphql_type?
37
-
38
- if list?
39
- parsed_list_type
40
- else
41
- parsed_inner_type
42
- end
43
- end
44
-
45
- def graphql_model
46
- type_class = inner_type_name.safe_constantize
47
- return unless type_class.respond_to?(:graphql)
48
-
49
- type_class
50
- end
51
-
52
- private
53
-
54
- attr_reader :unparsed_type
55
-
56
- def parsed_list_type
57
- list_type = parsed_inner_type.to_list_type
58
-
59
- if required_list_type?
60
- list_type.to_non_null_type
61
- else
62
- list_type
63
- end
64
- end
65
-
66
- def parsed_inner_type
67
- if required_inner_type?
68
- type_by_name.to_non_null_type
69
- else
70
- type_by_name
71
- end
72
- end
73
-
74
- def required_inner_type?
75
- !!unparsed_type[/\w!/] # rubocop:disable Style/DoubleNegation
76
- end
77
-
78
- def list?
79
- unparsed_type.to_s.include?(']')
80
- end
81
-
82
- def required_list_type?
83
- unparsed_type.to_s.include?(']!')
84
- end
85
-
86
- def raw_graphql_type?
87
- unparsed_type.is_a?(GraphQL::BaseType) ||
88
- unparsed_type.is_a?(GraphQL::ObjectType) ||
89
- unparsed_type.is_a?(GraphQL::InputObjectType) ||
90
- (defined?(GraphQL::Schema::Member) && unparsed_type.is_a?(Class) && unparsed_type < GraphQL::Schema::Member)
91
- end
92
-
93
- def inner_type_name
94
- unparsed_type.to_s.tr('[]!', '')
95
- end
96
-
97
- def type_by_name
98
- TYPE_MAPPING.fetch(inner_type_name.downcase) do
99
- dynamicly_defined_type || raise(
100
- UnknownTypeError,
101
- "Type #{unparsed_type.inspect} is not supported. Supported scalar types are: #{TYPE_MAPPING.keys}." \
102
- ' All the classes that includes `GraphqlRails::Model` are also supported as types.'
103
- )
104
- end
105
- end
106
-
107
- def dynamicly_defined_type
108
- type_class = graphql_model
109
- return unless type_class
110
-
111
- type_class.graphql.graphql_type
112
- end
113
- end
114
- end
115
- end
@@ -1,50 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'graphql_rails/controller/action'
4
- require_relative 'request'
5
- require_relative 'action'
6
-
7
- module GraphqlRails
8
- class Controller
9
- # graphql resolver which redirects actions to appropriate controller and controller action
10
- class ControllerFunction < GraphQL::Function
11
- # accepts path of given format "controller_name#action"
12
- attr_reader :type
13
-
14
- def initialize(controller:, action_name:, type:)
15
- @controller = controller
16
- @action_name = action_name
17
- @type = type
18
- end
19
-
20
- def self.from_route(route)
21
- action = Action.new(route)
22
-
23
- action_function_class(action).new(
24
- controller: action.controller,
25
- action_name: action.name,
26
- type: action.return_type
27
- )
28
- end
29
-
30
- def self.action_function_class(action)
31
- Class.new(self) do
32
- description(action.description)
33
-
34
- action.arguments.each do |attribute|
35
- argument(*attribute.function_argument_args)
36
- end
37
- end
38
- end
39
-
40
- def call(object, inputs, ctx)
41
- request = Request.new(object, inputs, ctx)
42
- controller.new(request).call(action_name)
43
- end
44
-
45
- private
46
-
47
- attr_reader :controller, :action_name
48
- end
49
- end
50
- end
@@ -1,36 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module GraphqlRails
4
- class Controller
5
- # Convers raw controller results in to graphql-friendly format
6
- class FormatResults
7
- def initialize(original_result, action_config:, params:, graphql_context:)
8
- @original_result = original_result
9
- @action_config = action_config
10
- @controller_params = params
11
- @graphql_context = graphql_context
12
- end
13
-
14
- def call
15
- if action_config.paginated? && original_result
16
- paginated_result
17
- else
18
- original_result
19
- end
20
- end
21
-
22
- private
23
-
24
- attr_reader :original_result, :action_config, :controller_params, :graphql_context
25
-
26
- def paginated_result
27
- pagination_params = controller_params.slice(:first, :last, :before, :after)
28
- pagination_options = action_config.pagination_options.merge(context: graphql_context)
29
-
30
- GraphQL::Relay::BaseConnection
31
- .connection_for_nodes(original_result)
32
- .new(original_result, pagination_params, pagination_options)
33
- end
34
- end
35
- end
36
- end
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module GraphqlRails
4
- module Model
5
- # stores information about model specific config, like attributes and types
6
- class GraphqlTypeBuilder
7
- def initialize(name:, description: nil, attributes:)
8
- @name = name
9
- @attributes = attributes
10
- @description = description
11
- end
12
-
13
- def call
14
- type_name = name
15
- type_description = description
16
- type_attributes = attributes
17
-
18
- GraphQL::ObjectType.define do
19
- name(type_name)
20
- description(type_description)
21
-
22
- type_attributes.each_value do |attribute|
23
- field(*attribute.field_args)
24
- end
25
- end
26
- end
27
-
28
- private
29
-
30
- attr_reader :model_configuration, :attributes, :name, :description
31
- end
32
- end
33
- end