graphql_connector 1.1.1.1 → 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.
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