graphql_connector 1.0.0 → 1.3.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: 3fe4b7086d0edec851e2a6b7f12708e805e6d5eac0454d5eef4aa5cb3267ef4f
4
- data.tar.gz: e2261580d8b3b238a7f9c9e1bdc6227290dc42efd51931d15a1a54d54e06afc9
3
+ metadata.gz: dec05781e16009638ffb57356c4b648730b6d04eb60129211cc1f462c6b1db77
4
+ data.tar.gz: fde0b8313c159ae15b43b3aa050c53c710902854080459e41d7da28595226c8d
5
5
  SHA512:
6
- metadata.gz: 8b45378200a895f3f163e52e99dce0547fa024316cceb5e3e0fa3a1b67859abc230435096667bdf1cb997a67b5b3d00c6e5fccf5d5ea59a1fac7f042b91c5b08
7
- data.tar.gz: 621b269795d71c4bdf8ce268cb93b6f5e084e7c434c6d17e01b1c18179e8f133f3cd7b44716669e7d829ebdcbb51f8530b087432669ebd98ebe0a6996a619e95
6
+ metadata.gz: 4a30c45c22bc87d78bc34377c1bca7e40c78d72c96a8d1e840ee89aec119cacfc01b1a517c86c5ca2867573a160742382bd3bf9ce02f256e8e8f42313b36aa5b
7
+ data.tar.gz: 218116c1728b4da4583e709435df2f2e1ba3477edcae34ec0958f398e42ccad3fdee8b80d3de1cc3312c4d7cae9a97add6384273d2cc162a4a86031dd4c5f149
@@ -0,0 +1,37 @@
1
+ name: CI
2
+
3
+ on: [push]
4
+
5
+ jobs:
6
+ build:
7
+ runs-on: ubuntu-latest
8
+ strategy:
9
+ matrix:
10
+ ruby: ['2.4.10', '2.5.9', '2.6.7', '2.7.3', '3.0.1']
11
+ name: "ruby ${{ matrix.ruby }}"
12
+ steps:
13
+ - uses: actions/checkout@v1
14
+ - uses: ruby/setup-ruby@v1
15
+ with:
16
+ ruby-version: ${{ matrix.ruby }}
17
+ - name: Install dependencies
18
+ run: |
19
+ gem install bundler
20
+ bundle install
21
+ - name: Run Test with rspec
22
+ run: bundle exec rspec spec
23
+ rubocop:
24
+ name: Rubocop
25
+ runs-on: ubuntu-latest
26
+ steps:
27
+ - uses: actions/checkout@v2
28
+ - uses: ruby/setup-ruby@v1
29
+ with:
30
+ ruby-version: 2.7.3
31
+ - name: Install dependencies
32
+ run: |
33
+ gem install bundler
34
+ bundle install
35
+ - name: Build and test
36
+ run: |
37
+ bundle exec rubocop
data/.rspec CHANGED
@@ -1 +1,2 @@
1
1
  --require spec_helper
2
+ --order rand
data/CHANGELOG.md CHANGED
@@ -1,3 +1,32 @@
1
+ ## 1.3.0 (2021-06-02)
2
+
3
+ * add the option to use a connector instead of init header for authorization
4
+ part of headers
5
+
6
+ ## 1.2.1 (2021-05-25)
7
+
8
+ * relax httparty dependency(`~> 0.17` => `~> 0.16`)
9
+
10
+ ## 1.2.0 (2020-12-22)
11
+
12
+ ### Features
13
+ * Add `mutation` under server namespace and service class inclusion
14
+ * See `README` for details
15
+
16
+ ## 1.1.1 (2020-5-04)
17
+
18
+ ### BugFix
19
+ * Omit invalid `()` for empty conditions in `query` method
20
+
21
+ ## 1.1.0 (2020-4-19)
22
+
23
+ ### Features
24
+ * Allow building graphql querying in custom classes via `service class inclusion`
25
+ * See `README` for details about `service class inclusion`
26
+
27
+ ### BugFix
28
+ * Forward `variables` when performing a `raw_query`
29
+
1
30
  ## 1.0.0 (2020-1-26)
2
31
 
3
32
  ### Breaking
data/Gemfile.lock CHANGED
@@ -1,8 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- graphql_connector (1.0.0)
5
- httparty (~> 0.17)
4
+ graphql_connector (1.3.0)
5
+ httparty (~> 0.16)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
@@ -10,45 +10,46 @@ GEM
10
10
  ast (2.4.0)
11
11
  coderay (1.1.2)
12
12
  diff-lcs (1.3)
13
- httparty (0.17.1)
13
+ httparty (0.18.1)
14
14
  mime-types (~> 3.0)
15
15
  multi_xml (>= 0.5.2)
