docbase 3.0.0 → 3.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 04de1814e9cdba377e46783519ead41b3471972cd19d4e32c058eb952ad9261d
4
- data.tar.gz: 30f7944856b0157e89cc178b72f2d437a2ae1b1a6352b6437840566d461115fa
3
+ metadata.gz: db7bc6f5163f765c5a16ec85c3a190c894e87b484acac0901458f44b81ca0355
4
+ data.tar.gz: 0eaace24913ca86fa825a2da026da6f9aff7e3a5cad1af5df1f505a91ec39d6e
5
5
  SHA512:
6
- metadata.gz: 63c7b0575d1b5ca821ae2cf0c6cde0ba94514321ce061f7f8f4c1e8a63e6ab8040e2d219a857d87b5b71e375e539faf16e427f900fa5265b2fcda755bc901644
7
- data.tar.gz: af1764af0574365f63fdc14e9a206fb983632cb17c705cb442f68f9228c92b87a4c4c8af2fa66bf31200c77b0b02a408c85fa023445d62c0cfe9c4565d61dc2c
6
+ metadata.gz: 84a27324711539c8ffaea3365cbb1b75b8b2d6041d9f385b4994099657e183d4d42929c873701b4fed2e682c4164e240a03e5417f89c5897d20c2be3efd90fb4
7
+ data.tar.gz: 7a715b93cad6b2167bb98ca766698924e87ad63aa3034afe977fc60a97c07130cbb10c6f259e4a80c4fcad674abdea15b6a422faffa157378af5305d2e40c9f0
data/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## 3.1.0
2
+
3
+ * faradayを2.14.4以上3未満に変更し、faraday_middlewareへの依存を削除(faraday 2.14.3以前では使えなくなります)
4
+ * `access_token`を差し替えても古いトークンで送信される不具合を修正
5
+ * IDやチーム名が不正なときはURLに埋め込まず`ArgumentError`にするように
6
+ * 利用制限時のリトライに上限を追加(3回まで、1回の待ちは60秒まで)。有効な`X-RateLimit-Reset`がない429はリトライしない
7
+
1
8
  ## 3.0.0
2
9
 
3
10
  * DocBase API v3対応
