ghost_google-api-client 0.4.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/CHANGELOG.md +77 -0
  2. data/Gemfile +30 -0
  3. data/Gemfile.lock +80 -0
  4. data/LICENSE +202 -0
  5. data/README.md +71 -0
  6. data/Rakefile +42 -0
  7. data/bin/google-api +545 -0
  8. data/lib/compat/multi_json.rb +17 -0
  9. data/lib/google/api_client.rb +802 -0
  10. data/lib/google/api_client/batch.rb +296 -0
  11. data/lib/google/api_client/client_secrets.rb +106 -0
  12. data/lib/google/api_client/discovery.rb +19 -0
  13. data/lib/google/api_client/discovery/api.rb +287 -0
  14. data/lib/google/api_client/discovery/media.rb +77 -0
  15. data/lib/google/api_client/discovery/method.rb +369 -0
  16. data/lib/google/api_client/discovery/resource.rb +150 -0
  17. data/lib/google/api_client/discovery/schema.rb +119 -0
  18. data/lib/google/api_client/environment.rb +42 -0
  19. data/lib/google/api_client/errors.rb +49 -0
  20. data/lib/google/api_client/media.rb +172 -0
  21. data/lib/google/api_client/reference.rb +305 -0
  22. data/lib/google/api_client/result.rb +161 -0
  23. data/lib/google/api_client/service_account.rb +134 -0
  24. data/lib/google/api_client/version.rb +31 -0
  25. data/lib/google/inflection.rb +28 -0
  26. data/spec/fixtures/files/sample.txt +33 -0
  27. data/spec/google/api_client/batch_spec.rb +241 -0
  28. data/spec/google/api_client/discovery_spec.rb +670 -0
  29. data/spec/google/api_client/media_spec.rb +143 -0
  30. data/spec/google/api_client/result_spec.rb +185 -0
  31. data/spec/google/api_client/service_account_spec.rb +58 -0
  32. data/spec/google/api_client_spec.rb +139 -0
  33. data/spec/spec_helper.rb +7 -0
  34. data/tasks/gem.rake +97 -0
  35. data/tasks/git.rake +45 -0
  36. data/tasks/metrics.rake +22 -0
  37. data/tasks/spec.rake +57 -0
  38. data/tasks/wiki.rake +82 -0
  39. data/tasks/yard.rake +29 -0
  40. metadata +253 -0
