graphql_connector 0.1.0.beta → 1.1.1.1

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: 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