data/README.md CHANGED
@@ -35,6 +35,7 @@ client = DocBase::Client.new(access_token: 'your_access_token', team: 'your_team
35
35
  * retry_on_rate_limit_exceeded
36
36
  * [#16](https://github.com/krayinc/docbase-ruby/issues/16)
37
37
  * true or false
38
+ * When true, a 429 response is retried after waiting until `X-RateLimit-Reset`, up to 3 times and at most 60 seconds per wait. A 429 response without a valid `X-RateLimit-Reset` is returned without retrying.
38
39
 
39
40
  ### profile
40
41
 
data/docbase.gemspec CHANGED
@@ -18,6 +18,5 @@ Gem::Specification.new do |spec|
18
18
  spec.require_paths = ['lib']
19
19
 
20
20
  spec.add_dependency 'base64'
21
- spec.add_dependency 'faraday', '>= 0.15.2'
22
- spec.add_dependency 'faraday_middleware', '>= 0.12.2'
21
+ spec.add_dependency 'faraday', '>= 2.14.4', '< 3'
23
22
  end
@@ -1,7 +1,11 @@
1
1
  module DocBase
2
2
  class Client
3
3
  DEFAULT_URL = 'https://api.docbase.io'
4
+ TEAM_FORMAT = /\A[a-z0-9]+(-[a-z0-9]+)*\z/
5
+ ID_FORMAT = /\A[1-9][0-9]*\z/
4
6
  USER_AGENT = "DocBase Ruby Gem #{DocBase::VERSION}"
7
+ MAX_RATE_LIMIT_RETRIES = 3
8
+ MAX_RATE_LIMIT_WAIT_SECONDS = 60
5
9
 
6
10
  class NotSetTeamError < StandardError; end
7
11
  class NotSetPostIdError < StandardError; end
@@ -18,6 +22,8 @@ module DocBase
18
22
 
19
23
  def team!
20
24
  raise NotSetTeamError unless @team
25
+ raise ArgumentError, "invalid team: #{@team.inspect}" unless TEAM_FORMAT.match?(@team.to_s)
26
+
21
27
  @team
22
28
  end
23
29
 
@@ -36,13 +42,13 @@ module DocBase
36
42
  def user_groups(user_id, page: 1, per_page: 20)
37
43
  request(
38
44
  method: :get,
39
- path: "/teams/#{team!}/users/#{user_id}/groups",
45
+ path: "/teams/#{team!}/users/#{id_segment(user_id)}/groups",
40
46
  params: { page: page, per_page: per_page }
41
47
  )
42
48
  end
43
49
 
44
50
  def delete_user(id)
45
- request(method: :delete, path: "/teams/#{team!}/users/#{id}")
51
+ request(method: :delete, path: "/teams/#{team!}/users/#{id_segment(id)}")
46
52
  end
47
53
 
48
54
  def tags
@@ -54,7 +60,7 @@ module DocBase
54
60
  end
55
61
 
56
62
  def group(id)
57
- request(method: :get, path: "/teams/#{team!}/groups/#{id}")
63
+ request(method: :get, path: "/teams/#{team!}/groups/#{id_segment(id)}")
58
64
  end
59
65
 
60
66
  def create_group(params)
@@ -78,7 +84,7 @@ module DocBase
78
84
  end
79
85
 
80
86
  def post(id)
81
- request(method: :get, path: "/teams/#{team!}/posts/#{id}")
87
+ request(method: :get, path: "/teams/#{team!}/posts/#{id_segment(id)}")
82
88
  end
83
89
 
84
90
  def posts(q: '*', page: 1, per_page: 20)
@@ -106,21 +112,21 @@ module DocBase
106
112
  end
107
113
 
108
114
  def delete_post(id)
109
- request(method: :delete, path: "/teams/#{team!}/posts/#{id}")
115
+ request(method: :delete, path: "/teams/#{team!}/posts/#{id_segment(id)}")
110
116
  end
111
117
 
112
118
  def archive_post(id)
113
- request(method: :put, path: "/teams/#{team!}/posts/#{id}/archive")
119
+ request(method: :put, path: "/teams/#{team!}/posts/#{id_segment(id)}/archive")
114
120
  end
115
121
 
116
122
  def unarchive_post(id)
117
- request(method: :put, path: "/teams/#{team!}/posts/#{id}/unarchive")
123
+ request(method: :put, path: "/teams/#{team!}/posts/#{id_segment(id)}/unarchive")
118
124
  end
119
125
 
120
126
  def comments(post_id, page: 1, per_page: 20, order: 'asc', created_after: nil, created_before: nil)
121
127
  request(
122
128
  method: :get,
123
- path: "/teams/#{team!}/posts/#{post_id}/comments",
129
+ path: "/teams/#{team!}/posts/#{id_segment(post_id)}/comments",
124
130
  params: {
125
131
  page: page,
126
132
  per_page: per_page,
@@ -140,13 +146,13 @@ module DocBase
140
146
  end
141
147
 
142
148
  def delete_comment(id)
143
- request(method: :delete, path: "/teams/#{team!}/comments/#{id}")
149
+ request(method: :delete, path: "/teams/#{team!}/comments/#{id_segment(id)}")
144
150
  end
145
151
 
146
152
  def good_jobs(post_id, page: 1, per_page: 100, order: 'asc', created_after: nil, created_before: nil)
147
153
  request(
148
154
  method: :get,
149
- path: "/teams/#{team!}/posts/#{post_id}/good_jobs",
155
+ path: "/teams/#{team!}/posts/#{id_segment(post_id)}/good_jobs",
150
156
  params: {
151
157
  page: page,
152
158
  per_page: per_page,
@@ -158,11 +164,11 @@ module DocBase
158
164
  end
159
165
 
160
166
  def create_good_job(post_id, params = {})
161
- request(method: :post, path: "/teams/#{team!}/posts/#{post_id}/good_jobs", params: params)
167
+ request(method: :post, path: "/teams/#{team!}/posts/#{id_segment(post_id)}/good_jobs", params: params)
162
168
  end
163
169
 
164
170
  def delete_good_job(post_id, good_job_id)
165
- request(method: :delete, path: "/teams/#{team!}/posts/#{post_id}/good_jobs/#{good_job_id}")
171
+ request(method: :delete, path: "/teams/#{team!}/posts/#{id_segment(post_id)}/good_jobs/#{id_segment(good_job_id)}")
166
172
  end
167
173
 
168
174
  def upload(paths)
@@ -180,11 +186,24 @@ module DocBase
180
186
  end
181
187
 
182
188
  def attachment(id)
183
- request(method: :get, path: "/teams/#{team!}/attachments/#{id}", for_binary: true)
189
+ request(method: :get, path: "/teams/#{team!}/attachments/#{path_segment(id)}", for_binary: true)
184
190
  end
185
191
 
186
192
  private
187
193
 
194
+ def id_segment(id)
195
+ raise ArgumentError, "invalid id: #{id.inspect}" unless ID_FORMAT.match?(id.to_s)
196
+
197
+ id.to_s
198
+ end
199
+
200
+ def path_segment(value)
201
+ value = value.to_s
202
+ raise ArgumentError, "invalid id: #{value.inspect}" if value.empty? || value == '.' || value == '..'
203
+
204
+ URI.encode_www_form_component(value).gsub('+', '%20')
205
+ end
206
+
188
207
  def except(hash, reject_key)
189
208
  hash.reject { |key, _| key == reject_key }
190
209
  end
@@ -206,28 +225,35 @@ module DocBase
206
225
 
207
226
  def headers
208
227
  {
209
- 'Accept' => 'application/json',
210
- 'User-Agent' => USER_AGENT,
211
- 'X-DocBaseToken' => access_token,
228
+ 'Accept' => 'application/json',
229
+ 'User-Agent' => USER_AGENT,
212
230
  }
213
231
  end
214
232
 
215
233
  def request(method:, path:, params: nil, for_binary: false)
216
- conn = for_binary ? connection_for_binary : connection
217
- response = conn.send(method, path, params)
218
- raise TooManyRequestError if retry_on_rate_limit_exceeded && response.status == 429
219
- response
220
- rescue TooManyRequestError
221
- reset_time = response.headers['x-ratelimit-reset'].to_i
222
- puts "DocBase API Rate limit exceeded: will retry at #{Time.at(reset_time).strftime("%Y/%m/%d %H:%M:%S")}."
223
- wait_for(reset_time)
224
- retry
234
+ retries = 0
235
+ loop do
236
+ conn = for_binary ? connection_for_binary : connection
237
+ response = conn.send(method, path, params, 'X-DocBaseToken' => access_token)
238
+ return response unless retry_on_rate_limit_exceeded && response.status == 429
239
+
240
+ reset_time = rate_limit_reset_time(response)
241
+ return response if reset_time.nil? || retries >= MAX_RATE_LIMIT_RETRIES
242
+
243
+ retries += 1
244
+ wait_for(reset_time)
245
+ end
246
+ end
247
+
248
+ def rate_limit_reset_time(response)
249
+ reset_time = Integer(response.headers['x-ratelimit-reset'].to_s, exception: false)
250
+ reset_time if reset_time && reset_time > 0
225
251
  end
226
252
 
227
253
  def wait_for(reset_time)
228
- wait_time = reset_time - Time.now.to_i
229
- return if wait_time <= 0
230
- sleep wait_time
254
+ wait_time = (reset_time - Time.now.to_i).clamp(0, MAX_RATE_LIMIT_WAIT_SECONDS)
255
+ puts "DocBase API Rate limit exceeded: will retry at #{(Time.now + wait_time).strftime("%Y/%m/%d %H:%M:%S")}."
256
+ sleep wait_time if wait_time > 0
231
257
  end
232
258
  end
233
259
  end
@@ -1,3 +1,3 @@
1
1
  module DocBase
2
- VERSION = '3.0.0'
2
+ VERSION = '3.1.0'
3
3
  end
data/lib/docbase.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  require 'base64'
2
2
  require 'faraday'
3
- require 'faraday_middleware'
4
3
  require 'docbase/version'
5
4
  require 'docbase/client'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: docbase
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0
4
+ version: 3.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ttakuru88
@@ -29,28 +29,20 @@ dependencies:
29
29
  requirements:
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
- version: 0.15.2
33
- type: :runtime
34
- prerelease: false
35
- version_requirements: !ruby/object:Gem::Requirement
36
- requirements:
37
- - - ">="
32
+ version: 2.14.4
33
+ - - "<"
38
34
  - !ruby/object:Gem::Version
39
- version: 0.15.2
40
- - !ruby/object:Gem::Dependency
41
- name: faraday_middleware
42
- requirement: !ruby/object:Gem::Requirement
43
- requirements:
44
- - - ">="
45
- - !ruby/object:Gem::Version
46
- version: 0.12.2
35
+ version: '3'
47
36
  type: :runtime
48
37
  prerelease: false
49
38
  version_requirements: !ruby/object:Gem::Requirement
50
39
  requirements:
51
40
  - - ">="
52
41
  - !ruby/object:Gem::Version
53
- version: 0.12.2
42
+ version: 2.14.4
43
+ - - "<"
44
+ - !ruby/object:Gem::Version
45
+ version: '3'
54
46
  description: DocBase API Client, written in Ruby
55
47
  email:
56
48
  - asaumi+docbase-ruby@kray.jp
@@ -91,7 +83,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
91
83
  - !ruby/object:Gem::Version
92
84
  version: '0'
93
85
  requirements: []
94
- rubygems_version: 4.0.10
86
+ rubygems_version: 4.0.20
95
87
  specification_version: 4
96
88
  summary: DocBase API Client, written in Ruby
97
89
  test_files: []