16
- jaro_winkler (1.5.3)
17
- method_source (0.9.2)
18
- mime-types (3.3)
16
+ jaro_winkler (1.5.4)
17
+ method_source (1.0.0)
18
+ mime-types (3.3.1)
19
19
  mime-types-data (~> 3.2015)
20
- mime-types-data (3.2019.1009)
20
+ mime-types-data (3.2021.0225)
21
21
  multi_xml (0.6.0)
22
- parallel (1.17.0)
23
- parser (2.6.5.0)
22
+ parallel (1.19.1)
23
+ parser (2.7.1.1)
24
24
  ast (~> 2.4.0)
25
- pry (0.12.2)
26
- coderay (~> 1.1.0)
27
- method_source (~> 0.9.0)
25
+ pry (0.13.1)
26
+ coderay (~> 1.1)
27
+ method_source (~> 1.0)
28
28
  rainbow (3.0.0)
29
- rake (10.5.0)
30
- rspec (3.8.0)
31
- rspec-core (~> 3.8.0)
32
- rspec-expectations (~> 3.8.0)
33
- rspec-mocks (~> 3.8.0)
34
- rspec-core (3.8.2)
35
- rspec-support (~> 3.8.0)
36
- rspec-expectations (3.8.5)
29
+ rexml (3.2.5)
30
+ rspec (3.9.0)
31
+ rspec-core (~> 3.9.0)
32
+ rspec-expectations (~> 3.9.0)
33
+ rspec-mocks (~> 3.9.0)
34
+ rspec-core (3.9.1)
35
+ rspec-support (~> 3.9.1)
36
+ rspec-expectations (3.9.1)
37
37
  diff-lcs (>= 1.2.0, < 2.0)
38
- rspec-support (~> 3.8.0)
39
- rspec-mocks (3.8.2)
38
+ rspec-support (~> 3.9.0)
39
+ rspec-mocks (3.9.1)
40
40
  diff-lcs (>= 1.2.0, < 2.0)
41
- rspec-support (~> 3.8.0)
42
- rspec-support (3.8.3)
43
- rubocop (0.75.0)
41
+ rspec-support (~> 3.9.0)
42
+ rspec-support (3.9.2)
43
+ rubocop (0.82.0)
44
44
  jaro_winkler (~> 1.5.1)
45
45
  parallel (~> 1.10)
46
- parser (>= 2.6)
46
+ parser (>= 2.7.0.1)
47
47
  rainbow (>= 2.2.2, < 4.0)
48
+ rexml
48
49
  ruby-progressbar (~> 1.7)
49
- unicode-display_width (>= 1.4.0, < 1.7)
50
+ unicode-display_width (>= 1.4.0, < 2.0)
50
51
  ruby-progressbar (1.10.1)
51
- unicode-display_width (1.6.0)
52
+ unicode-display_width (1.7.0)
52
53
 
53
54
  PLATFORMS
54
55
  ruby
@@ -57,9 +58,8 @@ DEPENDENCIES
57
58
  bundler (~> 2.0)
58
59
  graphql_connector!
59
60
  pry (~> 0.10)
60
- rake (~> 10.0)
61
61
  rspec (~> 3.8)
62
62
  rubocop (~> 0.75)
63
63
 
64
64
  BUNDLED WITH
65
- 2.0.2
65
+ 2.1.4
data/README.md CHANGED
@@ -3,8 +3,7 @@
3
3
 
