jomz-google-api-client 0.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 (63) hide show
  1. data/CHANGELOG.md +144 -0
  2. data/CONTRIBUTING.md +32 -0
  3. data/Gemfile +41 -0
  4. data/LICENSE +202 -0
  5. data/README.md +192 -0
  6. data/Rakefile +46 -0
  7. data/lib/cacerts.pem +2183 -0
  8. data/lib/compat/multi_json.rb +16 -0
  9. data/lib/google/api_client.rb +672 -0
  10. data/lib/google/api_client/auth/compute_service_account.rb +28 -0
  11. data/lib/google/api_client/auth/file_storage.rb +87 -0
  12. data/lib/google/api_client/auth/installed_app.rb +122 -0
  13. data/lib/google/api_client/auth/jwt_asserter.rb +126 -0
  14. data/lib/google/api_client/auth/key_utils.rb +93 -0
  15. data/lib/google/api_client/auth/pkcs12.rb +41 -0
  16. data/lib/google/api_client/batch.rb +323 -0
  17. data/lib/google/api_client/client_secrets.rb +176 -0
  18. data/lib/google/api_client/discovery.rb +19 -0
  19. data/lib/google/api_client/discovery/api.rb +300 -0
  20. data/lib/google/api_client/discovery/media.rb +77 -0
  21. data/lib/google/api_client/discovery/method.rb +363 -0
  22. data/lib/google/api_client/discovery/resource.rb +156 -0
  23. data/lib/google/api_client/discovery/schema.rb +121 -0
  24. data/lib/google/api_client/environment.rb +42 -0
  25. data/lib/google/api_client/errors.rb +60 -0
  26. data/lib/google/api_client/gzip.rb +28 -0
  27. data/lib/google/api_client/logging.rb +32 -0
  28. data/lib/google/api_client/media.rb +259 -0
  29. data/lib/google/api_client/railtie.rb +16 -0
  30. data/lib/google/api_client/reference.rb +27 -0
  31. data/lib/google/api_client/request.rb +351 -0
  32. data/lib/google/api_client/result.rb +253 -0
  33. data/lib/google/api_client/service.rb +233 -0
  34. data/lib/google/api_client/service/batch.rb +103 -0
  35. data/lib/google/api_client/service/request.rb +144 -0
  36. data/lib/google/api_client/service/resource.rb +40 -0
  37. data/lib/google/api_client/service/result.rb +162 -0
  38. data/lib/google/api_client/service/simple_file_store.rb +151 -0
  39. data/lib/google/api_client/service/stub_generator.rb +59 -0
  40. data/lib/google/api_client/service_account.rb +18 -0
  41. data/lib/google/api_client/version.rb +31 -0
  42. data/lib/google/inflection.rb +28 -0
  43. data/spec/fixtures/files/privatekey.p12 +0 -0
  44. data/spec/fixtures/files/sample.txt +33 -0
  45. data/spec/fixtures/files/secret.pem +19 -0
  46. data/spec/google/api_client/batch_spec.rb +249 -0
  47. data/spec/google/api_client/discovery_spec.rb +652 -0
  48. data/spec/google/api_client/gzip_spec.rb +86 -0
  49. data/spec/google/api_client/media_spec.rb +179 -0
  50. data/spec/google/api_client/request_spec.rb +30 -0
  51. data/spec/google/api_client/result_spec.rb +203 -0
  52. data/spec/google/api_client/service_account_spec.rb +164 -0
  53. data/spec/google/api_client/service_spec.rb +586 -0
  54. data/spec/google/api_client/simple_file_store_spec.rb +137 -0
  55. data/spec/google/api_client_spec.rb +253 -0
  56. data/spec/spec_helper.rb +56 -0
  57. data/tasks/gem.rake +97 -0
  58. data/tasks/git.rake +45 -0
  59. data/tasks/metrics.rake +22 -0
  60. data/tasks/spec.rake +57 -0
  61. data/tasks/wiki.rake +82 -0
  62. data/tasks/yard.rake +29 -0
  63. metadata +309 -0
