google-api 0.0.1.alpha → 0.0.1.beta

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.
@@ -0,0 +1,13 @@
1
+ {
2
+ "error": {
3
+ "errors": [
4
+ {
5
+ "domain": "global",
6
+ "reason": "notFound",
7
+ "message": "File not found: asdfasdf"
8
+ }
9
+ ],
10
+ "code": 404,
11
+ "message": "File not found: asdfasdf"
12
+ }
13
+ }
@@ -0,0 +1,51 @@
1
+ {
2
+ "kind": "drive#fileList",
3
+ "etag": "\"j-adummyetag/pIy649ByRBzgnLlQWUq3mFq4jbc\"",
4
+ "selfLink": "https://www.googleapis.com/drive/v2/files",
5
+ "items": [
6
+ {
7
+ "kind": "drive#file",
8
+ "id": "adummyidnumber",
9
+ "etag": "\"j-adummyetag/MTM0NjIwMjM0MzYzOQ\"",
10
+ "selfLink": "https://www.googleapis.com/drive/v2/files/adummyidnumber",
11
+ "alternateLink": "https://docs.google.com/a/bryant.edu/folder/d/adummyidnumber/edit",
12
+ "title": "Test Folder",
13
+ "mimeType": "application/vnd.google-apps.folder",
14
+ "labels": {
15
+ "starred": false,
16
+ "hidden": false,
17
+ "trashed": false,
18
+ "restricted": false,
19
+ "viewed": true
20
+ },
21
+ "createdDate": "2012-08-29T01:05:43.651Z",
22
+ "modifiedDate": "2012-08-29T01:05:43.639Z",
23
+ "modifiedByMeDate": "2012-08-29T01:05:43.639Z",
24
+ "lastViewedByMeDate": "2012-08-29T01:05:43.639Z",
25
+ "parents": [
26
+ {
27
+ "kind": "drive#parentReference",
28
+ "id": "adummyparentidnumber",
29
+ "selfLink": "https://www.googleapis.com/drive/v2/files/adummyidnumber/parents/adummyparentidnumber",
30
+ "parentLink": "https://www.googleapis.com/drive/v2/files/adummyparentidnumber",
31
+ "isRoot": true
32
+ }
33
+ ],
34
+ "userPermission": {
35
+ "kind": "drive#permission",
36
+ "etag": "\"j-adummyetag/JxXBnYVV_rnFy38Ll-sTHyzNvF8\"",
37
+ "id": "me",
38
+ "selfLink": "https://www.googleapis.com/drive/v2/files/adummyidnumber/permissions/me",
39
+ "role": "owner",
40
+ "type": "user"
41
+ },
42
+ "quotaBytesUsed": "0",
43
+ "ownerNames": [
44
+ "Alexander Robbin"
45
+ ],
46
+ "lastModifyingUserName": "Alexander Robbin",
47
+ "editable": true,
48
+ "writersCanShare": true
49
+ }
50
+ ]
51
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "access_token": "reallysecureaccesstoken",
3
+ "token_type": "Bearer",
4
+ "expires_in": 3600
5
+ }
@@ -0,0 +1,201 @@
1
+ require 'spec_helper'
2
+
3
+ shared_context "api call" do
4
+
5
+ let(:map) { fixture(:discovery_rest_drive)['resources']['files']['methods'] }
6
+ let(:api) { GoogleAPI::API.new(client.access_token, :drive, map) }
7
+ let(:stubbed_response) { OAuth2Response.new }
8
+ before(:each) do
9
+ GoogleAPI.discovered_apis[:drive] = fixture(:discovery_rest_drive)
10
+ client.client.connection = TEST_CONNECTION
11
+ end
12
+
13
+ end
14
+
15
+ describe GoogleAPI::API do
16
+
17
+ let(:object) { User.new }
18
+ let(:client) { GoogleAPI::Client.new(object) }
19
+ before(:each) do
20
+ GoogleAPI.configure do |config|
21
+ config.client_id = 'test id'
22
+ config.client_secret = 'test secret'
23
+ end
24
+ object.authenticated = true
25
+ end
26
+
27
+ describe "#initialize" do
28
+
29
+ let(:api) { GoogleAPI::API.new(client.access_token, :drive, fixture(:discovery_rest_drive)['resources']) }
30
+
31
+ it "should set the access_token attribute" do
32
+ api.access_token.token.should == client.access_token.token
33
+ end
34
+
35
+ it "should set the api attribute" do
36
+ api.api.should == :drive
37
+ end
38
+
39
+ it "should set the map attribute" do
40
+ api.map.should == fixture(:discovery_rest_drive)['resources']
41
+ end
42
+
43
+ end
44
+
45
+ describe "API method generation" do
46
+
47
+ context "when there are methods lower in the tree" do
48
+
49
+ it "should build another GoogleAPI::API class with the correct map" do
50
+ api = GoogleAPI::API.new(client.access_token, :drive, fixture(:discovery_rest_drive)['resources'])
51
+ api.files.should be_an_instance_of(GoogleAPI::API)
52
+ end
53
+
54
+ end
55
+
56
+ context "when the method tree is the lowest it can be" do
57
+
58
+ include_context "api call"
59
+
60
+ it "should call the #build_url method" do
61
+ api.should_receive(:build_url).with(map['list'], {}).and_return(['https://www.googleapis.com/drive/v2/files', {}])
62
+ api.list
63
+ end
64
+
65
+ %w(POST PUT PATCH).each do |http_method|
66
+ context "when the method's HTTP method is #{http_method}" do
67
+
68
+ it "should raise ArgumentError if no body was passed" do
69
+ expect {
70
+ api.insert(media: '/Test/File/Path.txt')
71
+ }.to raise_error(ArgumentError)
72
+ end
73
+
74
+ end
75
+ end
76
+
77
+ context "when the mediaUpload key is present" do
78
+
79
+ it "should call the #upload method" do
80
+ api.should_receive(:upload).with('post', 'https://www.googleapis.com/resumable/upload/drive/v2/files', body: {title: 'Test File.txt'}, media: '/Test/File/Path.txt')
81
+ api.insert(body: {title: 'Test File.txt'}, media: '/Test/File/Path.txt')
82
+ end
83
+
84
+ end
85
+
86
+ context "when it is a normal API method request" do
87
+
88
+ it "should delegate to the #request method" do
89
+ api.should_receive(:request).with('get', 'https://www.googleapis.com/drive/v2/files', {})
90
+ api.list
91
+ end
92
+
93
+ end
94
+
95
+ end
96
+
97
+ end
98
+
99
+ describe "#request" do
100
+
101
+ include_context "api call"
102
+
103
+ it "should build the correct headers hash" do
104
+ ::OAuth2::AccessToken.any_instance.should_receive(:get).with('https://www.googleapis.com/drive/v2/files', headers: {'Content-Type' => 'application/json'}).and_return(stubbed_response)
105
+ api.list
106
+ end
107
+
108
+ it "should convert the body to JSON" do
109
+ ::OAuth2::AccessToken.any_instance.should_receive(:post).with('https://www.googleapis.com/drive/v2/files', body: '{"title":"Test Folder"}', headers: {'Content-Type' => 'application/json'}).and_return(stubbed_response)
110
+ api.insert(body: {title: 'Test Folder'})
111
+ end
112
+
113
+ context "when a request is successful" do
114
+
115
+ it "should only make the request attempt once" do
116
+ ::OAuth2::AccessToken.any_instance.should_receive(:get).once.and_return(stubbed_response)
117
+ api.list
118
+ end
119
+
120
+ it "should return a parsed response if body is present" do
121
+ api.list.should == fixture(:drive_files)
122
+ end
123
+
124
+ it "should return the full Response object if body is blank" do
125
+ api.delete(fileId: 'adummyfileidnumber').should be_an_instance_of(::OAuth2::Response)
126
+ end
127
+
128
+ end
129
+
130
+ context "when a request fails and development mode is turned off" do
131
+
132
+ it "should attempt the request 5 times" do
133
+ ::OAuth2::AccessToken.any_instance.should_receive(:put).exactly(5).times.and_return(OAuth2Response.new(404))
134
+ api.update(fileId: 'adummyfileidnumber', body: {bad: 'attribute'})
135
+ end
136
+
137
+ it "should break out of the attempt loop if the request succeeds" do
138
+ ::OAuth2::AccessToken.any_instance.should_receive(:put).exactly(2).times.and_return(OAuth2Response.new(404))
139
+ ::OAuth2::AccessToken.any_instance.should_receive(:put).once.and_return(stubbed_response)
140
+ api.update(fileId: 'adummyfileidnumber', body: {bad: 'attribute'})
141
+ end
142
+
143
+ end
144
+
145
+ end
146
+
147
+ describe "#upload" do
148
+
149
+ include_context "api call"
150
+
151
+ before(:each) { api.stub(:request).and_return(stubbed_response) }
152
+
153
+ it "should build the correct options hash that is passed to the first request" do
154
+ api.should_receive(:request).with('post', 'https://www.googleapis.com/resumable/upload/drive/v2/files', body: {title: 'Test File.txt', mimeType: 'application/x-ruby'}, headers: {'X-Upload-Content-Type' => 'application/x-ruby'})
155
+ api.send(:upload, 'post', 'https://www.googleapis.com/resumable/upload/drive/v2/files', body: {title: 'Test File.txt'}, media: File.expand_path('spec/spec_helper.rb'))
156
+ end
157
+
158
+ it "should change the original headers for the actual file upload" do
159
+ api.should_receive(:request)
160
+ api.should_receive(:request).with(:put, nil, body: File.read(File.expand_path('spec/spec_helper.rb')), headers: {'Content-Type' => 'application/x-ruby', 'Content-Length' => '399'})
161
+ api.send(:upload, 'post', 'https://www.googleapis.com/resumable/upload/drive/v2/files', body: {title: 'Test File.txt'}, media: File.expand_path('spec/spec_helper.rb'))
162
+ end
163
+
164
+ end
165
+
166
+ describe "#build_url" do
167
+
168
+ include_context "api call"
169
+
170
+ context "when the mediaUpload key is present" do
171
+
172
+ it "should use the rootUrl of the discovered API" do
173
+ url, options = api.send(:build_url, map['insert'], media: File.expand_path('spec/spec_helper.rb'))
174
+ url.should == 'https://www.googleapis.com/resumable/upload/drive/v2/files'
175
+ end
176
+
177
+ end
178
+
179
+ context "when it is a normal API method request" do
180
+
181
+ it "should build the final URL" do
182
+ url, options = api.send(:build_url, map['get'], fileId: 'adummyfileidnumber', updateViewedDate: true)
183
+ url.should == 'https://www.googleapis.com/drive/v2/files/adummyfileidnumber?updateViewedDate=true'
184
+ end
185
+
186
+ it "should delete query/path parameters from the options Hash" do
187
+ url, options = api.send(:build_url, map['get'], fileId: 'adummyfileidnumber', updateViewedDate: true)
188
+ options.should == {}
189
+ end
190
+
191
+ it "should raise ArgumentError if the parameter is required and the matching option isn't passed" do
192
+ expect {
193
+ api.send(:build_url, map['get'], updateViewedDate: true)
194
+ }.to raise_error(ArgumentError)
195
+ end
196
+
197
+ end
198
+
199
+ end
200
+
201
+ end
@@ -0,0 +1,115 @@
1
+ require 'spec_helper'
2
+
3
+ describe GoogleAPI::Client do
4
+
5
+ let(:object) { User.new }
6
+ let(:client) { GoogleAPI::Client.new(object) }
7
+ before(:each) do
8
+ GoogleAPI.configure do |config|
9
+ config.client_id = 'test id'
10
+ config.client_secret = 'test secret'
11
+ end
12
+ object.authenticated = true
13
+ client.client.connection = TEST_CONNECTION
14
+ end
15
+
16
+ describe "#initialize" do
17
+
18
+ it "should set the object attribute" do
19
+ client = GoogleAPI::Client.new(object)
20
+ client.object.should == object
21
+ end
22
+
23
+ it "should raise NoMethodError if the object isn't oauthable" do
24
+ expect {
25
+ GoogleAPI::Client.new(Class.new)
26
+ }.to raise_error(NoMethodError)
27
+ end
28
+
29
+ it "should raise ArgumentError if the object isn't OAuth2 authenticated" do
30
+ object.authenticated = false
31
+ expect {
32
+ GoogleAPI::Client.new(object)
33
+ }.to raise_error(ArgumentError)
34
+ end
35
+
36
+ end
37
+
38
+ describe "#access_token" do
39
+
40
+ it "should call to the OAuth2 Access Token class" do
41
+ client.access_token.token.should == object.oauth_hash[:access_token]
42
+ end
43
+
44
+ context "when the access token is expired" do
45
+
46
+ before(:each) { object.expires_at = Time.now - 86400 }
47
+
48
+ it "should call refresh on the OAuth2 Access Token" do
49
+ stubbed_access_token = Class.new
50
+ stubbed_access_token.stub(:token).and_return('test token')
51
+ ::OAuth2::AccessToken.any_instance.should_receive(:refresh!).and_return(stubbed_access_token)
52
+ client.access_token
53
+ end
54
+
55
+ it "should update the object's stored OAuth information" do
56
+ object.should_receive(:update_access_token!)
57
+ client.access_token
58
+ end
59
+
60
+ end
61
+
62
+ context "when the access token is fresh" do
63
+
64
+ it "should not try and refresh the token again" do
65
+ ::OAuth2::AccessToken.any_instance.should_not_receive(:refresh!)
66
+ client.access_token
67
+ end
68
+
69
+ end
70
+
71
+ end
72
+
73
+ describe "#client" do
74
+
75
+ it "should create the OAuth2 Client instance" do
76
+ client.client.should be_an_instance_of(::OAuth2::Client)
77
+ end
78
+
79
+ end
80
+
81
+ describe "API method generation" do
82
+
83
+ context "when the API hasn't been discovered" do
84
+
85
+ it "should call out to the Discovery API" do
86
+ GoogleAPI.discovered_apis[:drive].should be_nil
87
+ client.drive
88
+ GoogleAPI.discovered_apis[:drive].should == fixture(:discovery_rest_drive)
89
+ end
90
+
91
+ it "should save the Discovery API map to the cache" do
92
+ client.drive
93
+ GoogleAPI.discovered_apis[:drive].should == fixture(:discovery_rest_drive)
94
+ end
95
+
96
+ end
97
+
98
+ context "when the API has already been discovered" do
99
+
100
+ it "should not fetch the Discovery API map" do
101
+ GoogleAPI.discovered_apis[:drive] = fixture(:discovery_drive)
102
+ ::OAuth2::AccessToken.any_instance.should_not_receive(:get)
103
+ client.drive
104
+ end
105
+
106
+ end
107
+
108
+ it "should build the GoogleAPI::API class" do
109
+ GoogleAPI::API.should_receive(:new).with(an_instance_of(::OAuth2::AccessToken), :drive, fixture(:discovery_rest_drive)['resources'])
110
+ client.drive
111
+ end
112
+
113
+ end
114
+
115
+ end
@@ -0,0 +1,102 @@
1
+ require 'spec_helper'
2
+
3
+ describe GoogleAPI do
4
+
5
+ describe "#configure" do
6
+
7
+ it "should raise an error when the CLIENT ID is blank" do
8
+ expect {
9
+ GoogleAPI.configure do |config|
10
+ config.client_id = nil
11
+ config.client_secret = 'test secret'
12
+ end
13
+ }.to raise_error(ArgumentError)
14
+ end
15
+
16
+ it "should raise an error when the CLIENT SECRET is blank" do
17
+ expect {
18
+ GoogleAPI.configure do |config|
19
+ config.client_id = 'test id'
20
+ config.client_secret = nil
21
+ end
22
+ }.to raise_error(ArgumentError)
23
+ end
24
+
25
+ it "should raise an error when both the CLIENT ID and SECRET are blank" do
26
+ expect {
27
+ GoogleAPI.configure do |config|
28
+ config.client_id = nil
29
+ config.client_secret = nil
30
+ end
31
+ }.to raise_error(ArgumentError)
32
+ end
33
+
34
+ it "should set development mode to false when not passed" do
35
+ GoogleAPI.configure do |config|
36
+ config.client_id = 'test id'
37
+ config.client_secret = 'test secret'
38
+ end
39
+
40
+ GoogleAPI.development_mode.should be_false
41
+ end
42
+
43
+ it "should set development mode when passed" do
44
+ GoogleAPI.configure do |config|
45
+ config.client_id = 'test id'
46
+ config.client_secret = 'test secret'
47
+ config.development_mode = true
48
+ end
49
+
50
+ GoogleAPI.development_mode.should be_true
51
+ end
52
+
53
+ it "should use Rails' logger when Rails is defined" do
54
+ stub_const("Rails", Class.new)
55
+ Rails.stub(:logger).and_return(Logger.new(STDOUT))
56
+
57
+ Rails.should_receive(:logger)
58
+
59
+ GoogleAPI.configure do |config|
60
+ config.client_id = 'test id'
61
+ config.client_secret = 'test secret'
62
+ end
63
+
64
+ end
65
+
66
+ it "should use a STDOUT when Rails is not defined" do
67
+ GoogleAPI.should_receive(:stdout_logger)
68
+
69
+ GoogleAPI.configure do |config|
70
+ config.client_id = 'test id'
71
+ config.client_secret = 'test secret'
72
+ end
73
+ end
74
+
75
+ end
76
+
77
+ describe "#discovered_apis" do
78
+
79
+ it "should start as an empty hash" do
80
+ GoogleAPI.discovered_apis.should == {}
81
+ end
82
+
83
+ it "should keep a cache of discovered APIs" do
84
+ GoogleAPI.discovered_apis[:test_api1] = {a: 1, b: 2, c: 3}
85
+
86
+ GoogleAPI.discovered_apis[:test_api1].should == {a: 1, b: 2, c: 3}
87
+ end
88
+
89
+ end
90
+
91
+ describe "#stdout_logger" do
92
+
93
+ let(:logger) { GoogleAPI.stdout_logger }
94
+
95
+ it "should create a new Logger object" do
96
+ logger = GoogleAPI.stdout_logger
97
+ logger.should be_an_instance_of(Logger)
98
+ end
99
+
100
+ end
101
+
102
+ end
@@ -0,0 +1,17 @@
1
+ require 'simplecov'
2
+ SimpleCov.start do
3
+ add_filter 'spec'
4
+ end
5
+
6
+ require 'rspec'
7
+ require 'google-api'
8
+
9
+ Faraday.default_adapter = :test
10
+
11
+ # Requires supporting files with custom matchers and macros, etc,
12
+ # in ./support/ and its subdirectories.
13
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
14
+
15
+ RSpec.configure do |config|
16
+ config.after(:each) { GoogleAPI.reset_environment! }
17
+ end
@@ -0,0 +1,17 @@
1
+ stubs = Faraday::Adapter::Test::Stubs.new do |stub|
2
+ stub.post('/o/oauth2/token') { faraday_response(:refresh_access_token) }
3
+ stub.get('/discovery/v1/apis?name=drive&preferred=true') { faraday_response(:discovery_drive) }
4
+ stub.get('/discovery/v1/apis/drive/v2/rest') { faraday_response(:discovery_rest_drive) }
5
+ stub.post('/resumable/upload/drive/v2/files') { [200] }
6
+ stub.get('/drive/v2/files') { faraday_response(:drive_files) }
7
+ stub.delete('/drive/v2/files/adummyfileidnumber') { [204] }
8
+ stub.put('/drive/v2/files/adummyfileidnumber') { faraday_response(:drive_error, 404) }
9
+ end
10
+
11
+ TEST_CONNECTION = Faraday.new do |builder|
12
+ builder.adapter :test, stubs
13
+ end
14
+
15
+ def faraday_response(fixture, code = 200)
16
+ [code, {'Content-Type' => 'application/json'}, fixture(fixture, false)]
17
+ end
@@ -0,0 +1,9 @@
1
+ require 'multi_json'
2
+
3
+ def fixture(name, jsonify = true)
4
+ path = File.expand_path '../../fixtures', __FILE__
5
+
6
+ file = File.read("#{path}/#{name}.json")
7
+
8
+ jsonify ? MultiJson.load(file) : file
9
+ end
@@ -0,0 +1,17 @@
1
+ class OAuth2Response
2
+
3
+ attr_accessor :status
4
+
5
+ def initialize(status_code = 200)
6
+ @status = status_code
7
+ end
8
+
9
+ def headers
10
+ {}
11
+ end
12
+
13
+ def parsed
14
+ fixture(:drive_files)
15
+ end
16
+
17
+ end
@@ -0,0 +1,27 @@
1
+ class User
2
+
3
+ attr_accessor :authenticated, :expires_at
4
+
5
+ def self.oauthable
6
+ true
7
+ end
8
+
9
+ def id
10
+ 1
11
+ end
12
+
13
+ def oauth_hash
14
+ {
15
+ access_token: authenticated ? 'test access token' : nil,
16
+ refresh_token: authenticated ? 'test refresh token' : nil,
17
+ expires_at: expires_at || (authenticated ? Time.now + 3600 : nil)
18
+ }
19
+ end
20
+
21
+ def update_access_token!(access_token)
22
+ end
23
+
24
+ def google
25
+ end
26
+
27
+ end