4
4
  [![Gem
5
5
  Version](https://badge.fury.io/rb/graphql_connector.svg)](https://badge.fury.io/rb/graphql_connector)
6
- [![Build
7
- Status](https://travis-ci.org/Garllon/graphql_connector.svg?branch=master)](https://travis-ci.org/Garllon/graphql_connector)
6
+ [![CI](https://github.com/Garllon/graphql_connector/workflows/CI/badge.svg)](https://github.com/Garllon/graphql_connector/actions?query=workflow%3ACI)
8
7
  [![Maintainability](https://api.codeclimate.com/v1/badges/548db3cf0d078b379c84/maintainability)](https://codeclimate.com/github/Garllon/graphql_connector/maintainability)
9
8
 
10
9
  An easy connector to call your `graphql` server. Currently there is no schema
@@ -31,21 +30,47 @@ Or install it yourself as:
31
30
  You need to configure the `graphql_connector` first:
32
31
  ``` ruby
33
32
  GraphqlConnector.configure do |config|
34
- config.add_server(name: 'Foo', uri: 'http://foo.com/api/graphql', headers: {})
33
+ config.add_server(name: 'Foo', uri: 'http://foo.com/api/graphql', headers: {}, connector: {})
35
34
  end
35
+ ```
36
36
 
37
- For each graphql server you wish to query use `add_server`.
37
+ The connector is expecting that it contains a `base` connector instance and a
38
+ `method` parameter as string, where it gets the token. Currently like this:
39
+ ```ruby
40
+ { base: TokenAgent.new, method: 'get_authorization_header' }
38
41
  ```
39
42
 
43
+ Your method should return a hash like this:
44
+ ```ruby
45
+ { 'Authorization' => 'Token HERE' }
46
+
47
+ When you set a connector, it will override the setting in the headers for
48
+ Authorization.
49
+
50
+ For each graphql server you wish to query use `add_server`.
51
+
52
+ Afterwards you will have the following options to fetch and/or mutate data with
53
+ one or many graphql servers:
54
+
55
+ * `raw_query` --> [Examples](examples/raw_query_examples.rb)
56
+ * `query` --> [Examples](examples/query_examples.rb)
57
+ * `mutation` --> [Examples](examples/mutation_examples.rb)
58
+ * `service class inclusion` --> [Examples](examples/departments_service_class_examples.rb)
59
+
60
+ See the following sub sections for details
61
+
40
62
  ### raw_query
41
63
 
42
- Then you can call your graphql_endpoint:
64
+ You can call your graphql_endpoint via:
43
65
  ```ruby
44
66
  GraphqlConnector::<name>.raw_query(query_string)
45
67
  ```
46
68
 
47
- Note that `<name>` has to be replaced by any of the ones added via `add_mapping`
69
+ Note that `<name>` has to be replaced by any of the ones added via `add_server`
48
70
 
71
+ See also [here](examples/raw_query_examples.rb) for example usage
72
+
73
+ ---
49
74
  ### query
50
75
 
51
76
  You can also use the more comfortable `query`:
@@ -64,9 +89,11 @@ GraphqlConnector::<name>.query(model, condition, selected_fields)
64
89
  > supported with OpenStruct, associated objects are still a normal array of
65
90
  > hashes.
66
91
 
92
+ See also [here](examples/query_examples.rb) for example usage
93
+
67
94
  #### selected_fields
68
95
 
69
- The synatx for the associations looks like the following:
96
+ The syntax for the associations looks like the following:
70
97
  ```
71
98
  ['<attribute_name>', <association_name>: ['<attribute_name_of_the_association>']]
72
99
  ```
@@ -76,6 +103,129 @@ Example:
76
103
  ['id', 'name', productCategory: ['id', 'name']]
77
104
  ```
78
105
 
106
+ ---
107
+
108
+ ### mutation
109
+
110
+ Works in the same way as [query](#query)
111
+
112
+ See also [here](examples/mutation_examples.rb) for example usage
113
+
114
+ ### Service class inclusion
115
+
116
+ This approach can be used to `graphqlize` **any** kind of ruby (service) class
117
+ so that it has re-usable graphql `query` and `mutation` **class methods**.
118
+
119
+ * First add `extend GraphqlConnector::<server>::Query` in the the class(es) that should be `graphqlized`
120
+
121
+ * Then you can aliases as many graphql server types via `add_query` and/or `add_raw_query` and/or `add_mutation`:
122
+
123
+ ```ruby
124
+ add_query <alias>: :<graphql_server_type>, params: [...], returns: [...]
125
+
126
+ add_raw_query <alias>: 'query { ... }', params: [...]
127
+
128
+ add_mutation <alias>: :<graphql_server_type>, params: [...], returns: [...]
129
+ ```
130
+ * :grey_exclamation: If not needed omit `params`
131
+
132
+ See also [here](examples/departments_service_class_examples.rb) and also here for complete example usage:
133
+
134
+ ```ruby
135
+ GraphqlConnector.configure do |config|
136
+ config.add_server(name: 'Foo', uri: 'http://foo.com/api/graphql', headers: {})
137
+ end
138
+
139
+ # product.rb
140
+ class Product
141
+ extend GraphqlConnector::Foo::Query
142
+
143
+ add_query all: :products,
144
+ returns: [:id, :name]
145
+
146
+ add_query by_id: :products,
147
+ params: :id,
148
+ returns: [:name, product_category: [:id, :name]]
149
+
150
+ add_query by_names: :products,
151
+ params: :names,
152
+ returns: [:id, :name, product_category: [:id, :name]]
153
+
154
+ add_query by: :products,
155
+ params: [:id, :name],
156
+ returns: [:name]
157
+
158
+ add_query by_category_id: :products,
159
+ params: :product_category,
160
+ returns: [product_category: [:id, :name]]
161
+
162
+ add_mutation create: :createProduct,
163
+ params: [:name, :catgetoryId],
164
+ returns: [:id, :name]
165
+ end
166
+
167
+ Product.all
168
+ => [OpenStruct<id=1, name='Demo Product', ...]
169
+
170
+ Product.by_id(id: 1)
171
+ => [OpenStruct<name='Demo Product', product_category=<ProductCategory<id=10, name='Demo Category'>>]
172
+
173
+ Product.by_names(names: ['Demo Product', 'Non Demo Product'])
174
+ => [OpenStruct<id=1, name='Demo Product', product_category=<ProductCategory<id=10, name='Demo Category'>>, Product<id=2, name='Demo Product' ...]
175
+
176
+ Product.by(id: 1, name: 'Demo Product')
177
+ => OpenStruct<name='Demo Product'>
178
+
179
+ Product.by_category_id(product_category: { id: 10})
180
+ => OpenStruct<product_category=<ProductCategory<id=10, name='Demo Category'>>
181
+
182
+ Product.create(name: 'Another Product', catgetoryId: 10)
183
+ => OpenStruct<id=10, name='Another Product'>
184
+ ```
185
+
186
+ Also custom **class methods** can used to call any kind of `query` and do further selection instead:
187
+
188
+ ```ruby
189
+ class Product
190
+ extend GraphqlConnector::Foo::Query
191
+
192
+ add_query all: :products, returns: [:name]
193
+
194
+ def self.by_id(id:)
195
+ all.select { |products| products.id == id }.first
196
+ end
197
+ end
198
+
199
+ Product.by_id(id: 1)
200
+ => OpenStruct<id=1, name='Demo Product'>>
201
+ ```
202
+
203
+ :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/)
204
+
205
+
206
+ Example for `raw_query`:
207
+
208
+ ```ruby
209
+ class Product
210
+ extend GraphqlConnector::Foo::Query
211
+
212
+ add_raw_query all: ' query { products { id name } } '
213
+ add_raw_query by: ' query products($id: !ID, $name: !String) '\
214
+ '{ products(id: $id, name: $name) { id name } }',
215
+ params: [:id, :name]
216
+
217
+ end
218
+
219
+ Product.all
220
+ => [ { id: '1', name: 'Demo Product' }, ...]
221
+
222
+ Product.by(id: '1', name: 'Demo Product')
223
+ => { id: '1', name: 'Demo Product' }
224
+
225
+ ```
226
+
227
+ :exclamation: There is no `add_raw_mutation` since `add_raw_query` does already cover such a case
228
+
79
229
  ## Development
80
230
 
81
231
  After checking out the repo, run
@@ -100,4 +250,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
100
250
 
101
251
  ## Code of Conduct
102
252
 
103
- Everyone interacting in the GraphqlConnector project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/graphql_connector/blob/master/CODE_OF_CONDUCT.md).
253
+ Everyone interacting in the GraphqlConnector project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/Garllon/graphql_connector/blob/master/CODE_OF_CONDUCT.md).
@@ -0,0 +1,51 @@
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
+ # Service class for fetching department data
14
+ class Department
15
+ extend GraphqlConnector::RailsGraphqlServer::Query
16
+
17
+ add_query all: :departments, returns: [:id, :name, employees: [:yearlySalary]]
18
+
19
+ add_query by_id: :departments,
20
+ params: [:id],
21
+ returns: [:id, :name, employees: [:yearlySalary]]
22
+
23
+ add_raw_query all_raw: 'query {
24
+ departments {
25
+ id name employees { yearlySalary }
26
+ }
27
+ }'
28
+
29
+ add_raw_query by_id_raw: 'query departments($id: [ID!]) {
30
+ departments(id: $id) {
31
+ name employees { name }
32
+ }
33
+ }',
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]]
41
+ end
42
+
43
+ Department.all
44
+
45
+ Department.by_id(id: %w[1 2])
46
+
47
+ Department.all_raw
48
+
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)
@@ -0,0 +1,21 @@
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
+ GraphqlConnector::RailsGraphqlServer.query('departments',
14
+ {},
15
+ ['id', 'name',
16
+ 'employees' => ['yearlySalary']])
17
+
18
+ GraphqlConnector::RailsGraphqlServer.query('departments',
19
+ { id: %w[1 2] },
20
+ ['id', 'name',
21
+ 'employees' => ['yearlySalary']])
@@ -0,0 +1,24 @@
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
+ GraphqlConnector.configure do |config|
9
+ config.add_server(name: 'RailsGraphqlServer',
10
+ uri: 'http://rails-graphql-server.herokuapp.com/api/graphql',
11
+ headers: {})
12
+ end
13
+
14
+ GraphqlConnector::RailsGraphqlServer.raw_query(
15
+ 'query { departments { id name employees { yearlySalary } } }'
16
+ )
17
+
18
+ GraphqlConnector::RailsGraphqlServer.raw_query(
19
+ 'query departments($id: [ID!]) {
20
+ departments(id: $id) { name employees { name }
21
+ }
22
+ }',
23
+ variables: { id: %w[1 2] }
24
+ )
@@ -7,12 +7,13 @@ require 'graphql_connector/version'
7
7
  Gem::Specification.new do |spec|