@@ -0,0 +1,86 @@
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::Gzip do
21
+
22
+ def create_connection(&block)
23
+ Faraday.new do |b|
24
+ b.response :gzip
25
+ b.adapter :test do |stub|
26
+ stub.get '/', &block
27
+ end
28
+ end
29
+ end
30
+
31
+ it 'should ignore non-zipped content' do
32
+ conn = create_connection do |env|
33
+ [200, {}, 'Hello world']
34
+ end
35
+ result = conn.get('/')
36
+ result.body.should == "Hello world"
37
+ end
38
+
39
+ it 'should decompress gziped content' do
40
+ conn = create_connection do |env|
41
+ [200, { 'Content-Encoding' => 'gzip'}, Base64.decode64('H4sICLVGwlEAA3RtcADzSM3JyVcozy/KSeECANXgObcMAAAA')]
42
+ end
43
+ result = conn.get('/')
44
+ result.body.should == "Hello world\n"
45
+ end
46
+
47
+ describe 'with API Client' do
48
+
49
+ before do
50
+ @client = Google::APIClient.new(:application_name => 'test')
51
+ @client.authorization = nil
52
+ end
53
+
54
+
55
+ it 'should send gzip in user agent' do
56
+ conn = create_connection do |env|
57
+ agent = env[:request_headers]['User-Agent']
58
+ agent.should_not be_nil
59
+ agent.should include 'gzip'
60
+ [200, {}, 'Hello world']
61
+ end
62
+ @client.execute(:uri => 'http://www.example.com/', :connection => conn)
63
+ end
64
+
65
+ it 'should send gzip in accept-encoding' do
66
+ conn = create_connection do |env|
67
+ encoding = env[:request_headers]['Accept-Encoding']
68
+ encoding.should_not be_nil
69
+ encoding.should include 'gzip'
70
+ [200, {}, 'Hello world']
71
+ end
72
+ @client.execute(:uri => 'http://www.example.com/', :connection => conn)
73
+ end
74
+
75
+ it 'should not send gzip in accept-encoding if disabled for request' do
76
+ conn = create_connection do |env|
77
+ encoding = env[:request_headers]['Accept-Encoding']
78
+ encoding.should_not include('gzip') unless encoding.nil?
79
+ [200, {}, 'Hello world']
80
+ end
81
+ response = @client.execute(:uri => 'http://www.example.com/', :gzip => false, :connection => conn)
82
+ puts response.status
83
+ end
84
+
85
+ end
86
+ end
@@ -0,0 +1,179 @@
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::RangedIO do
61
+ before do
62
+ @source = StringIO.new("1234567890abcdef")
63
+ @io = Google::APIClient::RangedIO.new(@source, 1, 5)
64
+ end
65
+
66
+ it 'should return the correct range when read entirely' do
67
+ @io.read.should == "23456"
68
+ end
69
+
70
+ it 'should maintain position' do
71
+ @io.read(1).should == '2'
72
+ @io.read(2).should == '34'
73
+ @io.read(2).should == '56'
74
+ end
75
+
76
+ it 'should allow rewinds' do
77
+ @io.read(2).should == '23'
78
+ @io.rewind()
79
+ @io.read(2).should == '23'
80
+ end
81
+
82
+ it 'should allow setting position' do
83
+ @io.pos = 3
84
+ @io.read.should == '56'
85
+ end
86
+
87
+ it 'should not allow position to be set beyond range' do
88
+ @io.pos = 10
89
+ @io.read.should == ''
90
+ end
91
+
92
+ it 'should return empty string when read amount is zero' do
93
+ @io.read(0).should == ''
94
+ end
95
+
96
+ it 'should return empty string at EOF if amount is nil' do
97
+ @io.read
98
+ @io.read.should == ''
99
+ end
100
+
101
+ it 'should return nil at EOF if amount is positive int' do
102
+ @io.read
103
+ @io.read(1).should == nil
104
+ end
105
+
106
+ end
107
+
108
+ describe Google::APIClient::ResumableUpload do
109
+ CLIENT = Google::APIClient.new(:application_name => 'API Client Tests') unless defined?(CLIENT)
110
+
111
+ after do
112
+ # Reset client to not-quite-pristine state
113
+ CLIENT.key = nil
114
+ CLIENT.user_ip = nil
115
+ end
116
+
117
+ before do
118
+ @drive = CLIENT.discovered_api('drive', 'v1')
119
+ @file = File.expand_path('files/sample.txt', fixtures_path)
120
+ @media = Google::APIClient::UploadIO.new(@file, 'text/plain')
121
+ @uploader = Google::APIClient::ResumableUpload.new(
122
+ :media => @media,
123
+ :api_method => @drive.files.insert,
124
+ :uri => 'https://www.googleapis.com/upload/drive/v1/files/12345')
125
+ end
126
+
127
+ it 'should consider 20x status as complete' do
128
+ request = @uploader.to_http_request
129
+ @uploader.process_http_response(mock_result(200))
130
+ @uploader.complete?.should == true
131
+ end
132
+
133
+ it 'should consider 30x status as incomplete' do
134
+ request = @uploader.to_http_request
135
+ @uploader.process_http_response(mock_result(308))
136
+ @uploader.complete?.should == false
137
+ @uploader.expired?.should == false
138
+ end
139
+
140
+ it 'should consider 40x status as fatal' do
141
+ request = @uploader.to_http_request
142
+ @uploader.process_http_response(mock_result(404))
143
+ @uploader.expired?.should == true
144
+ end
145
+
146
+ it 'should detect changes to location' do
147
+ request = @uploader.to_http_request
148
+ @uploader.process_http_response(mock_result(308, 'location' => 'https://www.googleapis.com/upload/drive/v1/files/abcdef'))
149
+ @uploader.uri.to_s.should == 'https://www.googleapis.com/upload/drive/v1/files/abcdef'
150
+ end
151
+
152
+ it 'should resume from the saved range reported by the server' do
153
+ @uploader.chunk_size = 200
154
+ @uploader.to_http_request # Send bytes 0-199, only 0-99 saved
155
+ @uploader.process_http_response(mock_result(308, 'range' => '0-99'))
156
+ method, url, headers, body = @uploader.to_http_request # Send bytes 100-299
157
+ headers['Content-Range'].should == "bytes 100-299/#{@media.length}"
158
+ headers['Content-length'].should == "200"
159
+ end
160
+
161
+ it 'should resync the offset after 5xx errors' do
162
+ @uploader.chunk_size = 200
163
+ @uploader.to_http_request
164
+ @uploader.process_http_response(mock_result(500)) # Invalidates range
165
+ method, url, headers, body = @uploader.to_http_request # Resync
166
+ headers['Content-Range'].should == "bytes */#{@media.length}"
167
+ headers['Content-length'].should == "0"
168
+ @uploader.process_http_response(mock_result(308, 'range' => '0-99'))
169
+ method, url, headers, body = @uploader.to_http_request # Send next chunk at correct range
170
+ headers['Content-Range'].should == "bytes 100-299/#{@media.length}"
171
+ headers['Content-length'].should == "200"
172
+ end
173
+
174
+ def mock_result(status, headers = {})
175
+ reference = Google::APIClient::Reference.new(:api_method => @drive.files.insert)
176
+ double('result', :status => status, :headers => headers, :reference => reference)
177
+ end
178
+
179
+ end
@@ -0,0 +1,30 @@
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::Request do
21
+ CLIENT = Google::APIClient.new(:application_name => 'API Client Tests') unless defined?(CLIENT)
22
+
23
+ it 'should normalize parameter names to strings' do
24
+ request = Google::APIClient::Request.new(:uri => 'https://www.google.com', :parameters => {
25
+ :a => '1', 'b' => '2'
26
+ })
27
+ request.parameters['a'].should == '1'
28
+ request.parameters['b'].should == '2'
29
+ end
30
+ end
@@ -0,0 +1,203 @@
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(:application_name => 'API Client Tests') unless defined?(CLIENT)
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_http_request
36
+
37
+ # Response double
38
+ @response = double("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": "123456790",
65
+ "items": []
66
+ }
67
+ END_OF_STRING
68
+ )
69
+ @result = Google::APIClient::Result.new(@reference, @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_env(CLIENT.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 == "123456790"
106
+ @result.data.items.should be_empty
107
+ end
108
+ end
109
+
110
+ describe 'without a next page token' do
111
+ before do
112
+ @response.stub(:body).and_return(
113
+ <<-END_OF_STRING
114
+ {
115
+ "kind": "plus#activityFeed",
116
+ "etag": "FOO",
117
+ "selfLink": "https://www.googleapis.com/plus/v1/people/foo/activities/public?",
118
+ "title": "Plus Public Activity Feed for ",
119
+ "updated": "2012-04-23T00:00:00.000Z",
120
+ "id": "123456790",
121
+ "items": []
122
+ }
123
+ END_OF_STRING
124
+ )
125
+ @result = Google::APIClient::Result.new(@reference, @response)
126
+ end
127
+
128
+ it 'should not return a next page token' do
129
+ @result.next_page_token.should == nil
130
+ end
131
+
132
+ it 'should return content type correctly' do
133
+ @result.media_type.should == 'application/json'
134
+ end
135
+
136
+ it 'should return the result data correctly' do
137
+ @result.data?.should be_true
138
+ @result.data.class.to_s.should ==
139
+ 'Google::APIClient::Schema::Plus::V1::ActivityFeed'
140
+ @result.data.kind.should == 'plus#activityFeed'
141
+ @result.data.etag.should == 'FOO'
142
+ @result.data.selfLink.should ==
143
+ 'https://www.googleapis.com/plus/v1/people/foo/activities/public?'
144
+ @result.data.title.should == 'Plus Public Activity Feed for '
145
+ @result.data.id.should == "123456790"
146
+ @result.data.items.should be_empty
147
+ end
148
+ end
149
+
150
+ describe 'with JSON error response' do
151
+ before do
152
+ @response.stub(:body).and_return(
153
+ <<-END_OF_STRING
154
+ {
155
+ "error": {
156
+ "errors": [
157
+ {
158
+ "domain": "global",
159
+ "reason": "parseError",
160
+ "message": "Parse Error"
161
+ }
162
+ ],
163
+ "code": 400,
164
+ "message": "Parse Error"
165
+ }
166
+ }
167
+ END_OF_STRING
168
+ )
169
+ @response.stub(:status).and_return(400)
170
+ @result = Google::APIClient::Result.new(@reference, @response)
171
+ end
172
+
173
+ it 'should return error status correctly' do
174
+ @result.error?.should be_true
175
+ end
176
+
177
+ it 'should return the correct error message' do
178
+ @result.error_message.should == 'Parse Error'
179
+ end
180
+ end
181
+
182
+ describe 'with 204 No Content response' do
183
+ before do
184
+ @response.stub(:body).and_return('')
185
+ @response.stub(:status).and_return(204)
186
+ @response.stub(:headers).and_return({})
187
+ @result = Google::APIClient::Result.new(@reference, @response)
188
+ end
189
+
190
+ it 'should indicate no data is available' do
191
+ @result.data?.should be_false
192
+ end
193
+
194
+ it 'should return nil for data' do
195
+ @result.data.should == nil
196
+ end
197
+
198
+ it 'should return nil for media_type' do
199
+ @result.media_type.should == nil
200
+ end
201
+ end
202
+ end
203
+ end