ghost_google-api-client 0.4.7.1

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.
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