graphql_connector 0.1.0.beta → 1.1.1.1

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: 4e9f9c92e28fb86eff5e5fce2e6f7ed2302517c43bb0697247aaf729c0d23ba5
4
- data.tar.gz: 8ae4ab7860cef16ecf330314d5fc1739851eb198945e248e4327ce5bcc39785e
3
+ metadata.gz: 8dbc699b8eeae3209dd9c8b4ddd01bcd0e544a56ac099465dc1508b1cb5d89fc
4
+ data.tar.gz: b7136a702f6c91413eb232cc3c10516c4404462a5ebe97a74bb573df6ce70f79
5
5
  SHA512:
6
- metadata.gz: 4fcfa21b2178e1408b115bb4741217449bebad3c50fef8a2c260aaddb6d54a8b5207e502be349c55cfc1521b3a5484f7e2d59a67c5c3f4e766f64ae74ef254b0
7
- data.tar.gz: 5d96018a7e2f5ab81fa0e35bec3b0e2d840d799be6551ac9aee4c63366bd7d7a77fd210ebbc9b85a34303145cb3687719aa6cb1dbda13a55413d080fb309ce26
6
+ metadata.gz: 28c64334cab94490d9f0cb4cd3aa210a5f4c647282b72ebb5bd9d7ed45ab29afd4e1dda57b483ab341835da4b52770a84e493f6a4e0e2cbc9a8d111a08832ccc
7
+ data.tar.gz: 9f4d50ce4a1cc525834c51f1f1a454ae00e269c74c6eb19369f398b6acce766c1f5d24c5ecf81698ca3f1d49df09686ca741d3454c467e50285f11cd58cbdbb9
@@ -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.x', '2.5.x', '2.6.x', '2.7.x']
11
+ name: "ruby ${{ matrix.ruby }}"
12
+ steps:
13
+ - uses: actions/checkout@v1
14
+ - uses: actions/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: actions/setup-ruby@v1
29
+ with:
30
+ ruby-version: 2.7.x
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
@@ -0,0 +1,2 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
@@ -0,0 +1,6 @@
1
+ Lint/DuplicateMethods:
2
+ Exclude:
3
+ - lib/graphql_connector.rb
4
+ Metrics/BlockLength:
5
+ Exclude:
6
+ - spec/graphql_connector/**/*.rb
@@ -1,3 +1,37 @@
1
- ### 0.1.0 Init
1
+ ## 1.1.1 (2020-5-04)
2
2
 
3
+ ### BugFix
4
+ * Omit invalid `()` for empty conditions in `query` method
3
5
 
6
+ ## 1.1.0 (2020-4-19)
7
+
8
+ ### Features
9
+ * Allow building graphql querying in custom classes via `service class inclusion`
10
+ * See `README` for details about `service class inclusion`
11
+
12
+ ### BugFix
13
+ * Forward `variables` when performing a `raw_query`
14
+
15
+ ## 1.0.0 (2020-1-26)
16
+
17
+ ### Breaking
18
+ * add multiple graphql server querying
19
+ * `query` and `raw_query` nested under server namespace
20
+
21
+
22
+ ## 0.2.0 (2019-11-19)
23
+
24
+ ### Features
25
+ * new `raw_query` method. you have to write the graphql query
26
+ string by your self and also you get only the parsed json back.
27
+ * query supports associations for the selected_fields attribute
28
+
29
+ ## 0.1.0.beta1 (2019-10-06)
30
+
31
+ ### BugFix
32
+ * use model instead of hardcoded product
33
+
34
+
35
+ ## 0.1.0.beta (2019-10-03)
36
+
37
+ * easy graphql logic
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- graphql_connector (0.1.0)
4
+ graphql_connector (1.1.1)
5
5
  httparty (~> 0.17)
6
6
 
7
7
  GEM
@@ -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.0)
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.0904)
20
+ mime-types-data (3.2019.1009)
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.4)
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
@@ -56,10 +57,9 @@ PLATFORMS
56
57
  DEPENDENCIES
57
58
  bundler (~> 2.0)
58
59
  graphql_connector!
59
- pry
60
- rake (~> 10.0)
60
+ pry (~> 0.10)
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
@@ -1,12 +1,13 @@
1
- # GraphqlConnector
2
1
 
