graphql_connector 1.1.1.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8dbc699b8eeae3209dd9c8b4ddd01bcd0e544a56ac099465dc1508b1cb5d89fc
4
- data.tar.gz: b7136a702f6c91413eb232cc3c10516c4404462a5ebe97a74bb573df6ce70f79
3
+ metadata.gz: 76343e24e10c4dd1cf216693639ca283fa96c6562e10432b7815f57effd07171
4
+ data.tar.gz: 83f6e99ac9d87619e2464d7436493ccc0eaa79b2a27cf9d60103e806fe2d09aa
5
5
  SHA512:
6
- metadata.gz: 28c64334cab94490d9f0cb4cd3aa210a5f4c647282b72ebb5bd9d7ed45ab29afd4e1dda57b483ab341835da4b52770a84e493f6a4e0e2cbc9a8d111a08832ccc
7
- data.tar.gz: 9f4d50ce4a1cc525834c51f1f1a454ae00e269c74c6eb19369f398b6acce766c1f5d24c5ecf81698ca3f1d49df09686ca741d3454c467e50285f11cd58cbdbb9
6
+ metadata.gz: 725ae753055da95a57738ce5581e9e877bbd54c0511eab2726e2b415701e9be8ca1dd3e5db201af8cbdadc0e70e855ee90f7c7bc58a9e95591fbe19a6eb4c58f
7
+ data.tar.gz: 3f4b4ecfe3ea1a75010c94e9d8dab85b192ef254a84f913f635584d814689d6fabca96d416c74ed75acfb1acb12a02397d0353b10effe6ad8d8d7b0fa75c6564
@@ -1,3 +1,9 @@
1
+ ## 1.2.0 (2020-12-22)
2
+
3
+ ### Features
4
+ * Add `mutation` under server namespace and service class inclusion
5
+ * See `README` for details
6
+
1
7
  ## 1.1.1 (2020-5-04)
2
8
 
3
9
  ### BugFix
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- graphql_connector (1.1.1)
4
+ graphql_connector (1.2.0)
5
5
  httparty (~> 0.17)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -41,6 +41,7 @@ one or many graphql servers:
41
41
 
42
42
  * `raw_query` --> [Examples](examples/raw_query_examples.rb)
43
43
  * `query` --> [Examples](examples/query_examples.rb)
44
+ * `mutation` --> [Examples](examples/mutation_examples.rb)
44
45
  * `service class inclusion` --> [Examples](examples/departments_service_class_examples.rb)
45
46
 
46
47
  See the following sub sections for details
@@ -91,18 +92,31 @@ Example:
91
92
 
92
93
  ---
93
94
 
