network-client 2.0.0 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +194 -6
- data/lib/network/client.rb +13 -9
- data/lib/network/version.rb +1 -1
- data/network-client.gemspec +3 -3
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8d744e4ef7f2447c1bcc600b3ce026b2de61fcfe
|
4
|
+
data.tar.gz: c31e62ada023d7b11efc60c709393943fb407141
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 458e8ff4353c3bf358aadfd25a1ac3ee6dba002b7067e3b373be44d6c33c608b72bb13e7e60d469534056113e7ddbbbdd216ff1424cc2988303cb25891417277
|
7
|
+
data.tar.gz: 474215650614bd145967951af5a3ffc5011c780225137e6e1dc49fb2c4175d595b9d92cf291ef8daa6982f631683fc93cf472ff0be69da8846e1dc6ad4f82ce1
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# Network Client
|
2
|
+
|
2
3
|
[![Gem Version](https://badge.fury.io/rb/network-client.svg)](https://rubygems.org/gems/network-client)
|
4
|
+
[![Gem](https://img.shields.io/gem/dt/network-client.svg?colorB=8b0000)](https://rubygems.org/gems/network-client)
|
3
5
|
[![Build Status](https://travis-ci.org/abarrak/network-client.svg?branch=master)](https://travis-ci.org/abarrak/network-client)
|
4
6
|
[![Dependency Status](https://gemnasium.com/badges/github.com/abarrak/network-client.svg)](https://gemnasium.com/github.com/abarrak/network-client)
|
5
7
|
[![Test Coverage](https://codeclimate.com/github/abarrak/network-client/badges/coverage.svg)](https://codeclimate.com/github/abarrak/network-client/coverage)
|
@@ -28,18 +30,204 @@ $ gem install network-client
|
|
28
30
|
|
29
31
|
## Usage
|
30
32
|
|
33
|
+
#### Making JSON requests
|
34
|
+
Given this client set up:
|
31
35
|
|
32
|
-
|
36
|
+
```ruby
|
37
|
+
require "network-client"
|
33
38
|
|
34
|
-
|
39
|
+
client = Network::Client.new(endpoint: 'https://jsonplaceholder.typicode.com')
|
40
|
+
```
|
41
|
+
|
42
|
+
We can perform the following requests:
|
43
|
+
|
44
|
+
* **GET**
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
client.get '/todos/10'
|
48
|
+
|
49
|
+
#=> #<struct Network::Client::Response code=200,
|
50
|
+
body={"userId"=>1, "id"=>10, "title"=>"illo est ...", "completed"=>true}>
|
51
|
+
```
|
52
|
+
|
53
|
+
* **POST**
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
client.post '/todos', params: { title: 'foo bar', completed: 'false', userId: 1 }.to_json
|
57
|
+
|
58
|
+
#=> #<struct Network::Client::Response code=201,
|
59
|
+
body={"title"=>"foo bar", "completed"=>false, "userId"=>1, "id"=>201}>
|
60
|
+
```
|
61
|
+
|
62
|
+
* **PATCH**
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
client.patch '/todos/10', params: { title: 'new title' }.to_json
|
66
|
+
|
67
|
+
#=> #<struct Network::Client::Response code=200,
|
68
|
+
body={"userId"=>1, "id"=>10, "title"=>"new title", "completed"=>true}>
|
69
|
+
```
|
70
|
+
|
71
|
+
* **PUT**
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
client.put '/todos/43', params: { completed: false }.to_json
|
75
|
+
|
76
|
+
#=> #<struct Network::Client::Response code=200, body={"completed"=>false, "id"=>43}>
|
77
|
+
```
|
78
|
+
|
79
|
+
* **DELETE**
|
80
|
+
|
81
|
+
```ruby
|
82
|
+
client.delete '/todos/25'
|
83
|
+
|
84
|
+
#=> #<struct Network::Client::Response code=200, body={}>
|
85
|
+
```
|
86
|
+
|
87
|
+
#### Returned Response
|
88
|
+
|
89
|
+
As appears in previous examples, the returned value of each successful request is a `Response` struct.
|
90
|
+
It holds the response's HTTP code and body parsed as JSON.
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
response = client.get '/posts/30'
|
94
|
+
response.code #=> 200
|
95
|
+
response.body #=> { "userId"=>3, "id"=>30, "title"=>"a quo magni similique perferendis",
|
96
|
+
"body"=>"alias dolor cumque ..." }
|
97
|
+
```
|
98
|
+
|
99
|
+
#### Setting Request Headers
|
100
|
+
Since this is mainly JSON web client, `Accept` and `Content-Type` headers are set to json by default.
|
101
|
+
|
102
|
+
You can override them and set extra headers during initialization by providing `headers:` argument:
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
headers = { 'X-SPECIAL-KEY' => '123456' }
|
106
|
+
client = Network::Client.new(endpoint: 'https://api.example.com', headers: headers)
|
107
|
+
```
|
108
|
+
|
109
|
+
Or on request basis with the `headers:` argument too:
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
client.get 'posts/', headers: { 'X-SPECIAL-KEY' => '123456' }
|
113
|
+
```
|
114
|
+
|
115
|
+
#### HTTP Authentication
|
116
|
+
|
117
|
+
1. **Basic:**
|
118
|
+
```ruby
|
119
|
+
# using `username` and `password` named parameters when initialized:
|
120
|
+
|
121
|
+
client = Network::Client.new(endpoint: 'https://api.example.com',
|
122
|
+
username: 'ABC',
|
123
|
+
password: '999')
|
124
|
+
client.username #=> "ABC"
|
125
|
+
client.password #=> "999"
|
126
|
+
|
127
|
+
# or via `#set_basic_auth`:
|
128
|
+
|
129
|
+
client.set_basic_auth('John Doe', '112233')
|
130
|
+
client.username #=> "John Doe"
|
131
|
+
client.password #=> "112233"
|
132
|
+
```
|
133
|
+
|
134
|
+
2. **OAuth Bearer:**
|
135
|
+
```ruby
|
136
|
+
client.set_bearer_auth(token: 'e08f7739c3abb78c')
|
137
|
+
client.bearer_token
|
138
|
+
#=> "e08f7739c3abb78c"
|
139
|
+
```
|
35
140
|
|
141
|
+
3. **Token Based:**
|
142
|
+
```ruby
|
143
|
+
client.set_token_auth(header_value: 'Token token=sec_key_aZcNRzoCMpmdMEP4OEeDUQ==')
|
144
|
+
client.auth_token_header
|
145
|
+
#=> "Token token=sec_key_aZcNRzoCMpmdMEP4OEeDUQ=="
|
146
|
+
```
|
147
|
+
|
148
|
+
#### Customizing User Agent
|
149
|
+
You can set the user agent header during initialization:
|
150
|
+
|
151
|
+
```ruby
|
152
|
+
client = Network::Client.new(endpoint: 'https://maps.googleapis.com', user_agent: 'App Service')
|
153
|
+
client.user_agent #=> "App Service"
|
154
|
+
```
|
155
|
+
|
156
|
+
Or later on via `#set_user_agent` method:
|
157
|
+
|
158
|
+
```ruby
|
159
|
+
client.set_user_agent('Gateway Server')
|
160
|
+
client.user_agent #=> "Gateway Server"
|
161
|
+
```
|
162
|
+
|
163
|
+
The default user agent is `Network Client`.
|
164
|
+
|
165
|
+
#### Retry and Error Handling
|
166
|
+
Set the `tries:` named argument to define the number of tries when request fails with one of the retryable errors.
|
167
|
+
|
168
|
+
```ruby
|
169
|
+
client = Network::Client.new(endpoint: 'https://api.foursquare.com', tries: 3)
|
170
|
+
client.tries #=> 3
|
171
|
+
```
|
172
|
+
|
173
|
+
The default `#tries` is 2.
|
174
|
+
|
175
|
+
To retrieve or extend the list of triable errors through `#errors_to_recover`:
|
176
|
+
|
177
|
+
```ruby
|
178
|
+
client.errors_to_recover
|
179
|
+
|
180
|
+
#=> [Net::HTTPTooManyRequests, Net::HTTPServerError, Net::ProtocolError,
|
181
|
+
Net::HTTPBadResponse,Net::ReadTimeout, Net::OpenTimeout, Errno::ECONNREFUSED,
|
182
|
+
Errno::ETIMEDOUT, OpenSSL::SSL::SSLError, SocketError]
|
183
|
+
|
184
|
+
client.errors_to_recover << Net::HTTPRequestTimeOut
|
185
|
+
|
186
|
+
#=> [Net::HTTPTooManyRequests, Net::HTTPServerError, Net::ProtocolError,
|
187
|
+
Net::HTTPBadResponse,Net::ReadTimeout, Net::OpenTimeout, Errno::ECONNREFUSED,
|
188
|
+
Errno::ETIMEDOUT, OpenSSL::SSL::SSLError, SocketError, Net::HTTPRequestTimeOut]
|
189
|
+
```
|
190
|
+
|
191
|
+
The list of `errors_to_propagate` takes precedence over `errors_to_recover`, and they are not retried.
|
192
|
+
|
193
|
+
You can retrieve them for rescue in your application layer, and extend them too.
|
194
|
+
|
195
|
+
```ruby
|
196
|
+
client.errors_to_propagate
|
197
|
+
#=> [Net::HTTPRequestURITooLong, Net::HTTPMethodNotAllowed]
|
198
|
+
|
199
|
+
client.errors_to_propagate << Net::HTTPNotAcceptable
|
200
|
+
#=> [Net::HTTPRequestURITooLong, Net::HTTPMethodNotAllowed, Net::HTTPNotAcceptable]
|
201
|
+
```
|
202
|
+
|
203
|
+
*Be careful not to add ancestor error class (higher in the inheritance chain) as it will prevent any of it's descendant classes from getting retried. Unless this is an intended behavior, of course.*
|
204
|
+
|
205
|
+
#### Logger
|
206
|
+
When `Rails` is in scope, it's logger will be used by default.
|
207
|
+
|
208
|
+
If not, then it defaults to a fallback logger that writes to `STDOUT`.
|
209
|
+
|
210
|
+
Additionally, you can override with your custom logger by supplying block to `#set_logger` like so:
|
211
|
+
|
212
|
+
```ruby
|
213
|
+
client = Network::Client.new(endpoint: 'https://api.foursquare.com')
|
214
|
+
|
215
|
+
client.set_logger { Logger.new(STDERR) }
|
216
|
+
client.logger
|
217
|
+
#=> #<Logger:0x007fb3cd136d38 @progname=nil, @level=0, @default_formatter=#<Logger::Formatter:0x007fb3cd136d10 @datetime_format=nil>, @formatter=nil, @logdev=#<Logger::LogDevice:0x007fb3cd136c98 @shift_size=nil, @shift_age=nil, @filename=nil, @dev=#<IO:<STDERR>>, @mon_owner=nil, @mon_count=0, @mon_mutex=#<Thread::Mutex:0x007fb3cd136c70>>>
|
218
|
+
```
|
219
|
+
|
220
|
+
## Documentation
|
221
|
+
For more details, please refer to [the API documentation](http://www.rubydoc.info/gems/network-client/2.0.1/Network/Client).
|
222
|
+
|
223
|
+
## Contributing
|
36
224
|
Bug reports and pull requests are very much appreciated at [Github](https://github.com/abarrak/network-client).
|
37
225
|
|
38
226
|
- Fork The repository.
|
39
|
-
- Create a branch with
|
40
|
-
- Make your changes (
|
41
|
-
- Push changes to the created branch
|
42
|
-
- Create an Pull Request
|
227
|
+
- Create a branch with the fix or feature name.
|
228
|
+
- Make your changes (with test or README changes/additions if applicable).
|
229
|
+
- Push changes to the created branch.
|
230
|
+
- Create an Pull Request.
|
43
231
|
- That's it!
|
44
232
|
|
45
233
|
|
data/lib/network/client.rb
CHANGED
@@ -1,12 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'net/http'
|
2
3
|
require 'openssl'
|
3
4
|
require 'json'
|
4
5
|
require 'logger'
|
5
6
|
|
6
7
|
module Network
|
8
|
+
# This class is simple +JSON+ client that is meant to be initialized with a single URI.
|
9
|
+
# Subsequent calls should target endpoints/paths of that URI.
|
7
10
|
class Client
|
8
|
-
DEFAULT_HEADERS = { '
|
9
|
-
|
11
|
+
DEFAULT_HEADERS = { 'Accept' => 'application/json', 'Content-Type' => 'application/json' }
|
12
|
+
|
13
|
+
##
|
10
14
|
# The success response template.
|
11
15
|
#
|
12
16
|
# Represents the return of rest-like methods holding two values:
|
@@ -14,7 +18,7 @@ module Network
|
|
14
18
|
Response = Struct.new(:code, :body)
|
15
19
|
|
16
20
|
# Stamp in front of each log written by client +@logger+.
|
17
|
-
LOG_TAG = '[NETWORK CLIENT]:'
|
21
|
+
LOG_TAG = '[NETWORK CLIENT]:'
|
18
22
|
|
19
23
|
attr_reader :username, :password, :default_headers, :logger, :tries, :user_agent,
|
20
24
|
:bearer_token, :auth_token_header
|
@@ -54,18 +58,18 @@ module Network
|
|
54
58
|
# ... }
|
55
59
|
#
|
56
60
|
def initialize(endpoint:, tries: 2, headers: {}, username: nil, password: nil,
|
57
|
-
user_agent: '
|
61
|
+
user_agent: 'Network Client')
|
58
62
|
@uri = URI.parse(endpoint)
|
59
63
|
@tries = tries
|
60
64
|
|
61
65
|
set_http_client
|
66
|
+
define_error_strategies
|
62
67
|
set_default_headers(headers)
|
63
68
|
set_basic_auth(username, password)
|
69
|
+
set_bearer_auth
|
70
|
+
set_token_auth
|
64
71
|
set_logger
|
65
|
-
define_error_strategies
|
66
72
|
set_user_agent(headers['User-Agent'] || user_agent)
|
67
|
-
set_bearer_auth
|
68
|
-
set_custom_token_auth
|
69
73
|
end
|
70
74
|
|
71
75
|
##
|
@@ -198,7 +202,7 @@ module Network
|
|
198
202
|
# == Returns:
|
199
203
|
# [@auth_token_header] +string+ the newly assigned +@auth_token_header+ value.
|
200
204
|
#
|
201
|
-
def
|
205
|
+
def set_token_auth(header_value: '')
|
202
206
|
@auth_token_header = header_value
|
203
207
|
end
|
204
208
|
|
@@ -327,7 +331,7 @@ module Network
|
|
327
331
|
|
328
332
|
def formulate_path(path)
|
329
333
|
path = '/' if path.nil? || path.empty?
|
330
|
-
path.strip
|
334
|
+
path = path.strip if path.respond_to?(:strip)
|
331
335
|
path.prepend('/') unless path.chars.first == '/'
|
332
336
|
path
|
333
337
|
end
|
data/lib/network/version.rb
CHANGED
data/network-client.gemspec
CHANGED
@@ -12,9 +12,9 @@ Gem::Specification.new do |spec|
|
|
12
12
|
spec.authors = ["Abdullah Barrak (abarrak)"]
|
13
13
|
spec.email = ["abdullah@abarrak.com"]
|
14
14
|
|
15
|
-
spec.summary = "A thin resilient
|
16
|
-
spec.description = "network-client
|
17
|
-
|
15
|
+
spec.summary = "A thin resilient JSON web client built around Net::HTTP"
|
16
|
+
spec.description = "network-client is a drop-in thin layer on top of Net::HTTP classes for \
|
17
|
+
JSON web requests with retry functionality and simple error handling"
|
18
18
|
spec.homepage = "https://github.com/abarrak/network-client"
|
19
19
|
spec.license = "MIT"
|
20
20
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: network-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Abdullah Barrak (abarrak)
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-07-
|
11
|
+
date: 2017-07-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -108,8 +108,8 @@ dependencies:
|
|
108
108
|
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '2.2'
|
111
|
-
description: network-client
|
112
|
-
|
111
|
+
description: network-client is a drop-in thin layer on top of Net::HTTP classes for JSON
|
112
|
+
web requests with retry functionality and simple error handling
|
113
113
|
email:
|
114
114
|
- abdullah@abarrak.com
|
115
115
|
executables: []
|
@@ -153,5 +153,5 @@ rubyforge_project:
|
|
153
153
|
rubygems_version: 2.6.11
|
154
154
|
signing_key:
|
155
155
|
specification_version: 4
|
156
|
-
summary: A thin resilient
|
156
|
+
summary: A thin resilient JSON web client built around Net::HTTP
|
157
157
|
test_files: []
|