my_api_client 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +1 -1
- data/Gemfile.lock +2 -2
- data/README.jp.md +61 -19
- data/lib/my_api_client.rb +2 -0
- data/lib/my_api_client/config.rb +19 -0
- data/lib/my_api_client/request.rb +4 -3
- data/lib/my_api_client/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d3ada9d2c8dbfadb9462b2cff381e9ccaf822a543f63672f6e5fdcd99f9ce4ed
|
4
|
+
data.tar.gz: b00cc646de8c05bc5378645940732e7dec5fc80d0dd4380e816f0a228e8b6f00
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 977d55b39c0a35d56d4ff2ddc142765a7807f2b027471186d947cda8ea4f606d81fbfc842524318c9630f48a88aeb5adea5cc7e948233323f3072200e624d034
|
7
|
+
data.tar.gz: e8d4f395990d049afddc3e380044e5f65ad57d0130da1b08114d4bf28965a0d53fe756d147ebf17c7ffac3b236fc8c3f9e7fc7c74edfb90e9f57b0ed94b10930
|
data/.rubocop_todo.yml
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# This configuration was generated by
|
2
2
|
# `rubocop --auto-gen-config`
|
3
|
-
# on 2019-05-
|
3
|
+
# on 2019-05-30 23:31:07 +0000 using RuboCop version 0.71.0.
|
4
4
|
# The point is for the user to remove these configuration records
|
5
5
|
# one by one as the offenses are removed from the code base.
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
my_api_client (0.
|
4
|
+
my_api_client (0.4.0)
|
5
5
|
activesupport (>= 4.2.0)
|
6
6
|
jsonpath
|
7
7
|
sawyer
|
@@ -69,7 +69,7 @@ GEM
|
|
69
69
|
rspec-support (3.8.0)
|
70
70
|
rspec_junit_formatter (0.4.1)
|
71
71
|
rspec-core (>= 2, < 4, != 2.12.0)
|
72
|
-
rubocop (0.
|
72
|
+
rubocop (0.71.0)
|
73
73
|
jaro_winkler (~> 1.5.1)
|
74
74
|
parallel (~> 1.10)
|
75
75
|
parser (>= 2.6)
|
data/README.jp.md
CHANGED
@@ -2,7 +2,9 @@
|
|
2
2
|
|
3
3
|
# MyApiClient
|
4
4
|
|
5
|
-
MyApiClient は API リクエストクラスを作成するための汎用的な機能を提供します。Sawyer や Faraday
|
5
|
+
MyApiClient は API リクエストクラスを作成するための汎用的な機能を提供します。Sawyer や Faraday をベースにエラーハンドリングの機能を強化した構造になっています。
|
6
|
+
|
7
|
+
ただし、 Sawyer はダミーデータの作成が難しかったり、他の gem で競合することがよくあるので、将来的には依存しないように変更していくかもしれません。
|
6
8
|
|
7
9
|
また、 Ruby on Rails で利用することを想定してますが、それ以外の環境でも動作するように作っているつもりです。不具合などあれば Issue ページからご報告下さい。
|
8
10
|
|
@@ -13,7 +15,7 @@ MyApiClient は API リクエストクラスを作成するための汎用的な
|
|
13
15
|
|
14
16
|
## Installation
|
15
17
|
|
16
|
-
この gem は macOS と Linux
|
18
|
+
この gem は macOS と Linux で作動します。まずは `my_api_client` を Gemfile に追加します:
|
17
19
|
|
18
20
|
```ruby
|
19
21
|
gem 'my_api_client'
|
@@ -38,7 +40,7 @@ create spec/api_clients/path/to/resource_api_client_spec.rb
|
|
38
40
|
|
39
41
|
```ruby
|
40
42
|
class ExampleApiClient < MyApiClient::Base
|
41
|
-
endpoint 'https://example.com'
|
43
|
+
endpoint 'https://example.com/v1'
|
42
44
|
|
43
45
|
attr_reader :access_token
|
44
46
|
|
@@ -46,14 +48,14 @@ class ExampleApiClient < MyApiClient::Base
|
|
46
48
|
@access_token = access_token
|
47
49
|
end
|
48
50
|
|
49
|
-
# GET https://example.com/users
|
51
|
+
# GET https://example.com/v1/users
|
50
52
|
#
|
51
53
|
# @return [Sawyer::Response] HTTP response parameter
|
52
54
|
def get_users
|
53
55
|
get 'users', headers: headers, query: { key: 'value' }
|
54
56
|
end
|
55
57
|
|
56
|
-
# POST https://example.com/users
|
58
|
+
# POST https://example.com/v1/users
|
57
59
|
#
|
58
60
|
# @param name [String] Username which want to create
|
59
61
|
# @return [Sawyer::Response] HTTP response parameter
|
@@ -75,8 +77,10 @@ api_clinet = ExampleApiClient.new(access_token: 'access_token')
|
|
75
77
|
api_clinet.get_users #=> #<Sawyer::Response>
|
76
78
|
```
|
77
79
|
|
78
|
-
クラス定義の最初に記述される `endpoint`
|
80
|
+
クラス定義の最初に記述される `endpoint` にはリクエスト URL の共通部分を定義します。後述の各メソッドで後続の path を定義しますが、上記の例だと `get 'users'` と定義すると、 `GET https://example.com/v1/users` というリクエストが実行されます。
|
81
|
+
|
79
82
|
次に、 `#initialize` を定義します。上記の例のように Access Token や API Key などを設定することを想定します。必要なければ定義の省略も可能です。
|
83
|
+
|
80
84
|
続いて、 `#get_users` や `#post_user` を定義します。メソッド名には API のタイトルを付けると良いと思います。メソッド内部で `#get` や `#post` を呼び出していますが、これがリクエスト時の HTTP Method になります。他にも `#patch` `#put` `#delete` が利用可能です。
|
81
85
|
|
82
86
|
### Error handling
|
@@ -121,7 +125,7 @@ error_handling status_code: 400..499, raise: MyApiClient::ClientError
|
|
121
125
|
|
122
126
|
https://github.com/ryz310/my_api_client/blob/master/lib/my_api_client/errors.rb
|
123
127
|
|
124
|
-
次に、 `raise` の代わりに
|
128
|
+
次に、 `raise` の代わりに `block` を指定する場合について。
|
125
129
|
|
126
130
|
```ruby
|
127
131
|
error_handling status_code: 500..599 do |params, logger|
|
@@ -130,7 +134,7 @@ error_handling status_code: 500..599 do |params, logger|
|
|
130
134
|
end
|
131
135
|
```
|
132
136
|
|
133
|
-
上記の例であれば、ステータスコードが `500..599` の場合に
|
137
|
+
上記の例であれば、ステータスコードが `500..599` の場合に `block` の内容が実行れます。引数の `params` にはリクエスト情報とレスポンス情報が含まれています。`logger` はログ出力用インスタンスですが、このインスタンスを使ってログ出力すると、以下のようにリクエスト情報がログ出力に含まれるようになり、デバッグの際に便利です。
|
134
138
|
|
135
139
|
```text
|
136
140
|
API request `GET https://example.com/path/to/resouce`: "Server error occurred."
|
@@ -155,7 +159,7 @@ error_handling json: { '$.errors.code': 10..19 }, with: :my_error_handling
|
|
155
159
|
}
|
156
160
|
```
|
157
161
|
|
158
|
-
`with` にはインスタンスメソッド名を指定することで、エラーを検出した際に任意のメソッドを実行させることができます。メソッドに渡される引数は
|
162
|
+
`with` にはインスタンスメソッド名を指定することで、エラーを検出した際に任意のメソッドを実行させることができます。メソッドに渡される引数は `block` 定義の場合と同じく `params` と `logger` です。
|
159
163
|
|
160
164
|
```ruby
|
161
165
|
# @param params [MyApiClient::Params::Params] HTTP req and res params
|
@@ -166,15 +170,59 @@ def my_error_handling(params, logger)
|
|
166
170
|
end
|
167
171
|
```
|
168
172
|
|
169
|
-
|
173
|
+
#### MyApiClient::Params::Params
|
170
174
|
|
171
175
|
WIP
|
172
176
|
|
177
|
+
#### MyApiClient::Error
|
178
|
+
|
179
|
+
WIP
|
180
|
+
|
181
|
+
### Retry
|
182
|
+
|
183
|
+
次に `MyApiClient` が提供するリトライ機能についてご紹介致します。
|
184
|
+
|
185
|
+
```ruby
|
186
|
+
class ExampleApiClient < MyApiClient::Base
|
187
|
+
endpoint 'https://example.com'
|
188
|
+
|
189
|
+
retry_on MyApiClient::NetworkError, wait: 0.1.seconds, attempts: 3
|
190
|
+
retry_on MyApiClient::ApiLimitError, wait: 30.seconds, attempts: 3
|
191
|
+
|
192
|
+
error_handling json: { '$.errors.code': 20 }, raise: MyApiClient::ApiLimitError
|
193
|
+
end
|
194
|
+
```
|
195
|
+
|
196
|
+
API リクエストを何度も実行していると回線の不調などによりネットワークエラーが発生する事があります。長時間ネットワークが使えなくなるケースもありますが、瞬間的なエラーであるケースも多々あります。 `MyApiClient` ではネットワーク系の例外はまとめて `MyApiClient::NetworkError` として `raise` されます。この例外の詳細は後述しますが、 `retry_on` を利用する事で、 `ActiveJob` のように任意の例外処理を補足して、一定回数、一定の期間を空けて API リクエストをリトライさせる事ができます。
|
197
|
+
|
198
|
+
ただし、 `ActiveJob` とは異なり同期処理でリトライするため、ネットワークの瞬断に備えたリトライ以外ではあまり使う機会はないのではないかと思います。上記の例のように API Limit に備えてリトライするケースもあるかと思いますが、こちらは `ActiveJob` で対応した方が良いと思います。
|
199
|
+
|
200
|
+
ちなみに一応 `discard_on` も実装していますが、作者自身が有効な用途を見出せていないので、詳細は割愛します。良い利用方法があれば教えてください。
|
201
|
+
|
173
202
|
#### MyApiClient::NetworkError
|
174
203
|
|
204
|
+
前述の通りですが、 `MyApiClient` ではネットワーク系の例外はまとめて `MyApiClient::NetworkError` として `raise` されます。他の例外と同じく `MyApiClient::Error` を親クラスとしています。 `MyApiClient::NetworkError` として扱われる例外クラスの一覧は `MyApiClient::NETWORK_ERRORS` で参照できます。また、元となった例外は `#original_error` で参照できます。
|
205
|
+
|
206
|
+
```ruby
|
207
|
+
begin
|
208
|
+
api_client.request
|
209
|
+
rescue MyApiClient::NetworkError => e
|
210
|
+
e.original_error # => #<Net::OpenTimeout>
|
211
|
+
e.params.response # => nil
|
212
|
+
end
|
213
|
+
```
|
214
|
+
|
215
|
+
なお、通常の例外はリクエストの結果によって発生しますが、この例外はリクエスト中に発生するため、例外インスタンスにレスポンスパラメータは含まれません。
|
216
|
+
|
217
|
+
### Timeout
|
218
|
+
|
175
219
|
WIP
|
176
220
|
|
177
|
-
###
|
221
|
+
### Logger
|
222
|
+
|
223
|
+
WIP
|
224
|
+
|
225
|
+
## One request for one class
|
178
226
|
|
179
227
|
多くの場合、同一ホストの API は リクエストヘッダーやエラー情報が同じ構造になっているため、上記のように一つのクラス内に複数の API を定義する設計が理にかなっていますが、 API 毎に個別に定義したい場合は、以下のように 1 つのクラスに 1 の API という構造で設計することも可能です。
|
180
228
|
|
@@ -224,13 +272,7 @@ class PostUserApiClient < ExampleApiClient
|
|
224
272
|
end
|
225
273
|
```
|
226
274
|
|
227
|
-
|
228
|
-
|
229
|
-
WIP
|
230
|
-
|
231
|
-
### Logger
|
232
|
-
|
233
|
-
WIP
|
275
|
+
## Testing
|
234
276
|
|
235
277
|
### RSpec
|
236
278
|
|
@@ -262,7 +304,7 @@ response = ExampleApiClient.new.request(user_id: 1)
|
|
262
304
|
response.id # => 12345
|
263
305
|
```
|
264
306
|
|
265
|
-
リクスエストパラメータを使ったレスポンスを返すようにスタブ化したい場合は、 block を利用することで実現できます。
|
307
|
+
リクスエストパラメータを使ったレスポンスを返すようにスタブ化したい場合は、 `block` を利用することで実現できます。
|
266
308
|
|
267
309
|
```ruby
|
268
310
|
my_api_client_stub(ExampleApiClient, :request) do |params|
|
data/lib/my_api_client.rb
CHANGED
data/lib/my_api_client/config.rb
CHANGED
@@ -16,5 +16,24 @@ module MyApiClient
|
|
16
16
|
METHOD
|
17
17
|
end
|
18
18
|
end
|
19
|
+
|
20
|
+
# Extracts schema and hostname from endpoint
|
21
|
+
#
|
22
|
+
# @example Extracts schema and hostname from 'https://example.com/path/to/api'
|
23
|
+
# schema_and_hostname # => 'https://example.com'
|
24
|
+
# @return [String] description_of_returned_object
|
25
|
+
def schema_and_hostname
|
26
|
+
uri = URI.parse(endpoint)
|
27
|
+
"#{uri.scheme}://#{uri.host}"
|
28
|
+
end
|
29
|
+
|
30
|
+
# Extracts pathname from endpoint
|
31
|
+
#
|
32
|
+
# @example Extracts pathname from 'https://example.com/path/to/api'
|
33
|
+
# common_path # => 'path/to/api'
|
34
|
+
# @return [String] The pathanem
|
35
|
+
def common_path
|
36
|
+
URI.parse(endpoint).path
|
37
|
+
end
|
19
38
|
end
|
20
39
|
end
|
@@ -14,8 +14,9 @@ module MyApiClient
|
|
14
14
|
# @return [Sawyer::Resource] description_of_returned_object
|
15
15
|
# rubocop:disable Metrics/ParameterLists
|
16
16
|
def _request(http_method, pathname, headers, query, body, logger)
|
17
|
-
|
18
|
-
|
17
|
+
processed_path = [common_path, pathname].join('/').gsub('//', '/')
|
18
|
+
request_params = Params::Request.new(http_method, processed_path, headers, query, body)
|
19
|
+
request_logger = Logger.new(logger, faraday, http_method, processed_path)
|
19
20
|
call(:_execute, request_params, request_logger)
|
20
21
|
end
|
21
22
|
# rubocop:enable Metrics/ParameterLists
|
@@ -26,7 +27,7 @@ module MyApiClient
|
|
26
27
|
#
|
27
28
|
# @return [Sawyer::Agent] description_of_returned_object
|
28
29
|
def agent
|
29
|
-
@agent ||= Sawyer::Agent.new(
|
30
|
+
@agent ||= Sawyer::Agent.new(schema_and_hostname, faraday: faraday)
|
30
31
|
end
|
31
32
|
|
32
33
|
# Description of #faraday
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: my_api_client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ryz310
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-06-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|