95
+ ### mutation
96
+
97
+ Works in the same way as [query](#query)
98
+
99
+ See also [here](examples/mutation_examples.rb) for example usage
100
+
94
101
  ### Service class inclusion
95
102
 
96
103
  This approach can be used to `graphqlize` **any** kind of ruby (service) class
97
- so that it has re-usable graphql query methods.
104
+ so that it has re-usable graphql `query` and `mutation` **class methods**.
98
105
 
99
106
  * First add `extend GraphqlConnector::<server>::Query` in the the class(es) that should be `graphqlized`
100
- * Next for each mapping add a `add_query` or `add_raw_query` aliasing the graphql server type supports as follows:
101
- * `add_query <alias>: <query type in graphql server>, params: [<any kind of query type params>], returns: [<selected_fields>]`
102
- * `add_raw_query <alias>: <query string>, params: [<any kind of query type params>]`
103
- * If <query type>/<query string> does not need them, omit `params`
104
107
 
105
- See also [here](examples/departments_service_class_examples.rb) for example usage as also in the following:
108
+ * Then you can aliases as many graphql server types via `add_query` and/or `add_raw_query` and/or `add_mutation`:
109
+
110
+ ```ruby
111
+ add_query <alias>: :<graphql_server_type>, params: [...], returns: [...]
112
+
113
+ add_raw_query <alias>: 'query { ... }', params: [...]
114
+
115
+ add_mutation <alias>: :<graphql_server_type>, params: [...], returns: [...]
116
+ ```
117
+ * :grey_exclamation: If not needed omit `params`
118
+
119
+ See also [here](examples/departments_service_class_examples.rb) and also here for complete example usage:
106
120
 
107
121
  ```ruby
108
122
  GraphqlConnector.configure do |config|
@@ -131,6 +145,10 @@ class Product
131
145
  add_query by_category_id: :products,
132
146
  params: :product_category,
133
147
  returns: [product_category: [:id, :name]]
148
+
149
+ add_mutation create: :createProduct,
150
+ params: [:name, :catgetoryId],
151
+ returns: [:id, :name]
134
152
  end
135
153
 
136
154
  Product.all
@@ -147,6 +165,9 @@ Product.by(id: 1, name: 'Demo Product')
147
165
 
148
166
  Product.by_category_id(product_category: { id: 10})
149
167
  => OpenStruct<product_category=<ProductCategory<id=10, name='Demo Category'>>
168
+
169
+ Product.create(name: 'Another Product', catgetoryId: 10)
170
+ => OpenStruct<id=10, name='Another Product'>
150
171
  ```
151
172
 
152
173
  Also custom **class methods** can used to call any kind of `query` and do further selection instead:
@@ -166,6 +187,9 @@ Product.by_id(id: 1)
166
187
  => OpenStruct<id=1, name='Demo Product'>>
167
188
  ```
168
189
 
190
+ :warning: Ensure that your custom **class method** never has the **same name** as an `<alias>` of `add_query`, `add_raw_query` or `add_mutation`. Otherwise the associated grapqhl query will not be performed because of [Ruby Open Class principle](https://ruby-lang.co/ruby-open-class/)
191
+
192
+
169
193
  Example for `raw_query`:
170
194
 
171
195
  ```ruby
@@ -187,6 +211,8 @@ Product.by(id: '1', name: 'Demo Product')
187
211
 
188
212
  ```
189
213
 
214
+ :exclamation: There is no `add_raw_mutation` since `add_raw_query` does already cover such a case
215
+
190
216
  ## Development
191
217
 
192
218
  After checking out the repo, run
@@ -1,5 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
4
+ # ! The following examples are used together with
5
+ # ! https://github.com/sushie1984/rails-graphql-server
6
+ # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
7
+
3
8
  uri = 'http://rails-graphql-server.herokuapp.com/api/graphql'
4
9
  GraphqlConnector.configure do |config|
5
10
  config.add_server(name: 'RailsGraphqlServer', uri: uri, headers: {})
@@ -27,6 +32,12 @@ class Department
27
32
  }
28
33
  }',
29
34
  params: [:id]
35
+
36
+ # We use a nested 'input' as of convention for
37
+ # https://github.com/sushie1984/rails-graphql-server/blob/master/app/graphql/input_objects/department_attributes.rb
38
+ add_mutation create: :createDepartment,
39
+ params: :input,
40
+ returns: [department: %i[id name location]]
30
41
  end
31
42
 
32
43
  Department.all
@@ -36,3 +47,5 @@ Department.by_id(id: %w[1 2])
36
47
  Department.all_raw
37
48
 
38
49
  Department.by_id_raw(id: %w[1 2])
50
+
51
+ Department.create(input: { attributes: { name: 'One', location: 'Berlin' } })
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
4
+ # ! The following examples are used together with
5
+ # ! https://github.com/sushie1984/rails-graphql-server
6
+ # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
7
+
8
+ uri = 'http://rails-graphql-server.herokuapp.com/api/graphql'
9
+ GraphqlConnector.configure do |config|
10
+ config.add_server(name: 'RailsGraphqlServer', uri: uri, headers: {})
11
+ end
12
+
13
+ department_input = { attributes: { name: 'One', location: 'Berlin' } }
14
+ return_fields = ['department': ['id', 'name', 'employees' => ['yearlySalary']]]
15
+ GraphqlConnector::RailsGraphqlServer.mutation('createDepartment',
16
+ { input: department_input },
17
+ return_fields)
@@ -1,5 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
4
+ # ! The following examples are used together with
5
+ # ! https://github.com/sushie1984/rails-graphql-server
6
+ # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
7
+
3
8
  uri = 'http://rails-graphql-server.herokuapp.com/api/graphql'
4
9
  GraphqlConnector.configure do |config|
5
10
  config.add_server(name: 'RailsGraphqlServer', uri: uri, headers: {})
@@ -1,5 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
4
+ # ! The following examples are used together with
5
+ # ! https://github.com/sushie1984/rails-graphql-server
6
+ # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
7
+
3
8
  GraphqlConnector.configure do |config|
4
9
  config.add_server(name: 'RailsGraphqlServer',
5
10
  uri: 'http://rails-graphql-server.herokuapp.com/api/graphql',
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'graphql_connector/version'
4
- require 'graphql_connector/query_builder'
4
+ require 'graphql_connector/formatters/base_format'
5
+ require 'graphql_connector/formatters/query_format'
6
+ require 'graphql_connector/formatters/mutation_format'
5
7
  require 'graphql_connector/configuration'
6
8
  require 'graphql_connector/http_client'
7
9
  require 'graphql_connector/base_server_type'
@@ -9,8 +9,7 @@ module GraphqlConnector
9
9
  verify_new_client_type_for!(name)
10
10
  base_class = class_with(uri, headers)
11
11
  base_object = GraphqlConnector.const_set(name, base_class)
12
- inject_http_client(base_object)
13
- inject_query_methods(base_object)
12
+ inject_http_client_delegations(base_object)
14
13
  create_service_class_module(base_object)
15
14
 
16
15
  base_object
@@ -48,25 +47,16 @@ module GraphqlConnector
48
47
  end
49
48
  end
50
49
 
51
- def inject_http_client(base_object)
50
+ def inject_http_client_delegations(base_object)
52
51
  base_object.instance_eval do
52
+ extend SingleForwardable
53
+ def_delegators :http_client, :query, :raw_query, :mutation
54
+
53
55
  def http_client
54
56
  @http_client ||= GraphqlConnector::HttpClient.new(@uri, @headers)
55
57
  end
56
58
  end
57
59
  end
58
-
59
- def inject_query_methods(base_object)
60
- base_object.instance_eval do
61
- def query(model, conditions, selected_fields)
62
- http_client.query(model, conditions, selected_fields)
63
- end
64
-
65
- def raw_query(query_string, variables: {})
66
- http_client.raw_query(query_string, variables: variables)
67
- end
68
- end
69
- end
70
60
  end
71
61
  end
72
62
  end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphqlConnector
4
+ module Formatters
5
+ # Class that returns in query or mutation string format
6
+ class BaseFormat
7
+ def initialize(model, conditions, selected_fields)
8
+ @model = model
9
+ @conditions = conditions
10
+ @selected_fields = selected_fields
11
+ end
12
+
13
+ def create
14
+ <<-STRING
15
+ #{query_type} {
16
+ #{@model}#{arguments} {
17
+ #{parse_fields(@selected_fields)}
18
+ }
19
+ }
20
+ STRING
21
+ end
22
+
23
+ private
24
+
25
+ def arguments
26
+ conditions = @conditions.each_with_object([]) do |(key, value), array|
27
+ array << "#{key}: #{value_as_parameter(value)}"
28
+ end
29
+
30
+ return '' if conditions.empty?
31
+
32
+ "(#{conditions.join(', ')})"
33
+ end
34
+
35
+ def value_as_parameter(value)
36
+ case value
37
+ when Array
38
+ casted_values = value.map { |v| value_as_parameter(v) }
39
+ "[#{casted_values.join(',')}]"
40
+ when Hash
41
+ casted_values = value.map { |k, v| "#{k}: #{value_as_parameter(v)}" }
42
+ "{#{casted_values.join(',')}}"
43
+ else
44
+ scalar_types(value)
45
+ end
46
+ end
47
+
48
+ def scalar_types(value)
49
+ case value
50
+ when TrueClass, FalseClass, Integer, Float
51
+ value
52
+ else # fallback to string
53
+ '"' + value.to_s + '"'
54
+ end
55
+ end
56
+
57
+ def parse_fields(selected_fields)
58
+ results = selected_fields.map do |field|
59
+ case field
60
+ when Hash
61
+ handle_association(field)
62
+ else
63
+ field
64
+ end
65
+ end
66
+
67
+ results.join(' ')
68
+ end
69
+
70
+ def handle_association(hash)
71
+ hash.map do |key, fields|
72
+ "#{key} { #{parse_fields(fields)} }"
73
+ end
74
+ end
75
+
76
+ def query_type
77
+ raise 'query_type undefined'
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphqlConnector
4
+ module Formatters
5
+ # Class that returns in mutation string format
6
+ class MutationFormat < BaseFormat
7
+ private
8
+
9
+ def query_type
10
+ 'mutation'
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphqlConnector
4
+ module Formatters
5
+ # Class that returns in query string format
6
+ class QueryFormat < BaseFormat
7
+ private
8
+
9
+ def query_type
10
+ 'query'
11
+ end
12
+ end
13
+ end
14
+ end
@@ -9,14 +9,17 @@ module GraphqlConnector
9
9
  end
10
10
 
11
11
  def query(model, conditions, selected_fields)
12
- query_string = GraphqlConnector::QueryBuilder.new(model,
13
- conditions,
14
- selected_fields).create
12
+ query_string =
13
+ Formatters::QueryFormat.new(model, conditions, selected_fields).create
15
14
  parsed_body = raw_query(query_string)
16
- result = parsed_body['data'][model.to_s]
17
- return OpenStruct.new(result) unless result.is_a? Array
15
+ format_body(parsed_body['data'][model.to_s])
16
+ end
18
17
 
19
- result.map { |entry| OpenStruct.new(entry) }
18
+ def mutation(model, inputs, selected_fields)
19
+ query_string =
20
+ Formatters::MutationFormat.new(model, inputs, selected_fields).create
21
+ parsed_body = raw_query(query_string)
22
+ format_body(parsed_body['data'][model.to_s])
20
23
  end
21
24
 
22
25
  def raw_query(query_string, variables: {})
@@ -31,6 +34,12 @@ module GraphqlConnector
31
34
 
32
35
  private
33
36
 
37
+ def format_body(response_body)
38
+ return OpenStruct.new(response_body) unless response_body.is_a? Array
39
+
40
+ response_body.map { |entry| OpenStruct.new(entry) }
41
+ end
42
+
34
43
  def verify_response!(parsed_body)
35
44
  return unless parsed_body.key? 'errors'
36
45
 
@@ -13,9 +13,9 @@ module GraphqlConnector
13
13
  .map(&:to_s)
14
14
  .include?(class_method_name.to_s)
15
15
 
16
- error_msg = "The (raw_)add_query '#{class_method_name}: ... ' is "\
16
+ error_msg = "The method '#{class_method_name}: ... ' is "\
17
17
  'already implemented within the context of '\
18
- "#{invoked_class} and therefore cannot be used!"
18
+ "#{invoked_class} and therefore cannot be used again!"
19
19
  raise ClassMethodAlreadyImplementedError, error_msg
20
20
  end
21
21
 
@@ -38,6 +38,21 @@ module GraphqlConnector
38
38
  raw_query_keyword_method(class_method_name, query_string, params)
39
39
  end
40
40
 
41
+ def add_mutation(params: [], returns:, **method_to_query)
42
+ class_method_name = method_to_query.first[0]
43
+ query_type = method_to_query.first[1]
44
+ ReturnFieldsValidator.validate(returns)
45
+ ClassMethodValidator.validate_class_method(class_method_name, self)
46
+ ClassMethodValidator.validate_element_class_type(query_type, Symbol)
47
+
48
+ if params.empty?
49
+ return mutation_method(class_method_name, query_type, returns)
50
+ end
51
+
52
+ ParamsValidator.validate(params)
53
+ mutation_keyword_method(class_method_name, query_type, params, returns)
54
+ end
55
+
41
56
  private
42
57
 
43
58
  def query_method(class_method_name, query_type, return_fields)
@@ -71,6 +86,23 @@ module GraphqlConnector
71
86
  end
72
87
  METHOD
73
88
  end
89
+
90
+ def mutation_method(class_method_name, query_type, return_fields)
91
+ define_singleton_method class_method_name do
92
+ http_client.mutation(query_type, {}, return_fields.to_a)
93
+ end
94
+ end
95
+
96
+ def mutation_keyword_method(name, query_type, keywords, return_fields)
97
+ keywords = [keywords].flatten
98
+ instance_eval <<-METHOD, __FILE__, __LINE__ + 1
99
+ def #{name}(#{keywords.map { |keyword| "#{keyword}:" }.join(', ')})
100
+ http_client.mutation("#{query_type}",
101
+ #{CONDITIONS},
102
+ #{return_fields.to_a})
103
+ end
104
+ METHOD
105
+ end
74
106
  end
75
107
  end
76
108
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GraphqlConnector
4
- VERSION = '1.1.1.1'
4
+ VERSION = '1.2.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql_connector
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Garllon
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2020-11-09 00:00:00.000000000 Z
12
+ date: 2020-12-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: httparty
@@ -103,6 +103,7 @@ files:
103
103
  - README.md
104
104
  - bin/console
105
105
  - examples/departments_service_class_examples.rb
106
+ - examples/mutation_examples.rb
106
107
  - examples/query_examples.rb
107
108
  - examples/raw_query_examples.rb
108
109
  - graphql_connector.gemspec
@@ -110,8 +111,10 @@ files:
110
111
  - lib/graphql_connector/base_server_type.rb
111
112
  - lib/graphql_connector/configuration.rb
112
113
  - lib/graphql_connector/custom_attribute_error.rb
114
+ - lib/graphql_connector/formatters/base_format.rb
115
+ - lib/graphql_connector/formatters/mutation_format.rb
116
+ - lib/graphql_connector/formatters/query_format.rb
113
117
  - lib/graphql_connector/http_client.rb
114
- - lib/graphql_connector/query_builder.rb
115
118
  - lib/graphql_connector/service_classable/class_method_validator.rb
116
119
  - lib/graphql_connector/service_classable/params_validator.rb
117
120
  - lib/graphql_connector/service_classable/queryable.rb
@@ -1,61 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module GraphqlConnector
4
- # create the graphql query_string out of the given attributes.
5
- class QueryBuilder
6
- def initialize(model, conditions, selected_fields)
7
- @model = model
8
- @conditions = conditions
9
- @selected_fields = selected_fields
10
- end
11
-
12
- def create
13
- "query { #{main_filter} { #{parse_fields(@selected_fields)} } }"
14
- end
15
-
16
- private
17
-
18
- def main_filter
19
- conditions = @conditions.each_with_object([]) do |(key, value), array|
20
- next if value.is_a? Hash # will be processed in #field_with_filter
21
-
22
- array << "#{key}: #{value_as_parameter(value)}"
23
- end
24
-
25
- return @model if conditions.empty?
26
-
27
- "#{@model}(#{conditions.join(', ')})"
28
- end
29
-
30
- def value_as_parameter(value)
31
- case value
32
- when TrueClass, FalseClass, Integer, Float
33
- value
34
- when Array
35
- casted_values = value.map { |v| value_as_parameter(v) }
36
- "[#{casted_values.join(',')}]"
37
- else # fallback to string
38
- '"' + value.to_s + '"'
39
- end
40
- end
41
-
42
- def parse_fields(selected_fields)
43
- results = selected_fields.map do |field|
44
- case field
45
- when Hash
46
- handle_association(field)
47
- else
48
- field
49
- end
50
- end
51
-
52
- results.join(' ')
53
- end
54
-
55
- def handle_association(hash)
56
- hash.map do |key, fields|
57
- "#{key} { #{parse_fields(fields)} }"
58
- end
59
- end
60
- end
61
- end