3
- [![Build
4
- Status](https://travis-ci.org/Garllon/graphql_connector.svg?branch=master)](https://travis-ci.org/Garllon/graphql_connector)
2
+ # GraphqlConnector
5
3
 
6
- An easy connector to call your `graphql` server.
7
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/graphql_connector`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+ [![Gem
5
+ Version](https://badge.fury.io/rb/graphql_connector.svg)](https://badge.fury.io/rb/graphql_connector)
6
+ [![CI](https://github.com/Garllon/graphql_connector/workflows/CI/badge.svg)](https://github.com/Garllon/graphql_connector/actions?query=workflow%3ACI)
7
+ [![Maintainability](https://api.codeclimate.com/v1/badges/548db3cf0d078b379c84/maintainability)](https://codeclimate.com/github/Garllon/graphql_connector/maintainability)
8
8
 
9
- TODO: Delete this and the text above, and describe your gem
9
+ An easy connector to call your `graphql` server. Currently there is no schema
10
+ check in the code, but i will add it.
10
11
 
11
12
  ## Installation
12
13
 
@@ -29,21 +30,162 @@ Or install it yourself as:
29
30
  You need to configure the `graphql_connector` first:
30
31
  ``` ruby
31
32
  GraphqlConnector.configure do |config|
32
- config.host = ''
33
- config.headers = {}
33
+ config.add_server(name: 'Foo', uri: 'http://foo.com/api/graphql', headers: {})
34
+ end
35
+ ```
36
+
37
+ For each graphql server you wish to query use `add_server`.
38
+
39
+ Afterwards you will have the following options to fetch and/or mutate data with
40
+ one or many graphql servers:
41
+
42
+ * `raw_query` --> [Examples](examples/raw_query_examples.rb)
43
+ * `query` --> [Examples](examples/query_examples.rb)
44
+ * `service class inclusion` --> [Examples](examples/departments_service_class_examples.rb)
45
+
46
+ See the following sub sections for details
47
+
48
+ ### raw_query
49
+
50
+ You can call your graphql_endpoint via:
51
+ ```ruby
52
+ GraphqlConnector::<name>.raw_query(query_string)
53
+ ```
54
+
55
+ Note that `<name>` has to be replaced by any of the ones added via `add_server`
56
+
57
+ See also [here](examples/raw_query_examples.rb) for example usage
58
+
59
+ ---
60
+ ### query
61
+
62
+ You can also use the more comfortable `query`:
63
+ ```ruby
64
+ GraphqlConnector::<name>.query(model, condition, selected_fields)
65
+ ```
66
+
67
+ | Variable | DataType | Example |
68
+ |----------------|-------------------------|------------------------------------------|
69
+ | model | String | 'product' |
70
+ | condition | Hash(key, value) | { id: 1 } |
71
+ | selected_fields | Array of Strings/Hashes | ['id', 'name', productCategory: ['id']] |
72
+
73
+ > Caution:
74
+ > You get an OpenStruct back. Currently only the first level attributes are
75
+ > supported with OpenStruct, associated objects are still a normal array of
76
+ > hashes.
77
+
78
+ See also [here](examples/query_examples.rb) for example usage
79
+
80
+ #### selected_fields
81
+
82
+ The syntax for the associations looks like the following:
83
+ ```
84
+ ['<attribute_name>', <association_name>: ['<attribute_name_of_the_association>']]
85
+ ```
86
+
87
+ Example:
88
+ ```ruby
89
+ ['id', 'name', productCategory: ['id', 'name']]
90
+ ```
91
+
92
+ ---
93
+
94
+ ### Service class inclusion
95
+
96
+ This approach can be used to `graphqlize` **any** kind of ruby (service) class
97
+ so that it has re-usable graphql query methods.
98
+
99
+ * 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
+
105
+ See also [here](examples/departments_service_class_examples.rb) for example usage as also in the following:
106
+
107
+ ```ruby
108
+ GraphqlConnector.configure do |config|
109
+ config.add_server(name: 'Foo', uri: 'http://foo.com/api/graphql', headers: {})
110
+ end
111
+
112
+ # product.rb
113
+ class Product
114
+ extend GraphqlConnector::Foo::Query
115
+
116
+ add_query all: :products,
117
+ returns: [:id, :name]
118
+
119
+ add_query by_id: :products,
120
+ params: :id,
121
+ returns: [:name, product_category: [:id, :name]]
122
+
123
+ add_query by_names: :products,
124
+ params: :names,
125
+ returns: [:id, :name, product_category: [:id, :name]]
126
+
127
+ add_query by: :products,
128
+ params: [:id, :name],
129
+ returns: [:name]
130
+
131
+ add_query by_category_id: :products,
132
+ params: :product_category,
133
+ returns: [product_category: [:id, :name]]
34
134
  end
135
+
136
+ Product.all
137
+ => [OpenStruct<id=1, name='Demo Product', ...]
138
+
139
+ Product.by_id(id: 1)
140
+ => [OpenStruct<name='Demo Product', product_category=<ProductCategory<id=10, name='Demo Category'>>]
141
+
142
+ Product.by_names(names: ['Demo Product', 'Non Demo Product'])
143
+ => [OpenStruct<id=1, name='Demo Product', product_category=<ProductCategory<id=10, name='Demo Category'>>, Product<id=2, name='Demo Product' ...]
144
+
145
+ Product.by(id: 1, name: 'Demo Product')
146
+ => OpenStruct<name='Demo Product'>
147
+
148
+ Product.by_category_id(product_category: { id: 10})
149
+ => OpenStruct<product_category=<ProductCategory<id=10, name='Demo Category'>>
35
150
  ```
36
151
 
37
- Then you can call your graphql_endpoint:
152
+ Also custom **class methods** can used to call any kind of `query` and do further selection instead:
153
+
38
154
  ```ruby
39
- GraphqlConnector.query(model, condition, selected_fields)
155
+ class Product
156
+ extend GraphqlConnector::Foo::Query
157
+
158
+ add_query all: :products, returns: [:name]
159
+
160
+ def self.by_id(id:)
161
+ all.select { |products| products.id == id }.first
162
+ end
163
+ end
164
+
165
+ Product.by_id(id: 1)
166
+ => OpenStruct<id=1, name='Demo Product'>>
40
167
  ```
41
168
 
42
- | Variable | DataType | Example |
43
- | ----------------|------------------| ---------------|
44
- | model | String | 'product' |
45
- | condition | Hash(key, value) | { id: 1 } |
46
- | selected_fields | Array of Strings | ['id', 'name'] |
169
+ Example for `raw_query`:
170
+
171
+ ```ruby
172
+ class Product
173
+ extend GraphqlConnector::Foo::Query
174
+
175
+ add_raw_query all: ' query { products { id name } } '
176
+ add_raw_query by: ' query products($id: !ID, $name: !String) '\
177
+ '{ products(id: $id, name: $name) { id name } }',
178
+ params: [:id, :name]
179
+
180
+ end
181
+
182
+ Product.all
183
+ => [ { id: '1', name: 'Demo Product' }, ...]
184
+
185
+ Product.by(id: '1', name: 'Demo Product')
186
+ => { id: '1', name: 'Demo Product' }
187
+
188
+ ```
47
189
 
48
190
  ## Development
49
191
 
@@ -53,12 +195,15 @@ bundle install
53
195
  ```
54
196
 
55
197
  Then, run
56
- ```bundle exec rspec spec` to run the tests.
198
+ ```shell
199
+ bundle exec rspec spec
200
+ ```
201
+ to run the tests.
57
202
  You can also run `bin/console` for an interactive prompt that will allow you to experiment.
58
203
 
59
204
  ## Contributing
60
205
 
61
- Bug reports and pull requests are welcome on GitHub at https://github.com/garllon/graphql_connector. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
206
+ Bug reports and pull requests are welcome on GitHub at [https://github.com/garllon/graphql_connector](https://github.com/garllon/graphql_connector). This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
62
207
 
63
208
  ## License
64
209
 
@@ -66,4 +211,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
66
211
 
67
212
  ## Code of Conduct
68
213
 
69
- 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).
214
+ 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,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ uri = 'http://rails-graphql-server.herokuapp.com/api/graphql'
4
+ GraphqlConnector.configure do |config|
5
+ config.add_server(name: 'RailsGraphqlServer', uri: uri, headers: {})
6
+ end
7
+
8
+ # Service class for fetching department data
9
+ class Department
10
+ extend GraphqlConnector::RailsGraphqlServer::Query
11
+
12
+ add_query all: :departments, returns: [:id, :name, employees: [:yearlySalary]]
13
+
14
+ add_query by_id: :departments,
15
+ params: [:id],
16
+ returns: [:id, :name, employees: [:yearlySalary]]
17
+
18
+ add_raw_query all_raw: 'query {
19
+ departments {
20
+ id name employees { yearlySalary }
21
+ }
22
+ }'
23
+
24
+ add_raw_query by_id_raw: 'query departments($id: [ID!]) {
25
+ departments(id: $id) {
26
+ name employees { name }
27
+ }
28
+ }',
29
+ params: [:id]
30
+ end
31
+
32
+ Department.all
33
+
34
+ Department.by_id(id: %w[1 2])
35
+
36
+ Department.all_raw
37
+
38
+ Department.by_id_raw(id: %w[1 2])
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ uri = 'http://rails-graphql-server.herokuapp.com/api/graphql'
4
+ GraphqlConnector.configure do |config|
5
+ config.add_server(name: 'RailsGraphqlServer', uri: uri, headers: {})
6
+ end
7
+
8
+ GraphqlConnector::RailsGraphqlServer.query('departments',
9
+ {},
10
+ ['id', 'name',
11
+ 'employees' => ['yearlySalary']])
12
+
13
+ GraphqlConnector::RailsGraphqlServer.query('departments',
14
+ { id: %w[1 2] },
15
+ ['id', 'name',
16
+ 'employees' => ['yearlySalary']])
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ GraphqlConnector.configure do |config|
4
+ config.add_server(name: 'RailsGraphqlServer',
5
+ uri: 'http://rails-graphql-server.herokuapp.com/api/graphql',
6
+ headers: {})
7
+ end
8
+
9
+ GraphqlConnector::RailsGraphqlServer.raw_query(
10
+ 'query { departments { id name employees { yearlySalary } } }'
11
+ )
12
+
13
+ GraphqlConnector::RailsGraphqlServer.raw_query(
14
+ 'query departments($id: [ID!]) {
15
+ departments(id: $id) { name employees { name }
16
+ }
17
+ }',
18
+ variables: { id: %w[1 2] }
19
+ )
@@ -7,11 +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 = 'Hallo'
14
- spec.description = 'Hallo'
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.'
15
17
  spec.homepage = 'https://github.com/Garllon/graphql_connector/blob/master/README.md'
