dropbox-api-petems 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,11 @@
1
+ module Dropbox
2
+ module API
3
+ class Delta
4
+ attr_reader :cursor, :entries
5
+ def initialize(cursor, entries)
6
+ @cursor = cursor
7
+ @entries = entries
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,20 @@
1
+ module Dropbox
2
+ module API
3
+
4
+ class Dir < Dropbox::API::Object
5
+
6
+ include Dropbox::API::Fileops
7
+
8
+ def ls(path_to_list = '')
9
+ data = client.raw.metadata :path => path + path_to_list
10
+ if data['is_dir']
11
+ Dropbox::API::Object.convert(data.delete('contents') || [], client)
12
+ else
13
+ [Dropbox::API::Object.convert(data, client)]
14
+ end
15
+ end
16
+
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,44 @@
1
+ module Dropbox
2
+ module API
3
+
4
+ class File < Dropbox::API::Object
5
+
6
+ include Dropbox::API::Fileops
7
+
8
+ def revisions(options = {})
9
+ response = client.raw.revisions({ :path => self.path }.merge(options))
10
+ Dropbox::API::Object.convert(response, client)
11
+ end
12
+
13
+ def restore(rev, options = {})
14
+ response = client.raw.restore({ :rev => rev, :path => self.path }.merge(options))
15
+ self.update response
16
+ end
17
+
18
+ def share_url(options = {})
19
+ response = client.raw.shares({ :path => self.path }.merge(options))
20
+ Dropbox::API::Object.init(response, client)
21
+ end
22
+
23
+ def direct_url(options = {})
24
+ response = client.raw.media({ :path => self.path }.merge(options))
25
+ Dropbox::API::Object.init(response, client)
26
+ end
27
+
28
+ def thumbnail(options = {})
29
+ client.raw.thumbnails({ :path => self.path }.merge(options))
30
+ end
31
+
32
+ def copy_ref(options = {})
33
+ response = client.raw.copy_ref({ :path => self.path }.merge(options))
34
+ Dropbox::API::Object.init(response, client)
35
+ end
36
+
37
+ def download
38
+ client.download(self.path)
39
+ end
40
+
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,28 @@
1
+ module Dropbox
2
+ module API
3
+
4
+ module Fileops
5
+
6
+ def copy(to, options = {})
7
+ response = client.raw.copy({ :from_path => self.path, :to_path => to }.merge(options))
8
+ self.update response
9
+ end
10
+
11
+ def move(to, options = {})
12
+ response = client.raw.move({ :from_path => self.path, :to_path => to }.merge(options))
13
+ self.update response
14
+ end
15
+
16
+ def destroy(options = {})
17
+ response = client.raw.delete({ :path => self.path }.merge(options))
18
+ self.update response
19
+ end
20
+
21
+ def path
22
+ self['path'].sub(/^\//, '')
23
+ end
24
+
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,39 @@
1
+ module Dropbox
2
+ module API
3
+
4
+ class Object < Hashie::Mash
5
+ attr_accessor :client
6
+
7
+ def self.init(response, client)
8
+ instance = self.new(response)
9
+ instance.client = client
10
+ instance
11
+ end
12
+
13
+ def self.resolve_class(hash)
14
+ hash['is_dir'] == true ? Dropbox::API::Dir : Dropbox::API::File
15
+ end
16
+
17
+ def self.convert(array_or_object, client)
18
+ if array_or_object.is_a?(Array)
19
+ array_or_object.collect do |item|
20
+ resolve_class(item).init(item, client)
21
+ end
22
+ else
23
+ resolve_class(array_or_object).init(array_or_object, client)
24
+ end
25
+ end
26
+
27
+ # Kill off the ability for recursive conversion
28
+ def deep_update(other_hash)
29
+ other_hash.each_pair do |k,v|
30
+ key = convert_key(k)
31
+ regular_writer(key, convert_value(v, true))
32
+ end
33
+ self
34
+ end
35
+
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,48 @@
1
+ module Dropbox
2
+ module API
3
+
4
+ class Tasks
5
+
6
+ extend Rake::DSL if defined? Rake::DSL
7
+
8
+ def self.install
9
+
10
+ namespace :dropbox do
11
+ desc "Authorize wizard for Dropbox API"
12
+ task :authorize do
13
+ require "dropbox-api"
14
+ require "cgi"
15
+ print "Enter consumer key: "
16
+ consumer_key = $stdin.gets.chomp
17
+ print "Enter consumer secret: "
18
+ consumer_secret = $stdin.gets.chomp
19
+
20
+ Dropbox::API::Config.app_key = consumer_key
21
+ Dropbox::API::Config.app_secret = consumer_secret
22
+
23
+ consumer = Dropbox::API::OAuth.consumer(:authorize)
24
+ request_token = consumer.get_request_token
25
+ puts "\nGo to this url and click 'Authorize' to get the token:"
26
+ puts request_token.authorize_url
27
+ query = request_token.authorize_url.split('?').last
28
+ params = CGI.parse(query)
29
+ token = params['oauth_token'].first
30
+ print "\nOnce you authorize the app on Dropbox, press enter... "
31
+ $stdin.gets.chomp
32
+
33
+ access_token = request_token.get_access_token(:oauth_verifier => token)
34
+
35
+ puts "\nAuthorization complete!:\n\n"
36
+ puts " Dropbox::API::Config.app_key = '#{consumer.key}'"
37
+ puts " Dropbox::API::Config.app_secret = '#{consumer.secret}'"
38
+ puts " client = Dropbox::API::Client.new(:token => '#{access_token.token}', :secret => '#{access_token.secret}')"
39
+ puts "\n"
40
+ end
41
+ end
42
+
43
+ end
44
+
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,27 @@
1
+ module Dropbox
2
+ module API
3
+
4
+ module Config
5
+
6
+ class << self
7
+ attr_accessor :endpoints
8
+ attr_accessor :prefix
9
+ attr_accessor :app_key
10
+ attr_accessor :app_secret
11
+ attr_accessor :mode
12
+ end
13
+
14
+ self.endpoints = {
15
+ :main => "https://api.dropbox.com",
16
+ :content => "https://api-content.dropbox.com",
17
+ :authorize => "https://www.dropbox.com"
18
+ }
19
+ self.prefix = "/1"
20
+ self.app_key = nil
21
+ self.app_secret = nil
22
+ self.mode = 'sandbox'
23
+
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,20 @@
1
+ module Dropbox
2
+ module API
3
+
4
+ class Error < StandardError
5
+
6
+ class BadInput < Error; end
7
+ class ConnectionFailed < Error; end
8
+ class Config < Error; end
9
+ class Unauthorized < Error; end
10
+ class Forbidden < Error; end
11
+ class NotFound < Error; end
12
+ class Redirect < Error; end
13
+ class WrongMethod < Error; end
14
+ class RateLimit < Error; end
15
+ class StorageQuota < Error; end
16
+
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,29 @@
1
+ module Dropbox
2
+ module API
3
+
4
+ module OAuth
5
+
6
+ class << self
7
+
8
+ def consumer(endpoint)
9
+ if !Dropbox::API::Config.app_key or !Dropbox::API::Config.app_secret
10
+ raise Dropbox::API::Error::Config.new("app_key or app_secret not provided")
11
+ end
12
+ ::OAuth::Consumer.new(Dropbox::API::Config.app_key, Dropbox::API::Config.app_secret,
13
+ :site => Dropbox::API::Config.endpoints[endpoint],
14
+ :request_token_path => Dropbox::API::Config.prefix + "/oauth/request_token",
15
+ :authorize_path => Dropbox::API::Config.prefix + "/oauth/authorize",
16
+ :access_token_path => Dropbox::API::Config.prefix + "/oauth/access_token")
17
+ end
18
+
19
+ def access_token(consumer, options = {})
20
+ ::OAuth::AccessToken.new(consumer, options[:token], options[:secret])
21
+ end
22
+
23
+ end
24
+
25
+ end
26
+
27
+ end
28
+ end
29
+
@@ -0,0 +1,27 @@
1
+ module Dropbox
2
+ module API
3
+
4
+ module Util
5
+
6
+ class << self
7
+
8
+ def escape(string)
9
+ string.gsub(/([^ a-zA-Z0-9\.\\\-\/\_]+)/) do
10
+ '%' + $1.unpack('H2' * $1.bytesize).join('%').upcase
11
+ end.gsub(' ', '%20')
12
+ end
13
+
14
+ def query(data)
15
+ data.inject([]) { |memo, entry| memo.push(entry.join('=')); memo }.join('&')
16
+ end
17
+
18
+ def remove_double_slashes(path)
19
+ path.gsub('//', '/')
20
+ end
21
+
22
+ end
23
+
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,5 @@
1
+ module Dropbox
2
+ module API
3
+ VERSION = "0.4.1"
4
+ end
5
+ end
@@ -0,0 +1,22 @@
1
+ require "oauth"
2
+ require "multi_json"
3
+ require "hashie"
4
+
5
+ module Dropbox
6
+ module API
7
+
8
+ end
9
+ end
10
+
11
+ require "dropbox-api/version"
12
+ require "dropbox-api/util/config"
13
+ require "dropbox-api/util/oauth"
14
+ require "dropbox-api/util/error"
15
+ require "dropbox-api/util/util"
16
+ require "dropbox-api/objects/object"
17
+ require "dropbox-api/objects/fileops"
18
+ require "dropbox-api/objects/file"
19
+ require "dropbox-api/objects/dir"
20
+ require "dropbox-api/objects/delta"
21
+ require "dropbox-api/connection"
22
+ require "dropbox-api/client"
@@ -0,0 +1,5 @@
1
+ app_key: # CONSUMER KEY
2
+ app_secret: # CONSUMER SECRET
3
+ token: # ACCESS TOKEN
4
+ secret: # ACCESS SECRET
5
+ mode: # 'sandbox' or 'dropbox'
Binary file
@@ -0,0 +1,201 @@
1
+ # encoding: utf-8
2
+ require "spec_helper"
3
+
4
+ describe Dropbox::API::Client do
5
+
6
+ before do
7
+ # pending
8
+ @client = Dropbox::Spec.instance
9
+ end
10
+
11
+ describe "#initialize" do
12
+
13
+ it "has a handle to the connection" do
14
+ @client.connection.should be_an_instance_of(Dropbox::API::Connection)
15
+ end
16
+
17
+ end
18
+
19
+ describe "#account" do
20
+
21
+ it "retrieves the account object" do
22
+ response = @client.account
23
+ response.should be_an_instance_of(Dropbox::API::Object)
24
+ end
25
+
26
+ end
27
+
28
+ describe "#find" do
29
+
30
+ before do
31
+ @filename = "#{Dropbox::Spec.test_dir}/spec-find-file-test-#{Time.now.to_i}.txt"
32
+ @file = @client.upload @filename, "spec file"
33
+
34
+ @dirname = "#{Dropbox::Spec.test_dir}/spec-find-dir-test-#{Time.now.to_i}"
35
+ @dir = @client.mkdir @dirname
36
+ end
37
+
38
+ it "returns a single file" do
39
+ response = @client.find(@filename)
40
+ response.path.should == @file.path
41
+ response.should be_an_instance_of(Dropbox::API::File)
42
+ end
43
+
44
+ it "returns a single directory" do
45
+ response = @client.find(@dirname)
46
+ response.path.should == @dir.path
47
+ response.should be_an_instance_of(Dropbox::API::Dir)
48
+ end
49
+
50
+ end
51
+
52
+ describe "#ls" do
53
+
54
+ it "returns an array of files and dirs" do
55
+ result = @client.ls
56
+ result.should be_an_instance_of(Array)
57
+ end
58
+
59
+ it "returns a single item array of if we ls a file" do
60
+ result = @client.ls(Dropbox::Spec.test_dir)
61
+ first_file = result.detect { |f| f.class == Dropbox::API::File }
62
+ result = @client.ls first_file.path
63
+ result.should be_an_instance_of(Array)
64
+ end
65
+
66
+ end
67
+
68
+ describe "#mkdir" do
69
+
70
+ it "returns an array of files and dirs" do
71
+ dirname = "#{Dropbox::Spec.test_dir}/test-dir-#{Dropbox::Spec.namespace}"
72
+ response = @client.mkdir dirname
73
+ response.path.should == dirname
74
+ response.should be_an_instance_of(Dropbox::API::Dir)
75
+ end
76
+
77
+ it "creates dirs with tricky characters" do
78
+ dirname = "#{Dropbox::Spec.test_dir}/test-dir |!@\#$%^&*{b}[].;'.,<>?: #{Dropbox::Spec.namespace}"
79
+ response = @client.mkdir dirname
80
+ response.path.should == dirname.gsub(/[\\\:\?\*\<\>\"\|]+/, '')
81
+ response.should be_an_instance_of(Dropbox::API::Dir)
82
+ end
83
+
84
+ it "creates dirs with utf8 characters" do
85
+ dirname = "#{Dropbox::Spec.test_dir}/test-dir łółą #{Dropbox::Spec.namespace}"
86
+ response = @client.mkdir dirname
87
+ response.path.should == dirname
88
+ response.should be_an_instance_of(Dropbox::API::Dir)
89
+ end
90
+
91
+ end
92
+
93
+ describe "#upload" do
94
+
95
+ it "puts the file in dropbox" do
96
+ filename = "#{Dropbox::Spec.test_dir}/test-#{Dropbox::Spec.namespace}.txt"
97
+ response = @client.upload filename, "Some file"
98
+ response.path.should == filename
99
+ response.bytes.should == 9
100
+ end
101
+
102
+ it "uploads the file with tricky characters" do
103
+ filename = "#{Dropbox::Spec.test_dir}/test ,|!@\#$%^&*{b}[].;'.,<>?:-#{Dropbox::Spec.namespace}.txt"
104
+ response = @client.upload filename, "Some file"
105
+ response.path.should == filename
106
+ response.bytes.should == 9
107
+ end
108
+
109
+ it "uploads the file with utf8" do
110
+ filename = "#{Dropbox::Spec.test_dir}/test łołąó-#{Dropbox::Spec.namespace}.txt"
111
+ response = @client.upload filename, "Some file"
112
+ response.path.should == filename
113
+ response.bytes.should == 9
114
+ end
115
+ end
116
+
117
+ describe "#search" do
118
+
119
+ let(:term) { "searchable-test-#{Dropbox::Spec.namespace}" }
120
+
121
+ before do
122
+ filename = "#{Dropbox::Spec.test_dir}/searchable-test-#{Dropbox::Spec.namespace}.txt"
123
+ @client.upload filename, "Some file"
124
+ end
125
+
126
+ after do
127
+ @response.size.should == 1
128
+ @response.first.class.should == Dropbox::API::File
129
+ end
130
+
131
+ it "finds a file" do
132
+ @response = @client.search term, :path => "#{Dropbox::Spec.test_dir}"
133
+ end
134
+
135
+ it "works if leading slash is present in path" do
136
+ @response = @client.search term, :path => "/#{Dropbox::Spec.test_dir}"
137
+ end
138
+
139
+ end
140
+
141
+ describe "#copy_from_copy_ref" do
142
+
143
+ it "copies a file from a copy_ref" do
144
+ filename = "test/searchable-test-#{Dropbox::Spec.namespace}.txt"
145
+ @client.upload filename, "Some file"
146
+ response = @client.search "searchable-test-#{Dropbox::Spec.namespace}", :path => 'test'
147
+ ref = response.first.copy_ref['copy_ref']
148
+ @client.copy_from_copy_ref ref, "#{filename}.copied"
149
+ response = @client.search "searchable-test-#{Dropbox::Spec.namespace}.txt.copied", :path => 'test'
150
+ response.size.should == 1
151
+ response.first.class.should == Dropbox::API::File
152
+ end
153
+
154
+ end
155
+
156
+ describe "#download" do
157
+
158
+ it "downloads a file from Dropbox" do
159
+ @client.upload "#{Dropbox::Spec.test_dir}/test.txt", "Some file"
160
+ file = @client.download "#{Dropbox::Spec.test_dir}/test.txt"
161
+ file.should == "Some file"
162
+ end
163
+
164
+ it "raises a 404 when a file is not found in Dropbox" do
165
+ lambda {
166
+ @client.download "#{Dropbox::Spec.test_dir}/no.txt"
167
+ }.should raise_error(Dropbox::API::Error::NotFound)
168
+ end
169
+
170
+ end
171
+
172
+ describe "#delta" do
173
+ it "returns a cursor and list of files" do
174
+ filename = "#{Dropbox::Spec.test_dir}/delta-test-#{Dropbox::Spec.namespace}.txt"
175
+ @client.upload filename, 'Some file'
176
+ response = @client.delta
177
+ cursor, files = response.cursor, response.entries
178
+ cursor.should be_an_instance_of(String)
179
+ files.should be_an_instance_of(Array)
180
+ files.last.should be_an_instance_of(Dropbox::API::File)
181
+ end
182
+
183
+ it "returns the files that have changed since the cursor was made" do
184
+ filename = "#{Dropbox::Spec.test_dir}/delta-test-#{Dropbox::Spec.namespace}.txt"
185
+ delete_filename = "#{Dropbox::Spec.test_dir}/delta-test-delete-#{Dropbox::Spec.namespace}.txt"
186
+ @client.upload delete_filename, 'Some file'
187
+ response = @client.delta
188
+ cursor, files = response.cursor, response.entries
189
+ files.last.path.should == delete_filename
190
+ files.last.destroy
191
+ @client.upload filename, 'Another file'
192
+ response = @client.delta(cursor)
193
+ cursor, files = response.cursor, response.entries
194
+ files.length.should == 2
195
+ files.first.is_deleted.should == true
196
+ files.first.path.should == delete_filename
197
+ files.last.path.should == filename
198
+ end
199
+ end
200
+
201
+ end
@@ -0,0 +1,129 @@
1
+ require "spec_helper"
2
+
3
+ describe Dropbox::API::Connection do
4
+
5
+ before do
6
+ @connection = Dropbox::API::Connection.new(:token => Dropbox::Spec.token,
7
+ :secret => Dropbox::Spec.secret)
8
+ end
9
+
10
+ describe "#request" do
11
+
12
+ it "returns a parsed response when the response is a 200" do
13
+ response = double :code => 200, :body => '{ "a":1}'
14
+ response = @connection.request { response }
15
+ response.should be_an_instance_of(Hash)
16
+ end
17
+
18
+ it "raises a Dropbox::API::Error::Unauthorized when the response is a 401" do
19
+ response = double :code => 401, :body => '{ "a":1}'
20
+ lambda do
21
+ @connection.request { response }
22
+ end.should raise_error(Dropbox::API::Error::Unauthorized, '401 - Bad or expired token')
23
+ end
24
+
25
+ it "raises a Dropbox::API::Error::Forbidden when the response is a 403" do
26
+ response = double :code => 403, :body => '{ "a":1}'
27
+ lambda do
28
+ @connection.request { response }
29
+ end.should raise_error(Dropbox::API::Error::Forbidden, '403 - Bad OAuth request')
30
+ end
31
+
32
+ it "raises a Dropbox::API::Error::NotFound when the response is a 404" do
33
+ response = double :code => 404, :body => '{ "a":1}'
34
+ lambda do
35
+ @connection.request { response }
36
+ end.should raise_error(Dropbox::API::Error::NotFound, '404 - Not found')
37
+ end
38
+
39
+ it "raises a Dropbox::API::Error::WrongMethod when the response is a 405" do
40
+ response = double :code => 405, :body => '{ "error": "The requested method GET is not allowed for the URL /foo/." }'
41
+ lambda do
42
+ @connection.request { response }
43
+ end.should raise_error(Dropbox::API::Error::WrongMethod, '405 - Request method not expected - The requested method GET is not allowed for the URL /foo/.')
44
+ end
45
+
46
+ it "raises a Dropbox::API::Error when the response is a 3xx" do
47
+ response = double :code => 301, :body => '{ "a":1}'
48
+ lambda do
49
+ @connection.request { response }
50
+ end.should raise_error(Dropbox::API::Error::Redirect, '301 - Redirect Error')
51
+ end
52
+
53
+ it "raises a Dropbox::API::Error when the response is a 5xx" do
54
+ response = double :code => 500, :body => '{ "a":1}'
55
+ lambda do
56
+ @connection.request { response }
57
+ end.should raise_error(Dropbox::API::Error, '500 - Server error. Check http://status.dropbox.com/')
58
+ end
59
+
60
+ it "raises a Dropbox::API::Error when the response is a 400" do
61
+ response = double :code => 400, :body => '{ "error": "bad request foo" }'
62
+ lambda do
63
+ @connection.request { response }
64
+ end.should raise_error(Dropbox::API::Error::BadInput, '400 - Bad input parameter - bad request foo')
65
+ end
66
+
67
+ it "raises a Dropbox::API::Error when the response is a 406" do
68
+ response = double :code => 406, :body => '{ "error": "bad request bar" }'
69
+ lambda do
70
+ @connection.request { response }
71
+ end.should raise_error(Dropbox::API::Error, '406 - bad request bar')
72
+ end
73
+
74
+ it "raises a Dropbox::API::Error when the response is a 406" do
75
+ response = double :code => 429, :body => '{ "error": "rate limited" }'
76
+ lambda do
77
+ @connection.request { response }
78
+ end.should raise_error(Dropbox::API::Error::RateLimit, '429 - Rate Limiting in affect')
79
+ end
80
+
81
+ it "raises a Dropbox::API::Error when the response is a 503" do
82
+ response = double :code => 503, :body => '{ "error": "rate limited" }'
83
+ lambda do
84
+ @connection.request { response }
85
+ end.should raise_error(Dropbox::API::Error, '503 - Possible Rate Limiting: rate limited')
86
+ end
87
+
88
+ it "raises a Dropbox::API::Error::StorageQuota when the response is a 507" do
89
+ response = double :code => 507, :body => '{ "error": "quote limit" }'
90
+ lambda do
91
+ @connection.request { response }
92
+ end.should raise_error(Dropbox::API::Error, '507 - Dropbox storage quota exceeded.')
93
+ end
94
+
95
+
96
+ it "returns the raw response if :raw => true is provided" do
97
+ response = double :code => 200, :body => '{ "something": "more" }'
98
+ response = @connection.request(:raw => true) { response }
99
+ response.should == '{ "something": "more" }'
100
+ end
101
+
102
+ end
103
+
104
+ describe "#consumer" do
105
+
106
+ it "returns an appropriate consumer object" do
107
+ @connection.consumer(:main).should be_a(::OAuth::Consumer)
108
+ end
109
+
110
+ end
111
+
112
+ describe "errors" do
113
+
114
+ it "recovers error with rescue statement modifier" do
115
+ expect { raise Dropbox::API::Error rescue nil }.to_not raise_error
116
+ end
117
+
118
+ it "recovers any kind of errors with the generic error" do
119
+ expect do
120
+ begin
121
+ raise Dropbox::API::Error::Forbidden
122
+ rescue Dropbox::API::Error
123
+ end
124
+ end.to_not raise_error
125
+ end
126
+
127
+ end
128
+
129
+ end