yt 0.5.12 → 0.5.13
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/HISTORY.md +1 -0
- data/README.md +1 -1
- data/lib/yt/collections/earnings.rb +1 -21
- data/lib/yt/models/request.rb +40 -13
- data/lib/yt/version.rb +1 -1
- data/spec/associations/device_auth/partnered_channels_spec.rb +2 -1
- data/spec/collections/earnings_spec.rb +0 -40
- data/spec/models/request_spec.rb +62 -19
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bc79b5682be98448f4c2ad0e47e025ece396f2ca
|
4
|
+
data.tar.gz: 3b3ba9a7646c1fe350c10bf1cc01ffc9e3c01062
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e8be6da305362d7f2578157ba5cdd0d6c717bbbd8f5a677ac898af79332fc5bb098b0f777006fc7d002da696346403910d724c673e308d897b1c700fa83e295e
|
7
|
+
data.tar.gz: 90842585d61ad708d4dba3fefac8b7bc2f51ac8eeae58ca3acdd5d989bf4eae495ba9a6b984b758f4e7bc5f2b20a5d8f14604c6e821ecb935745af67fb5363c8
|
data/Gemfile.lock
CHANGED
data/HISTORY.md
CHANGED
@@ -19,6 +19,7 @@ v0.5 - 2014/05/16
|
|
19
19
|
* New ContentOwner subclass of Account with access to partnered channels
|
20
20
|
* Automatically refresh the access token when it expires or becomes invalid
|
21
21
|
* Retry once YouTube earning queries that return error 503
|
22
|
+
* Wait 3 seconds and retry *every* request that returns 500, 503 or 400 with "Invalid query"
|
22
23
|
|
23
24
|
v0.4 - 2014/05/09
|
24
25
|
--------------------
|
data/README.md
CHANGED
@@ -365,7 +365,7 @@ To install on your system, run
|
|
365
365
|
|
366
366
|
To use inside a bundled Ruby project, add this line to the Gemfile:
|
367
367
|
|
368
|
-
gem 'yt', '~> 0.5.
|
368
|
+
gem 'yt', '~> 0.5.13'
|
369
369
|
|
370
370
|
Since the gem follows [Semantic Versioning](http://semver.org),
|
371
371
|
indicating the full version in your Gemfile (~> *major*.*minor*.*patch*)
|
@@ -4,17 +4,9 @@ module Yt
|
|
4
4
|
module Collections
|
5
5
|
class Earnings < Base
|
6
6
|
|
7
|
-
def within(days_range
|
7
|
+
def within(days_range)
|
8
8
|
@days_range = days_range
|
9
9
|
Hash[*flat_map{|daily_earning| daily_earning}]
|
10
|
-
# NOTE: Once in a while, YouTube responds with 400 Error and the message
|
11
|
-
# "Invalid query. Query did not conform to the expectations."; in this
|
12
|
-
# case running the same query after one second fixes the issue. This is
|
13
|
-
# not documented by YouTube and hardly testable, but trying again the
|
14
|
-
# same query is a workaround that works and can hardly cause any damage.
|
15
|
-
# Similarly, once in while YouTube responds with a random 503 error.
|
16
|
-
rescue Yt::Error => e
|
17
|
-
try_again && rescue?(e) ? sleep(3) && within(days_range, false) : raise
|
18
10
|
end
|
19
11
|
|
20
12
|
private
|
@@ -41,18 +33,6 @@ module Yt
|
|
41
33
|
def items_key
|
42
34
|
'rows'
|
43
35
|
end
|
44
|
-
|
45
|
-
def rescue?(error)
|
46
|
-
bad_request?(error) || backend_error?(error)
|
47
|
-
end
|
48
|
-
|
49
|
-
def bad_request?(error)
|
50
|
-
'badRequest'.in?(error.reasons) && error.message =~ /did not conform/
|
51
|
-
end
|
52
|
-
|
53
|
-
def backend_error?(error)
|
54
|
-
'backendError'.in?(error.reasons)
|
55
|
-
end
|
56
36
|
end
|
57
37
|
end
|
58
38
|
end
|
data/lib/yt/models/request.rb
CHANGED
@@ -28,25 +28,13 @@ module Yt
|
|
28
28
|
def run
|
29
29
|
if response.is_a? @expected_response
|
30
30
|
response.tap{|response| response.body = parse_format response.body}
|
31
|
-
elsif run_again_with_refreshed_authentication?
|
32
|
-
run
|
33
31
|
else
|
34
|
-
raise
|
32
|
+
run_again? ? run : raise(error_for(response), request_error_message)
|
35
33
|
end
|
36
34
|
end
|
37
35
|
|
38
36
|
private
|
39
37
|
|
40
|
-
# If a request authorized with an access token returns 401, then the
|
41
|
-
# access token might have expired. If a refresh token is also present,
|
42
|
-
# try to run the request one more time with a refreshed access token.
|
43
|
-
def run_again_with_refreshed_authentication?
|
44
|
-
if response.is_a? Net::HTTPUnauthorized
|
45
|
-
@response = @http_request = @uri = nil
|
46
|
-
@auth.refresh
|
47
|
-
end if @auth.respond_to? :refresh
|
48
|
-
end
|
49
|
-
|
50
38
|
def response
|
51
39
|
@response ||= Net::HTTP.start(*net_http_options) do |http|
|
52
40
|
http.request http_request
|
@@ -113,6 +101,45 @@ module Yt
|
|
113
101
|
end if body
|
114
102
|
end
|
115
103
|
|
104
|
+
# There are two cases to run a request again: YouTube responds with a
|
105
|
+
# random error that can be fixed by waiting for some seconds and running
|
106
|
+
# the exact same query, or the access token needs to be refreshed.
|
107
|
+
def run_again?
|
108
|
+
run_again_with_refreshed_authentication? || run_again_after_a_while?
|
109
|
+
end
|
110
|
+
|
111
|
+
# Once in a while, YouTube responds with 500, or 503, or 400 Error and
|
112
|
+
# the text "Invalid query. Query did not conform to the expectations.".
|
113
|
+
# In all these cases, running the same query after some seconds fixes
|
114
|
+
# the issue. This it not documented by YouTube and hardly testable, but
|
115
|
+
# trying again is a workaround that works and hardly causes any damage.
|
116
|
+
def run_again_after_a_while?(max_retries = 1)
|
117
|
+
@retries_so_far ||= -1
|
118
|
+
@retries_so_far += 1
|
119
|
+
if (@retries_so_far < max_retries) && worth_another_try?
|
120
|
+
@response = @http_request = @uri = nil
|
121
|
+
sleep 3
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def worth_another_try?
|
126
|
+
case response
|
127
|
+
when Net::HTTPServerError then true
|
128
|
+
when Net::HTTPBadRequest then response.body =~ /did not conform/
|
129
|
+
else false
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# If a request authorized with an access token returns 401, then the
|
134
|
+
# access token might have expired. If a refresh token is also present,
|
135
|
+
# try to run the request one more time with a refreshed access token.
|
136
|
+
def run_again_with_refreshed_authentication?
|
137
|
+
if response.is_a? Net::HTTPUnauthorized
|
138
|
+
@response = @http_request = @uri = nil
|
139
|
+
@auth.refresh
|
140
|
+
end if @auth.respond_to? :refresh
|
141
|
+
end
|
142
|
+
|
116
143
|
def error_for(response)
|
117
144
|
case response
|
118
145
|
when Net::HTTPServerError then Errors::ServerError
|
data/lib/yt/version.rb
CHANGED
@@ -7,7 +7,8 @@ describe Yt::Associations::PartneredChannels, :partner do
|
|
7
7
|
context 'given a content owner with partnered channels' do
|
8
8
|
let(:content_owner) { $content_owner }
|
9
9
|
|
10
|
-
|
10
|
+
# NOTE: Uncomment once size does not runs through *all* the pages
|
11
|
+
# it { expect(partnered_channels.size).to be > 0 }
|
11
12
|
it { expect(partnered_channels.first).to be_a Yt::Channel }
|
12
13
|
end
|
13
14
|
end
|
@@ -4,10 +4,8 @@ require 'yt/collections/earnings'
|
|
4
4
|
describe Yt::Collections::Earnings do
|
5
5
|
subject(:collection) { Yt::Collections::Earnings.new parent: channel }
|
6
6
|
let(:channel) { Yt::Channel.new id: 'UCxO1tY8h1AhOz0T4ENwmpow' }
|
7
|
-
let(:msg) { {response_body: {error: {errors: [error]}}}.to_json }
|
8
7
|
let(:date) { 1.day.ago.to_date }
|
9
8
|
let(:dollars) { 10 }
|
10
|
-
let(:message) { 'Invalid query. Query did not conform to the expectations.' }
|
11
9
|
before { expect(collection).to behave }
|
12
10
|
|
13
11
|
describe '#within' do
|
@@ -16,43 +14,5 @@ describe Yt::Collections::Earnings do
|
|
16
14
|
|
17
15
|
it { expect(collection.within(date..date)[date]).to eq dollars }
|
18
16
|
end
|
19
|
-
|
20
|
-
# NOTE: This test is just a reflection of YouTube irrational behavior
|
21
|
-
# of raising 400 or 504 error once in a while when retrieving earnings.
|
22
|
-
# Hopefully this will get fixed and this code (and test) removed.
|
23
|
-
context 'given YouTube responds to the first request with' do
|
24
|
-
let(:behave) { receive(:flat_map) do
|
25
|
-
expect(collection).to receive(:flat_map).and_return [date, dollars]
|
26
|
-
raise Yt::Error, msg
|
27
|
-
end}
|
28
|
-
|
29
|
-
context 'an Invalid Query error' do
|
30
|
-
let(:error) { {reason: 'badRequest', message: message} }
|
31
|
-
|
32
|
-
it { expect(collection.within(date..date)[date]).to eq dollars }
|
33
|
-
end
|
34
|
-
|
35
|
-
context 'a Backend error' do
|
36
|
-
let(:error) { {reason: 'backendError', message: 'Backend Error'} }
|
37
|
-
|
38
|
-
it { expect(collection.within(date..date)[date]).to eq dollars }
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
context 'given YouTube responds to the second request with' do
|
43
|
-
let(:behave) { receive(:flat_map).twice.and_raise Yt::Error, msg }
|
44
|
-
|
45
|
-
context 'an Invalid Query error' do
|
46
|
-
let(:error) { {reason: 'badRequest', message: message} }
|
47
|
-
|
48
|
-
it { expect{collection.within date..date}.to raise_error Yt::Error }
|
49
|
-
end
|
50
|
-
|
51
|
-
context 'a Backend error' do
|
52
|
-
let(:error) { {reason: 'backendError', message: 'Backend Error'} }
|
53
|
-
|
54
|
-
it { expect{collection.within date..date}.to raise_error Yt::Error }
|
55
|
-
end
|
56
|
-
end
|
57
17
|
end
|
58
18
|
end
|
data/spec/models/request_spec.rb
CHANGED
@@ -1,31 +1,74 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'yt/models/request'
|
3
3
|
|
4
|
+
|
4
5
|
describe Yt::Request do
|
5
|
-
subject(:request) { Yt::Request.new
|
6
|
-
let(:
|
7
|
-
|
8
|
-
before { allow(response).to receive(:body) }
|
6
|
+
subject(:request) { Yt::Request.new host: 'example.com' }
|
7
|
+
let(:response) { response_class.new nil, nil, nil }
|
8
|
+
let(:response_body) { }
|
9
|
+
before { allow(response).to receive(:body).and_return response_body }
|
10
|
+
before { expect(Net::HTTP).to receive(:start).once.and_return response }
|
9
11
|
|
10
12
|
describe '#run' do
|
11
|
-
context 'given a request that returns
|
12
|
-
|
13
|
-
|
14
|
-
end
|
13
|
+
context 'given a request that returns' do
|
14
|
+
context 'a success code 2XX' do
|
15
|
+
let(:response_class) { Net::HTTPOK }
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
it { expect{request.run}.to fail }
|
19
|
-
end
|
17
|
+
it { expect{request.run}.not_to fail }
|
18
|
+
end
|
20
19
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
20
|
+
context 'an error code 5XX' do
|
21
|
+
let(:response_class) { Net::HTTPServerError }
|
22
|
+
let(:retry_response) { retry_response_class.new nil, nil, nil }
|
23
|
+
before { allow(retry_response).to receive(:body) }
|
24
|
+
before { expect(Net::HTTP).to receive(:start).at_least(:once).and_return retry_response }
|
25
|
+
|
26
|
+
context 'every time' do
|
27
|
+
let(:retry_response_class) { Net::HTTPServerError }
|
28
|
+
|
29
|
+
it { expect{request.run}.to fail }
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'but returns a success code 2XX the second time' do
|
33
|
+
let(:retry_response_class) { Net::HTTPOK }
|
34
|
+
|
35
|
+
it { expect{request.run}.not_to fail }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'an error code 400 with "Invalid Query" message' do
|
40
|
+
let(:response_class) { Net::HTTPBadRequest }
|
41
|
+
let(:response_body) { {error: {errors: [message: message]}}.to_json }
|
42
|
+
let(:message) { 'Invalid query. Query did not conform to the expectations' }
|
43
|
+
|
44
|
+
let(:retry_response) { retry_response_class.new nil, nil, nil }
|
45
|
+
before { allow(retry_response).to receive(:body) }
|
46
|
+
before { expect(Net::HTTP).to receive(:start).at_least(:once).and_return retry_response }
|
47
|
+
|
48
|
+
context 'every time' do
|
49
|
+
let(:retry_response_class) { Net::HTTPBadRequest }
|
50
|
+
|
51
|
+
it { expect{request.run}.to fail }
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'but returns a success code 2XX the second time' do
|
55
|
+
let(:retry_response_class) { Net::HTTPOK }
|
56
|
+
|
57
|
+
it { expect{request.run}.not_to fail }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'an error code 401' do
|
62
|
+
let(:response_class) { Net::HTTPUnauthorized }
|
63
|
+
|
64
|
+
it { expect{request.run}.to fail }
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'any other non-2XX error code' do
|
68
|
+
let(:response_class) { Net::HTTPNotFound }
|
25
69
|
|
26
|
-
|
27
|
-
|
28
|
-
it { expect{request.run}.not_to fail }
|
70
|
+
it { expect{request.run}.to fail }
|
71
|
+
end
|
29
72
|
end
|
30
73
|
end
|
31
74
|
end
|