16
18
  spec.license = 'MIT'
17
19
 
@@ -20,7 +22,8 @@ Gem::Specification.new do |spec|
20
22
  spec.metadata['changelog_uri'] = 'https://github.com/Garllon/graphql_connector/blob/master/CHANGELOG.md'
21
23
 
22
24
  # Specify which files should be added to the gem when it is released.
23
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
25
+ # The `git ls-files -z` loads the files in the RubyGem that have been added
26
+ # into git.
24
27
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
25
28
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(spec|features)/}) }
26
29
  end
@@ -31,8 +34,7 @@ Gem::Specification.new do |spec|
31
34
  spec.add_dependency 'httparty', '~> 0.17'
32
35
 
33
36
  spec.add_development_dependency 'bundler', '~> 2.0'
37
+ spec.add_development_dependency 'pry', '~> 0.10'
34
38
  spec.add_development_dependency 'rspec', '~> 3.8'
35
- spec.add_development_dependency 'rake', '~> 10.0'
36
- spec.add_development_dependency 'pry'
37
39
  spec.add_development_dependency 'rubocop', '~> 0.75'
38
40
  end
@@ -3,8 +3,18 @@
3
3
  require 'graphql_connector/version'
4
4
  require 'graphql_connector/query_builder'
5
5
  require 'graphql_connector/configuration'