8
8
  spec.name = 'graphql_connector'
9
9
  spec.version = GraphqlConnector::VERSION
10
- spec.authors = ['Garllon']
11
- spec.email = ['palluthe.bennet@gmail.com']
10
+ spec.authors = %w[Garllon sushie1984]
11
+ spec.email = ['palluthe.bennet@gmail.com', 'sascha_burku@yahoo.de']
12
12
 
13
- spec.summary = 'Simple GraphQL client'
14
- spec.description = 'Simple grahql client to query with your own raw string'\
15
- ' or with the small helper method query.'
13
+ spec.summary = 'GraphQL client'
14
+ spec.description = 'Grahql client to query with your own raw string, '\
15
+ 'with the small helper method query or with service '\
16
+ 'class inclusion.'
16
17
  spec.homepage = 'https://github.com/Garllon/graphql_connector/blob/master/README.md'
17
18
  spec.license = 'MIT'
18
19
 
@@ -30,11 +31,10 @@ Gem::Specification.new do |spec|
30
31
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
32
  spec.require_paths = ['lib']
32
33
 
33
- spec.add_dependency 'httparty', '~> 0.17'
34
+ spec.add_dependency 'httparty', '~> 0.16'
34
35
 
35
36
  spec.add_development_dependency 'bundler', '~> 2.0'
