graphql_connector 1.0.0 → 1.3.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: 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