6
+ require 'graphql_connector/http_client'
7
+ require 'graphql_connector/base_server_type'
8
+ require 'graphql_connector/service_classable/class_method_validator'
9
+ require 'graphql_connector/service_classable/params_validator'
10
+ require 'graphql_connector/service_classable/return_fields_validator'
11
+ require 'graphql_connector/service_classable/queryable'
12
+ require 'graphql_connector/custom_attribute_error'
6
13
  require 'httparty'
7
14
 
15
+ # Main file of the GraphQLConnector
16
+ # the main methods to configure the gem
17
+ # and to run a raw_query or a normal query.
8
18
  module GraphqlConnector
9
19
  class << self
10
20
  attr_accessor :configuration
@@ -15,19 +25,10 @@ module GraphqlConnector
15
25
  end
16
26
 
17
27
  def self.reset
18
- @configuration = Configuration.new
28
+ @configuration.reset!
19
29
  end
20
30
 
21
31
  def self.configure
22
32
  yield(configuration)
23
33
  end
24
-
25
- def self.query(model, conditions, selected_fields)
26
- query_string = QueryBuilder.new(model, conditions, selected_fields).create
27
- response = HTTParty.post(GraphqlConnector.configuration.host,
28
- headers: GraphqlConnector.configuration.headers,
29
- body: { query: query_string })
30
- parsed_body = JSON.parse(response.body)
31
- OpenStruct.new(parsed_body['data']['product'])
32
- end
33
34
  end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphqlConnector
4
+ class BaseServerTypeAlreadyExistsError < StandardError; end
5
+ # Class to wrap http_client calls under a specific namespaced class
6
+ class BaseServerType
7
+ class << self
8
+ def build(name, uri, headers)
9
+ verify_new_client_type_for!(name)
10
+ base_class = class_with(uri, headers)
11
+ base_object = GraphqlConnector.const_set(name, base_class)
12
+ inject_http_client(base_object)
13
+ inject_query_methods(base_object)
14
+ create_service_class_module(base_object)
15
+
16
+ base_object
17
+ end
18
+
19
+ private
20
+
21
+ def verify_new_client_type_for!(name)
22
+ return unless GraphqlConnector.const_defined?(name)
23
+
24
+ raise BaseServerTypeAlreadyExistsError,
25
+ "The name: #{name} is already in use. Check your "\
26
+ 'configuration!'
27
+ end
28
+
29
+ def create_service_class_module(base_object)
30
+ base_object.class_eval <<-METHOD, __FILE__, __LINE__ + 1
31
+ module Query
32
+ def self.extended(base)
33
+ base.extend(GraphqlConnector::ServiceClassable::Queryable)
34
+ end
35
+
36
+ def http_client
37
+ #{base_object}.http_client
38
+ end
39
+ end
40
+ METHOD
41
+ end
42
+
43
+ def class_with(uri, headers)
44
+ Class.new do
45
+ attr_accessor :uri, :headers
46
+ @uri = uri
47
+ @headers = headers
48
+ end
49
+ end
50
+
51
+ def inject_http_client(base_object)
52
+ base_object.instance_eval do
53
+ def http_client
54
+ @http_client ||= GraphqlConnector::HttpClient.new(@uri, @headers)
55
+ end
56
+ end
57
+ 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
+ end
71
+ end
72
+ end
@@ -1,10 +1,24 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module GraphqlConnector
4
+ # The configuration template file for the gem.
2
5
  class Configuration
3
- attr_accessor :host, :headers
6
+ attr_reader :base_server_types
4
7
 
5
8
  def initialize
6
- @host = nil
7
- @headers = nil
9
+ @base_server_types = {}
10
+ end
11
+
12
+ def add_server(name:, uri:, headers:)
13
+ @base_server_types[name] = BaseServerType.build(name, uri, headers)
14
+ end
15
+
16
+ def reset!
17
+ @base_server_types.keys.each do |name|
18
+ GraphqlConnector.const_get(name).send :remove_const, 'Query'
19
+ GraphqlConnector.send :remove_const, name
20
+ end
21
+ @base_server_types = {}
8
22
  end
9
23
  end
10
24
  end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CustomAttributeError < StandardError; end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphqlConnector
4
+ # Wrapper class for HTTParty post query
5
+ class HttpClient
6
+ def initialize(uri, headers)
7
+ @uri = uri
8
+ @headers = headers
9
+ end
10
+
11
+ def query(model, conditions, selected_fields)
12
+ query_string = GraphqlConnector::QueryBuilder.new(model,
13
+ conditions,
14
+ selected_fields).create
15
+ parsed_body = raw_query(query_string)
16
+ result = parsed_body['data'][model.to_s]
17
+ return OpenStruct.new(result) unless result.is_a? Array
18
+
19
+ result.map { |entry| OpenStruct.new(entry) }
20
+ end
21
+
22
+ def raw_query(query_string, variables: {})
23
+ response = HTTParty.post(@uri,
24
+ headers: @headers,
25
+ body: { query: query_string,
26
+ variables: variables })
27
+ parsed_body = JSON.parse(response.body)
28
+ verify_response!(parsed_body)
29
+ parsed_body
30
+ end
31
+
32
+ private
33
+
34
+ def verify_response!(parsed_body)
35
+ return unless parsed_body.key? 'errors'
36
+
37
+ raise CustomAttributeError, parsed_body['errors']
38
+ end
39
+ end
40
+ end
@@ -1,4 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module GraphqlConnector
4
+ # create the graphql query_string out of the given attributes.
2
5
  class QueryBuilder
3
6
  def initialize(model, conditions, selected_fields)
4
7
  @model = model
@@ -7,31 +10,52 @@ module GraphqlConnector
7
10
  end
8
11
 
9
12
  def create
10
- "query { #{@model}(#{main_filter}) { #{@selected_fields.join(' ')} } }"
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(', ')})"
11
28
  end
12
29
 
13
- private
14
-
15
- def main_filter
16
- conditions = @conditions.each_with_object([]) do |(key, value), array|
17
- next if value.is_a? Hash # will be processed in #field_with_filter
18
-
19
- array << "#{key}: #{value_as_parameter(value)}"
20
- end
21
-
22
- conditions.join(', ')
23
- end
24
-
25
- def value_as_parameter(value)
26
- case value
27
- when TrueClass, FalseClass, Integer, Float
28
- value
29
- when Array
30
- casted_values = value.map { |v| value_as_parameter(v) }
31
- "[#{casted_values.join(',')}]"
32
- else # fallback to string
33
- '"' + value.to_s + '"'
34
- end
35
- end
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
36
60
  end
37
61
  end