36
37
  spec.add_development_dependency 'pry', '~> 0.10'
37
- spec.add_development_dependency 'rake', '~> 10.0'
38
38
  spec.add_development_dependency 'rspec', '~> 3.8'
39
39
  spec.add_development_dependency 'rubocop', '~> 0.75'
40
40
  end
@@ -1,10 +1,16 @@
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'
10
+ require 'graphql_connector/service_classable/class_method_validator'
11
+ require 'graphql_connector/service_classable/params_validator'
12
+ require 'graphql_connector/service_classable/return_fields_validator'
13
+ require 'graphql_connector/service_classable/queryable'
8
14
  require 'graphql_connector/custom_attribute_error'
9
15
  require 'httparty'
10
16
 
@@ -21,7 +27,7 @@ module GraphqlConnector
21
27
  end
22
28
 
23
29
  def self.reset
24
- @configuration = Configuration.new
30
+ @configuration.reset!
25
31
  end
26
32
 
27
33
  def self.configure
@@ -5,12 +5,12 @@ module GraphqlConnector
5
5
  # Class to wrap http_client calls under a specific namespaced class
6
6
  class BaseServerType
7
7
  class << self
8
- def build(name, uri, headers)
8
+ def build(name, uri, headers, connector = {})
9
9
  verify_new_client_type_for!(name)
10
- base_class = class_with(uri, headers)
10
+ base_class = class_with(uri, headers, connector)
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)
13
+ create_service_class_module(base_object)
14
14
 
15
15
  base_object
16
16
  end
@@ -25,30 +25,37 @@ module GraphqlConnector
25
25
  'configuration!'
26
26
  end
27
27
 
28
- def class_with(uri, headers)
29
- Class.new do
30
- attr_accessor :uri, :headers
31
- @uri = uri
32
- @headers = headers
33
- end
34
- end
28
+ def create_service_class_module(base_object)
29
+ base_object.class_eval <<-METHOD, __FILE__, __LINE__ + 1
30
+ module Query
31
+ def self.extended(base)
32
+ base.extend(GraphqlConnector::ServiceClassable::Queryable)
33
+ end
35
34
 
36
- def inject_http_client(base_object)
37
- base_object.instance_eval do
38
- def http_client
39
- @http_client ||= GraphqlConnector::HttpClient.new(@uri, @headers)
35
+ def http_client
36
+ #{base_object}.http_client
37
+ end
40
38
  end
39
+ METHOD
40
+ end
41
+
42
+ def class_with(uri, headers, connector = {})
43
+ Class.new do
44
+ attr_accessor :uri, :headers, :connector
45
+ @uri = uri
46
+ @headers = headers
47
+ @connector = connector
41
48
  end
42
49
  end
43
50
 
44
- def inject_query_methods(base_object)
51
+ def inject_http_client_delegations(base_object)
45
52
  base_object.instance_eval do
46
- def query(model, conditions, selected_fields)
47
- http_client.query(model, conditions, selected_fields)
48
- end
53
+ extend SingleForwardable
54
+ def_delegators :http_client, :query, :raw_query, :mutation
49
55
 
50
- def raw_query(query_string)
51
- http_client.raw_query(query_string)
56
+ def http_client
57
+ @http_client ||=
58
+ GraphqlConnector::HttpClient.new(@uri, @headers, @connector)
52
59
  end
53
60
  end
54
61
  end
@@ -9,12 +9,14 @@ module GraphqlConnector
9
9
  @base_server_types = {}
10
10
  end