@@ -0,0 +1,143 @@
1
+ # Copyright 2012 Google Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'spec_helper'
16
+
17
+ require 'google/api_client'
18
+ require 'google/api_client/version'
19
+
20
+ fixtures_path = File.expand_path('../../../fixtures', __FILE__)
21
+
22
+ describe Google::APIClient::UploadIO do
23
+ it 'should reject invalid file paths' do
24
+ (lambda do
25
+ media = Google::APIClient::UploadIO.new('doesnotexist', 'text/plain')
26
+ end).should raise_error
27
+ end
28
+
29
+ describe 'with a file' do
30
+ before do
31
+ @file = File.expand_path('files/sample.txt', fixtures_path)
32
+ @media = Google::APIClient::UploadIO.new(@file, 'text/plain')
33
+ end
34
+
35
+ it 'should report the correct file length' do
36
+ @media.length.should == File.size(@file)
37
+ end
38
+
39
+ it 'should have a mime type' do
40
+ @media.content_type.should == 'text/plain'
41
+ end
42
+ end
43
+
44
+ describe 'with StringIO' do
45
+ before do
46
+ @content = "hello world"
47
+ @media = Google::APIClient::UploadIO.new(StringIO.new(@content), 'text/plain', 'test.txt')
48
+ end
49
+
50
+ it 'should report the correct file length' do
51
+ @media.length.should == @content.length
52
+ end
53
+
54
+ it 'should have a mime type' do
55
+ @media.content_type.should == 'text/plain'
56
+ end
57
+ end
58
+ end
59
+
60
+ describe Google::APIClient::ResumableUpload do
61
+ CLIENT ||= Google::APIClient.new
62
+
63
+ after do
64
+ # Reset client to not-quite-pristine state
65
+ CLIENT.key = nil
66
+ CLIENT.user_ip = nil
67
+ end
68
+
69
+ before do
70
+ @drive = CLIENT.discovered_api('drive', 'v1')
71
+ @file = File.expand_path('files/sample.txt', fixtures_path)
72
+ @media = Google::APIClient::UploadIO.new(@file, 'text/plain')
73
+ @uploader = Google::APIClient::ResumableUpload.new(
74
+ mock_result(308),
75
+ @media,
76
+ 'https://www.googleapis.com/upload/drive/v1/files/12345')
77
+ end
78
+
79
+ it 'should consider 20x status as complete' do
80
+ api_client = stub('api', :execute => mock_result(200))
81
+ @uploader.send_chunk(api_client)
82
+ @uploader.complete?.should == true
83
+ end
84
+
85
+ it 'should consider 30x status as incomplete' do
86
+ api_client = stub('api', :execute => mock_result(308))
87
+ @uploader.send_chunk(api_client)
88
+ @uploader.complete?.should == false
89
+ @uploader.expired?.should == false
90
+ end
91
+
92
+ it 'should consider 40x status as fatal' do
93
+ api_client = stub('api', :execute => mock_result(404))
94
+ @uploader.send_chunk(api_client)
95
+ @uploader.expired?.should == true
96
+ end
97
+
98
+ it 'should detect changes to location' do
99
+ api_client = stub('api', :execute => mock_result(308, 'location' => 'https://www.googleapis.com/upload/drive/v1/files/abcdef'))
100
+ @uploader.send_chunk(api_client)
101
+ @uploader.location.should == 'https://www.googleapis.com/upload/drive/v1/files/abcdef'
102
+ end
103
+
104
+ it 'should resume from the saved range reported by the server' do
105
+ api_client = mock('api')
106
+ api_client.should_receive(:execute).and_return(mock_result(308, 'range' => '0-99'))
107
+ api_client.should_receive(:execute).with(
108
+ hash_including(:headers => hash_including(
109
+ "Content-Range" => "bytes 100-299/#{@media.length}",
110
+ "Content-Length" => "200"
111
+ ))).and_return(mock_result(308))
112
+
113
+ @uploader.chunk_size = 200
114
+ @uploader.send_chunk(api_client) # Send bytes 0-199, only 0-99 saved
115
+ @uploader.send_chunk(api_client) # Send bytes 100-299
116
+ end
117
+
118
+ it 'should resync the offset after 5xx errors' do
119
+ api_client = mock('api')
120
+ api_client.should_receive(:execute).and_return(mock_result(500))
121
+ api_client.should_receive(:execute).with(
122
+ hash_including(:headers => hash_including(
123
+ "Content-Range" => "bytes */#{@media.length}",
124
+ "Content-Length" => "0"
125
+ ))).and_return(mock_result(308, 'range' => '0-99'))
126
+ api_client.should_receive(:execute).with(
127
+ hash_including(:headers => hash_including(
128
+ "Content-Range" => "bytes 100-299/#{@media.length}",
129
+ "Content-Length" => "200"
130
+ ))).and_return(mock_result(308))
131
+
132
+ @uploader.chunk_size = 200
133
+ @uploader.send_chunk(api_client) # 500, invalidate
134
+ @uploader.send_chunk(api_client) # Just resyncs, doesn't actually upload
135
+ @uploader.send_chunk(api_client) # Send next chunk at correct range
136
+ end
137
+
138
+ def mock_result(status, headers = {})
139
+ reference = Google::APIClient::Reference.new(:api_method => @drive.files.insert)
140
+ stub('result', :status => status, :headers => headers, :reference => reference)
141
+ end
142
+
143
+ end
@@ -0,0 +1,185 @@
1
+ # Copyright 2012 Google Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'spec_helper'
16
+
17
+ require 'google/api_client'
18
+ require 'google/api_client/version'
19
+
20
+ describe Google::APIClient::Result do
21
+ CLIENT ||= Google::APIClient.new
22
+
23
+ describe 'with the plus API' do
24
+ before do
25
+ CLIENT.authorization = nil
26
+ @plus = CLIENT.discovered_api('plus', 'v1')
27
+ @reference = Google::APIClient::Reference.new({
28
+ :api_method => @plus.activities.list,
29
+ :parameters => {
30
+ 'userId' => 'me',
31
+ 'collection' => 'public',
32
+ 'maxResults' => 20
33
+ }
34
+ })
35
+ @request = @reference.to_request
36
+
37
+ # Response stub
38
+ @response = stub("response")
39
+ @response.stub(:status).and_return(200)
40
+ @response.stub(:headers).and_return({
41
+ 'etag' => '12345',
42
+ 'x-google-apiary-auth-scopes' =>
43
+ 'https://www.googleapis.com/auth/plus.me',
44
+ 'content-type' => 'application/json; charset=UTF-8',
45
+ 'date' => 'Mon, 23 Apr 2012 00:00:00 GMT',
46
+ 'cache-control' => 'private, max-age=0, must-revalidate, no-transform',
47
+ 'server' => 'GSE',
48
+ 'connection' => 'close'
49
+ })
50
+ end
51
+
52
+ describe 'with a next page token' do
53
+ before do
54
+ @response.stub(:body).and_return(
55
+ <<-END_OF_STRING
56
+ {
57
+ "kind": "plus#activityFeed",
58
+ "etag": "FOO",
59
+ "nextPageToken": "NEXT+PAGE+TOKEN",
60
+ "selfLink": "https://www.googleapis.com/plus/v1/people/foo/activities/public?",
61
+ "nextLink": "https://www.googleapis.com/plus/v1/people/foo/activities/public?maxResults=20&pageToken=NEXT%2BPAGE%2BTOKEN",
62
+ "title": "Plus Public Activity Feed for ",
63
+ "updated": "2012-04-23T00:00:00.000Z",
64
+ "id": "tag:google.com,2010:/plus/people/foo/activities/public",
65
+ "items": []
66
+ }
67
+ END_OF_STRING
68
+ )
69
+ @result = Google::APIClient::Result.new(@reference, @request, @response)
70
+ end
71
+
72
+ it 'should indicate a successful response' do
73
+ @result.error?.should be_false
74
+ end
75
+
76
+ it 'should return the correct next page token' do
77
+ @result.next_page_token.should == 'NEXT+PAGE+TOKEN'
78
+ end
79
+
80
+ it 'should escape the next page token when calling next_page' do
81
+ reference = @result.next_page
82
+ Hash[reference.parameters].should include('pageToken')
83
+ Hash[reference.parameters]['pageToken'].should == 'NEXT+PAGE+TOKEN'
84
+ url = reference.to_request.to_env(Faraday.default_connection)[:url]
85
+ url.to_s.should include('pageToken=NEXT%2BPAGE%2BTOKEN')
86
+ end
87
+
88
+ it 'should return content type correctly' do
89
+ @result.media_type.should == 'application/json'
90
+ end
91
+
92
+ it 'should return the result data correctly' do
93
+ @result.data?.should be_true
94
+ @result.data.class.to_s.should ==
95
+ 'Google::APIClient::Schema::Plus::V1::ActivityFeed'
96
+ @result.data.kind.should == 'plus#activityFeed'
97
+ @result.data.etag.should == 'FOO'
98
+ @result.data.nextPageToken.should == 'NEXT+PAGE+TOKEN'
99
+ @result.data.selfLink.should ==
100
+ 'https://www.googleapis.com/plus/v1/people/foo/activities/public?'
101
+ @result.data.nextLink.should ==
102
+ 'https://www.googleapis.com/plus/v1/people/foo/activities/public?' +
103
+ 'maxResults=20&pageToken=NEXT%2BPAGE%2BTOKEN'
104
+ @result.data.title.should == 'Plus Public Activity Feed for '
105
+ @result.data.id.should ==
106
+ 'tag:google.com,2010:/plus/people/foo/activities/public'
107
+ @result.data.items.should be_empty
108
+ end
109
+ end
110
+
111
+ describe 'without a next page token' do
112
+ before do
113
+ @response.stub(:body).and_return(
114
+ <<-END_OF_STRING
115
+ {
116
+ "kind": "plus#activityFeed",
117
+ "etag": "FOO",
118
+ "selfLink": "https://www.googleapis.com/plus/v1/people/foo/activities/public?",
119
+ "title": "Plus Public Activity Feed for ",
120
+ "updated": "2012-04-23T00:00:00.000Z",
121
+ "id": "tag:google.com,2010:/plus/people/foo/activities/public",
122
+ "items": []
123
+ }
124
+ END_OF_STRING
125
+ )
126
+ @result = Google::APIClient::Result.new(@reference, @request, @response)
127
+ end
128
+
129
+ it 'should not return a next page token' do
130
+ @result.next_page_token.should == nil
131
+ end
132
+
133
+ it 'should return content type correctly' do
134
+ @result.media_type.should == 'application/json'
135
+ end
136
+
137
+ it 'should return the result data correctly' do
138
+ @result.data?.should be_true
139
+ @result.data.class.to_s.should ==
140
+ 'Google::APIClient::Schema::Plus::V1::ActivityFeed'
141
+ @result.data.kind.should == 'plus#activityFeed'
142
+ @result.data.etag.should == 'FOO'
143
+ @result.data.selfLink.should ==
144
+ 'https://www.googleapis.com/plus/v1/people/foo/activities/public?'
145
+ @result.data.title.should == 'Plus Public Activity Feed for '
146
+ @result.data.id.should ==
147
+ 'tag:google.com,2010:/plus/people/foo/activities/public'
148
+ @result.data.items.should be_empty
149
+ end
150
+ end
151
+
152
+ describe 'with JSON error response' do
153
+ before do
154
+ @response.stub(:body).and_return(
155
+ <<-END_OF_STRING
156
+ {
157
+ "error": {
158
+ "errors": [
159
+ {
160
+ "domain": "global",
161
+ "reason": "parseError",
162
+ "message": "Parse Error"
163
+ }
164
+ ],
165
+ "code": 400,
166
+ "message": "Parse Error"
167
+ }
168
+ }
169
+ END_OF_STRING
170
+ )
171
+ @response.stub(:status).and_return(400)
172
+ @result = Google::APIClient::Result.new(@reference, @request, @response)
173
+ end
174
+
175
+ it 'should return error status correctly' do
176
+ @result.error?.should be_true
177
+ end
178
+
179
+ it 'should return the correct error message' do
180
+ @result.error_message.should == 'Parse Error'
181
+ end
182
+
183
+ end
184
+ end
185
+ end
@@ -0,0 +1,58 @@
1
+ # Copyright 2012 Google Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'spec_helper'
16
+
17
+ require 'google/api_client'
18
+
19
+ describe Google::APIClient::JWTAsserter do
20
+
21
+ before do
22
+ @key = OpenSSL::PKey::RSA.new 2048
23
+ end
24
+
25
+ it 'should generate valid JWTs' do
26
+ asserter = Google::APIClient::JWTAsserter.new('client1', 'scope1 scope2', @key)
27
+ jwt = asserter.to_jwt
28
+ jwt.should_not == nil
29
+
30
+ claim = JWT.decode(jwt, @key.public_key, true)
31
+ claim["iss"].should == 'client1'
32
+ claim["scope"].should == 'scope1 scope2'
33
+ end
34
+
35
+ it 'should send valid access token request' do
36
+ stubs = Faraday::Adapter::Test::Stubs.new do |stub|
37
+ stub.post('/o/oauth2/token') do |env|
38
+ params = Addressable::URI.form_unencode(env[:body])
39
+ JWT.decode(params.assoc("assertion").last, @key.public_key)
40
+ params.assoc("grant_type").should == ['grant_type','urn:ietf:params:oauth:grant-type:jwt-bearer']
41
+ [200, {}, '{
42
+ "access_token" : "1/abcdef1234567890",
43
+ "token_type" : "Bearer",
44
+ "expires_in" : 3600
45
+ }']
46
+ end
47
+ end
48
+ connection = Faraday.new(:url => 'https://accounts.google.com') do |builder|
49
+ builder.adapter(:test, stubs)
50
+ end
51
+
52
+ asserter = Google::APIClient::JWTAsserter.new('client1', 'scope1 scope2', @key)
53
+ auth = asserter.authorize(nil, { :connection => connection})
54
+ auth.should_not == nil?
55
+ auth.access_token.should == "1/abcdef1234567890"
56
+ end
57
+ end
58
+
@@ -0,0 +1,139 @@
1
+ # Copyright 2010 Google Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'spec_helper'
16
+
17
+ gem 'faraday', '~> 0.8.1'
18
+ require 'faraday'
19
+ require 'faraday/utils'
20
+
21
+ gem 'signet', '~> 0.4.0'
22
+ require 'signet/oauth_1/client'
23
+
24
+ require 'google/api_client'
25
+ require 'google/api_client/version'
26
+
27
+ shared_examples_for 'configurable user agent' do
28
+ it 'should allow the user agent to be modified' do
29
+ client.user_agent = 'Custom User Agent/1.2.3'
30
+ client.user_agent.should == 'Custom User Agent/1.2.3'
31
+ end
32
+
33
+ it 'should allow the user agent to be set to nil' do
34
+ client.user_agent = nil
35
+ client.user_agent.should == nil
36
+ end
37
+
38
+ it 'should not allow the user agent to be used with bogus values' do
39
+ (lambda do
40
+ client.user_agent = 42
41
+ client.transmit(
42
+ ['GET', 'http://www.google.com/', [], []]
43
+ )
44
+ end).should raise_error(TypeError)
45
+ end
46
+
47
+ it 'should transmit a User-Agent header when sending requests' do
48
+ client.user_agent = 'Custom User Agent/1.2.3'
49
+ request = Faraday::Request.new(:get) do |req|
50
+ req.url('http://www.google.com/')
51
+ end
52
+ stubs = Faraday::Adapter::Test::Stubs.new do |stub|
53
+ stub.get('/') do |env|
54
+ headers = env[:request_headers]
55
+ headers.should have_key('User-Agent')
56
+ headers['User-Agent'].should == client.user_agent
57
+ [200, {}, ['']]
58
+ end
59
+ end
60
+ connection = Faraday.new(:url => 'https://www.google.com') do |builder|
61
+ builder.adapter(:test, stubs)
62
+ end
63
+ client.transmit(:request => request, :connection => connection)
64
+ stubs.verify_stubbed_calls
65
+ end
66
+ end
67
+
68
+ describe Google::APIClient do
69
+ let(:client) { Google::APIClient.new }
70
+
71
+ it 'should make its version number available' do
72
+ Google::APIClient::VERSION::STRING.should be_instance_of(String)
73
+ end
74
+
75
+ it 'should default to OAuth 2' do
76
+ Signet::OAuth2::Client.should === client.authorization
77
+ end
78
+
79
+ it_should_behave_like 'configurable user agent'
80
+
81
+ describe 'configured for OAuth 1' do
82
+ before do
83
+ client.authorization = :oauth_1
84
+ end
85
+
86
+ it 'should use the default OAuth1 client configuration' do
87
+ client.authorization.temporary_credential_uri.to_s.should ==
88
+ 'https://www.google.com/accounts/OAuthGetRequestToken'
89
+ client.authorization.authorization_uri.to_s.should include(
90
+ 'https://www.google.com/accounts/OAuthAuthorizeToken'
91
+ )
92
+ client.authorization.token_credential_uri.to_s.should ==
93
+ 'https://www.google.com/accounts/OAuthGetAccessToken'
94
+ client.authorization.client_credential_key.should == 'anonymous'
95
+ client.authorization.client_credential_secret.should == 'anonymous'
96
+ end
97
+
98
+ it_should_behave_like 'configurable user agent'
99
+ end
100
+
101
+ describe 'configured for OAuth 2' do
102
+ before do
103
+ client.authorization = :oauth_2
104
+ end
105
+
106
+ # TODO
107
+ it_should_behave_like 'configurable user agent'
108
+ end
109
+
110
+ describe 'when executing requests' do
111
+ before do
112
+ client.authorization = :oauth_2
113
+ @connection = Faraday.new(:url => 'https://www.googleapis.com') do |builder|
114
+ stubs = Faraday::Adapter::Test::Stubs.new do |stub|
115
+ stub.get('/test') do |env|
116
+ env[:request_headers]['Authorization'].should == 'Bearer 12345'
117
+ end
118
+ end
119
+ builder.adapter(:test, stubs)
120
+ end
121
+ end
122
+
123
+ it 'should use default authorization' do
124
+ client.authorization.access_token = "12345"
125
+ client.execute(:http_method => :get,
126
+ :uri => 'https://www.googleapis.com/test',
127
+ :connection => @connection)
128
+ end
129
+
130
+ it 'should use request scoped authorization when provided' do
131
+ client.authorization.access_token = "abcdef"
132
+ new_auth = Signet::OAuth2::Client.new(:access_token => '12345')
133
+ client.execute(:http_method => :get,
134
+ :uri => 'https://www.googleapis.com/test',
135
+ :connection => @connection,
136
+ :authorization => new_auth)
137
+ end
138
+ end
139
+ end