@@ -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 (raw_)add_query '#{class_method_name}: ... ' is "\
17
+ 'already implemented within the context of '\
18
+ "#{invoked_class} and therefore cannot be used!"
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,76 @@
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
+ private
42
+
43
+ def query_method(class_method_name, query_type, return_fields)
44
+ define_singleton_method class_method_name do
45
+ http_client.query(query_type, {}, return_fields.to_a)
46
+ end
47
+ end
48
+
49
+ def raw_query_method(class_method_name, query_string)
50
+ define_singleton_method class_method_name do
51
+ http_client.raw_query(query_string)
52
+ end
53
+ end
54
+
55
+ def query_keyword_method(name, query_type, keywords, return_fields)
56
+ keywords = [keywords].flatten
57
+ instance_eval <<-METHOD, __FILE__, __LINE__ + 1
58
+ def #{name}(#{keywords.map { |keyword| "#{keyword}:" }.join(', ')})
59
+ http_client.query("#{query_type}",
60
+ #{CONDITIONS},
61
+ #{return_fields.to_a})
62
+ end
63
+ METHOD
64
+ end
65
+
66
+ def raw_query_keyword_method(name, query_string, keywords)
67
+ keywords = [keywords].flatten
68
+ instance_eval <<-METHOD, __FILE__, __LINE__ + 1
69
+ def #{name}(#{keywords.map { |keyword| "#{keyword}:" }.join(', ')})
70
+ http_client.raw_query("#{query_string}", variables: #{CONDITIONS})
71
+ end
72
+ METHOD
73
+ end
74
+ end
75
+ end
76
+ 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 = '0.1.0.beta'
4
+ VERSION = '1.1.1.1'
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: 0.1.0.beta
4
+ version: 1.1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Garllon
8
+ - sushie1984
8
9
  autorequire:
9
10
  bindir: exe
10
11
  cert_chain: []
11
- date: 2019-10-03 00:00:00.000000000 Z
12
+ date: 2020-11-09 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: httparty
@@ -39,47 +40,33 @@ dependencies:
39
40
  - !ruby/object:Gem::Version
40
41
  version: '2.0'
41
42
  - !ruby/object:Gem::Dependency
42
- name: rspec
43
+ name: pry
43
44
  requirement: !ruby/object:Gem::Requirement
44
45
  requirements:
45
46
  - - "~>"
46
47
  - !ruby/object:Gem::Version
47
- version: '3.8'
48
+ version: '0.10'
48
49
  type: :development
49
50
  prerelease: false
50
51
  version_requirements: !ruby/object:Gem::Requirement
51
52
  requirements:
52
53
  - - "~>"
53
54
  - !ruby/object:Gem::Version
54
- version: '3.8'
55
+ version: '0.10'
55
56
  - !ruby/object:Gem::Dependency
56
- name: rake
57
+ name: rspec
57
58
  requirement: !ruby/object:Gem::Requirement
58
59
  requirements:
59
60
  - - "~>"
60
61
  - !ruby/object:Gem::Version
61
- version: '10.0'
62
+ version: '3.8'
62
63
  type: :development
63
64
  prerelease: false
64
65
  version_requirements: !ruby/object:Gem::Requirement
65
66
  requirements:
66
67
  - - "~>"
67
68
  - !ruby/object:Gem::Version
68
- version: '10.0'
69
- - !ruby/object:Gem::Dependency
70
- name: pry
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: '0'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: '0'
69
+ version: '3.8'
83
70
  - !ruby/object:Gem::Dependency
84
71
  name: rubocop
85
72
  requirement: !ruby/object:Gem::Requirement
@@ -94,16 +81,20 @@ dependencies:
94
81
  - - "~>"
95
82
  - !ruby/object:Gem::Version
96
83
  version: '0.75'
97
- description: Hallo
84
+ description: Grahql client to query with your own raw string, with the small helper
85
+ method query or with service class inclusion.
98
86
  email:
99
87
  - palluthe.bennet@gmail.com
88
+ - sascha_burku@yahoo.de
100
89
  executables: []
101
90
  extensions: []
102
91
  extra_rdoc_files: []
103
92
  files:
93
+ - ".github/workflows/ci.yaml"
104
94
  - ".gitignore"
105
95
  - ".rspec"
106
- - ".travis.yml"
96
+ - ".rubocop.yml"
97
+ - ".rubocop_todo.yml"
107
98
  - CHANGELOG.md
108
99
  - CODE_OF_CONDUCT.md
109
100
  - Gemfile
@@ -111,10 +102,20 @@ files:
111
102
  - LICENSE.txt
112
103
  - README.md
113
104
  - bin/console
105
+ - examples/departments_service_class_examples.rb
106
+ - examples/query_examples.rb
107
+ - examples/raw_query_examples.rb
114
108
  - graphql_connector.gemspec
115
109
  - lib/graphql_connector.rb
110
+ - lib/graphql_connector/base_server_type.rb
116
111
  - lib/graphql_connector/configuration.rb
112
+ - lib/graphql_connector/custom_attribute_error.rb
113
+ - lib/graphql_connector/http_client.rb
117
114
  - lib/graphql_connector/query_builder.rb
115
+ - lib/graphql_connector/service_classable/class_method_validator.rb
116
+ - lib/graphql_connector/service_classable/params_validator.rb
117
+ - lib/graphql_connector/service_classable/queryable.rb
118
+ - lib/graphql_connector/service_classable/return_fields_validator.rb
118
119
  - lib/graphql_connector/version.rb
119
120
  homepage: https://github.com/Garllon/graphql_connector/blob/master/README.md
120
121
  licenses:
@@ -134,12 +135,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
134
135
  version: '0'
135
136
  required_rubygems_version: !ruby/object:Gem::Requirement
136
137
  requirements:
137
- - - ">"
138
+ - - ">="
138
139
  - !ruby/object:Gem::Version
139
- version: 1.3.1
140
+ version: '0'
140
141
  requirements: []
141
- rubygems_version: 3.0.6
142
+ rubygems_version: 3.0.4
142
143
  signing_key:
143
144
  specification_version: 4
144
- summary: Hallo
145
+ summary: GraphQL client
145
146
  test_files: []
@@ -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