11
11
 
12
- def add_server(name:, uri:, headers:)
13
- @base_server_types[name] = BaseServerType.build(name, uri, headers)
12
+ def add_server(name:, uri:, headers:, connector: {})
13
+ @base_server_types[name] =
14
+ BaseServerType.build(name, uri, headers, connector)
14
15
  end
15
16
 
16
17
  def reset!
17
18
  @base_server_types.keys.each do |name|
19
+ GraphqlConnector.const_get(name).send :remove_const, 'Query'
18
20
  GraphqlConnector.send :remove_const, name
19
21
  end
20
22
  @base_server_types = {}
@@ -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
@@ -3,26 +3,31 @@
3
3
  module GraphqlConnector
4
4
  # Wrapper class for HTTParty post query
5
5
  class HttpClient
6
- def initialize(uri, headers)
6
+ def initialize(uri, headers, connector = {})
7
7
  @uri = uri
8
8
  @headers = headers
9
+ @connector = connector
9
10
  end
10
11
 
11
12
  def query(model, conditions, selected_fields)
12
- query_string = GraphqlConnector::QueryBuilder.new(model,
13
- conditions,
14
- selected_fields).create
13
+ query_string =
14
+ Formatters::QueryFormat.new(model, conditions, selected_fields).create
15
15
  parsed_body = raw_query(query_string)
16
- result = parsed_body['data'][model]
17
- return OpenStruct.new(result) unless result.is_a? Array
16
+ format_body(parsed_body['data'][model.to_s])
17
+ end
18
18
 
19
- result.map { |entry| OpenStruct.new(entry) }
19
+ def mutation(model, inputs, selected_fields)
20
+ query_string =
21
+ Formatters::MutationFormat.new(model, inputs, selected_fields).create
22
+ parsed_body = raw_query(query_string)
23
+ format_body(parsed_body['data'][model.to_s])
20
24
  end
21
25
 
22
- def raw_query(query_string)
26
+ def raw_query(query_string, variables: {})
23
27
  response = HTTParty.post(@uri,
24
- headers: @headers,
25
- body: { query: query_string })
28
+ headers: handle_headers,
29
+ body: { query: query_string,
30
+ variables: variables })
26
31
  parsed_body = JSON.parse(response.body)
27
32
  verify_response!(parsed_body)
28
33
  parsed_body
@@ -30,6 +35,19 @@ module GraphqlConnector
30
35
 
31
36
  private
32
37
 
38
+ def handle_headers
39
+ return @headers if @connector.empty?
40
+
41
+ @headers
42
+ .merge(@connector[:base].send(@connector[:method]))
43
+ end
44
+
45
+ def format_body(response_body)
46
+ return OpenStruct.new(response_body) unless response_body.is_a? Array
47
+
48
+ response_body.map { |entry| OpenStruct.new(entry) }
49
+ end
50
+
33
51
  def verify_response!(parsed_body)
34
52
  return unless parsed_body.key? 'errors'
35
53
 
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphqlConnector
4
+ module ServiceClassable
5
+ class ClassMethodAlreadyImplementedError < StandardError; end
6
+ class InvalidClassTypeError < StandardError; end
7
+ # Checks whether a class method for a specific graphql query is in an
8
+ # expected format
9
+ class ClassMethodValidator
10
+ class << self
11
+ def validate_class_method(class_method_name, invoked_class)
12
+ return unless invoked_class.singleton_methods
13
+ .map(&:to_s)
14
+ .include?(class_method_name.to_s)
15
+
16
+ error_msg = "The method '#{class_method_name}: ... ' is "\
17
+ 'already implemented within the context of '\
18
+ "#{invoked_class} and therefore cannot be used again!"
19
+ raise ClassMethodAlreadyImplementedError, error_msg
20
+ end
21
+
22
+ def validate_element_class_type(element, class_types)
23
+ return if element.class == class_types
24
+
25
+ raise InvalidClassTypeError, "Please ensure that #{element} is a"\
26
+ "#{class_types}!"
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphqlConnector
4
+ module ServiceClassable
5
+ class InvalidParamsError < StandardError; end
6
+ # Checks whether params for a specifc graphql query are in an expected
7
+ # format
8
+ class ParamsValidator
9
+ class << self
10
+ def validate(query_params)
11
+ params = [query_params].flatten
12
+ return if params.empty? ||
13
+ params.map(&:class).uniq == [Symbol] ||
14
+ params.map(&:class).uniq == [String]
15
+
16
+ raise InvalidParamsError,
17
+ "Please ensure that #{query_params} are either "\
18
+ 'Symbols/Strings as described in the README '\
19
+ '(e.g.: params: [:id, :name])'
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphqlConnector
4
+ module ServiceClassable
5
+ # Module that allows to build query methods within the context of
6
+ # service class
7
+ module Queryable
8
+ CONDITIONS = 'binding.local_variables.map do |var|
9
+ [var, binding.local_variable_get(var)]
10
+ end.to_h'
11
+
12
+ def add_query(params: [], returns:, **method_to_query)
13
+ class_method_name = method_to_query.first[0]
14
+ query_type = method_to_query.first[1]
15
+ ReturnFieldsValidator.validate(returns)
16
+ ClassMethodValidator.validate_class_method(class_method_name, self)
17
+ ClassMethodValidator.validate_element_class_type(query_type, Symbol)
18
+
19
+ if params.empty?
20
+ return query_method(class_method_name, query_type, returns)
21
+ end
22
+
23
+ ParamsValidator.validate(params)
24
+ query_keyword_method(class_method_name, query_type, params, returns)
25
+ end
26
+
27
+ def add_raw_query(params: [], **method_to_raw_query)
28
+ class_method_name = method_to_raw_query.first[0]
29
+ query_string = method_to_raw_query.first[1]
30
+ ClassMethodValidator.validate_class_method(class_method_name, self)
31
+ ClassMethodValidator.validate_element_class_type(query_string, String)
32
+
33
+ if params.empty?
34
+ return raw_query_method(class_method_name, query_string)
35
+ end
36
+
37
+ ParamsValidator.validate(params)
38
+ raw_query_keyword_method(class_method_name, query_string, params)
39
+ end
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
+
56
+ private
57
+
58
+ def query_method(class_method_name, query_type, return_fields)
59
+ define_singleton_method class_method_name do
60
+ http_client.query(query_type, {}, return_fields.to_a)
61
+ end
62
+ end
63
+
64
+ def raw_query_method(class_method_name, query_string)
65
+ define_singleton_method class_method_name do
66
+ http_client.raw_query(query_string)
67
+ end
68
+ end
69
+
70
+ def query_keyword_method(name, query_type, keywords, return_fields)
71
+ keywords = [keywords].flatten
72
+ instance_eval <<-METHOD, __FILE__, __LINE__ + 1
73
+ def #{name}(#{keywords.map { |keyword| "#{keyword}:" }.join(', ')})
74
+ http_client.query("#{query_type}",
75
+ #{CONDITIONS},
76
+ #{return_fields.to_a})
77
+ end
78
+ METHOD
79
+ end
80
+
81
+ def raw_query_keyword_method(name, query_string, keywords)
82
+ keywords = [keywords].flatten
83
+ instance_eval <<-METHOD, __FILE__, __LINE__ + 1
84
+ def #{name}(#{keywords.map { |keyword| "#{keyword}:" }.join(', ')})
85
+ http_client.raw_query("#{query_string}", variables: #{CONDITIONS})
86
+ end
87
+ METHOD
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
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphqlConnector
4
+ module ServiceClassable
5
+ class ReturnFieldsErrors < StandardError; end
6
+ # Valdations for return fields set within the context of a service class
7
+ class ReturnFieldsValidator
8
+ class << self
9
+ def validate(return_fields)
10
+ unless return_fields.is_a?(Array)
11
+ raise ReturnFieldsErrors, 'Please ensure that returns is followed '\
12
+ 'by an array. E.g. returns: [:id]'
13
+ end
14
+
15
+ return_fields.each { |entry| recursive_validation(entry) }
16
+ end
17
+
18
+ private
19
+
20
+ def recursive_validation(entry)
21
+ case entry
22
+ when Hash
23
+ hash_validation(entry)
24
+ when Array
25
+ entry.each { |item| recursive_validation(item) }
26
+ else
27
+ return if [String, Symbol].member?(entry.class)
28
+
29
+ raise ReturnFieldsErrors, "The #{entry} is neither a String nor a"\
30
+ 'Symbol!'
31
+ end
32
+ end
33
+
34
+ def hash_validation(hash)
35
+ hash.each do |key, value|
36
+ recursive_validation(key)
37
+ recursive_validation(value)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GraphqlConnector
4
- VERSION = '1.0.0'
4
+ VERSION = '1.3.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql_connector
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Garllon
8
- autorequire:
8
+ - sushie1984
9
+ autorequire:
9
10
  bindir: exe
10
11
  cert_chain: []
11
- date: 2020-01-28 00:00:00.000000000 Z
12
+ date: 2021-06-02 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: httparty
@@ -16,14 +17,14 @@ dependencies:
16
17
  requirements:
17
18
  - - "~>"
18
19
  - !ruby/object:Gem::Version
19
- version: '0.17'
20
+ version: '0.16'
20
21
  type: :runtime
21
22
  prerelease: false
22
23
  version_requirements: !ruby/object:Gem::Requirement
23
24
  requirements:
24
25
  - - "~>"
25
26
  - !ruby/object:Gem::Version
26
- version: '0.17'
27
+ version: '0.16'
27
28
  - !ruby/object:Gem::Dependency
28
29
  name: bundler
29
30
  requirement: !ruby/object:Gem::Requirement
@@ -52,20 +53,6 @@ dependencies:
52
53
  - - "~>"
53
54
  - !ruby/object:Gem::Version
54
55
  version: '0.10'
55
- - !ruby/object:Gem::Dependency
56
- name: rake
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: '10.0'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: '10.0'
69
56
  - !ruby/object:Gem::Dependency
70
57
  name: rspec
71
58
  requirement: !ruby/object:Gem::Requirement
@@ -94,19 +81,20 @@ dependencies:
94
81
  - - "~>"
95
82
  - !ruby/object:Gem::Version
96
83
  version: '0.75'
97
- description: Simple grahql client to query with your own raw string or with the small
98
- helper method query.
84
+ description: Grahql client to query with your own raw string, with the small helper
85
+ method query or with service class inclusion.
99
86
  email:
100
87
  - palluthe.bennet@gmail.com
88
+ - sascha_burku@yahoo.de
101
89
  executables: []
102
90
  extensions: []
103
91
  extra_rdoc_files: []
104
92
  files:
93
+ - ".github/workflows/ci.yaml"
105
94
  - ".gitignore"
106
95
  - ".rspec"
107
96
  - ".rubocop.yml"
108
97
  - ".rubocop_todo.yml"
109
- - ".travis.yml"
110
98
  - CHANGELOG.md
111
99
  - CODE_OF_CONDUCT.md
112
100
  - Gemfile
@@ -114,13 +102,23 @@ files:
114
102
  - LICENSE.txt
115
103
  - README.md
116
104
  - bin/console
105
+ - examples/departments_service_class_examples.rb
106
+ - examples/mutation_examples.rb
107
+ - examples/query_examples.rb
108
+ - examples/raw_query_examples.rb
117
109
  - graphql_connector.gemspec
118
110
  - lib/graphql_connector.rb
119
111
  - lib/graphql_connector/base_server_type.rb
120
112
  - lib/graphql_connector/configuration.rb
121
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
122
117
  - lib/graphql_connector/http_client.rb
123
- - lib/graphql_connector/query_builder.rb
118
+ - lib/graphql_connector/service_classable/class_method_validator.rb
119
+ - lib/graphql_connector/service_classable/params_validator.rb
120
+ - lib/graphql_connector/service_classable/queryable.rb
121
+ - lib/graphql_connector/service_classable/return_fields_validator.rb
124
122
  - lib/graphql_connector/version.rb
125
123
  homepage: https://github.com/Garllon/graphql_connector/blob/master/README.md
126
124
  licenses:
@@ -129,7 +127,7 @@ metadata:
129
127
  homepage_uri: https://github.com/Garllon/graphql_connector/blob/master/README.md
130
128
  source_code_uri: https://github.com/Garllon/graphql_connector
131
129
  changelog_uri: https://github.com/Garllon/graphql_connector/blob/master/CHANGELOG.md
132
- post_install_message:
130
+ post_install_message:
133
131
  rdoc_options: []
134
132
  require_paths:
135
133
  - lib
@@ -144,8 +142,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
144
142
  - !ruby/object:Gem::Version
145
143
  version: '0'
146
144
  requirements: []
147
- rubygems_version: 3.0.6
148
- signing_key:
145
+ rubygems_version: 3.1.4
146
+ signing_key:
149
147
  specification_version: 4
150
- summary: Simple GraphQL client
148
+ summary: GraphQL client
151
149
  test_files: []
data/.travis.yml DELETED
@@ -1,9 +0,0 @@
1
- ---
2
- sudo: false
3
- language: ruby
4
- cache: bundler
5
- rvm:
6
- - 2.6.4
7
- before_install: gem install bundler -v 2.0.2
8
- script:
9
- - bundle exec rspec spec
@@ -1,59 +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
- "#{@model}(#{conditions.join(', ')})"
26
- end
27
-
28
- def value_as_parameter(value)
29
- case value
30
- when TrueClass, FalseClass, Integer, Float
31
- value
32
- when Array
33
- casted_values = value.map { |v| value_as_parameter(v) }
34
- "[#{casted_values.join(',')}]"
35
- else # fallback to string
36
- '"' + value.to_s + '"'
37
- end
38
- end
39
-
40
- def parse_fields(selected_fields)
41
- results = selected_fields.map do |field|
42
- case field
43
- when Hash
44
- handle_association(field)
45
- else
46
- field
47
- end
48
- end
49
-
50
- results.join(' ')
51
- end
52
-
53
- def handle_association(hash)
54
- hash.map do |key, fields|
55
- "#{key} { #{parse_fields(fields)} }"
56
- end
57
- end
58
- end
59
- end