Skip to content

Commit

Permalink
Add upload session methods and UploadSessionCursor class
Browse files Browse the repository at this point in the history
  • Loading branch information
waits committed Jun 28, 2016
1 parent 039b46a commit 49f26e1
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 44 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ gem 'dropbox-sdk-v2'

## Usage

Also see the [full method documentation](http://www.rubydoc.info/gems/dropbox-sdk-v2/Dropbox/Client).
Also see the [full YARD documentation](http://www.rubydoc.info/github/waits/dropbox-sdk-ruby).

Set up a client:
```ruby
Expand All @@ -48,16 +48,16 @@ folder.path_lower # => "/myfolder"

Upload a file:
```ruby
file = dbx.upload('/myfolder/file.txt', 'the file contents') # Accepts a String or File
file.class # => Dropbox::FileMetadata
file.size # => 17
# File body can be a String, File, or any Enumerable.
file = dbx.upload('/myfolder/file.txt', 'file body') # => Dropbox::FileMetadata
file.size # => 9
file.rev # => a1c10ce0dd78
```

Download a file:
```ruby
file, body = dbx.download('/myfolder/file.txt') # => Dropbox::FileMetadata, HTTP::Response::Body
body.to_s # => "the file contents"
body.to_s # => "file body"
```

Delete a file:
Expand Down
8 changes: 6 additions & 2 deletions lib/dropbox.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@
require_relative 'dropbox/errors'
require_relative 'dropbox/account'
require_relative 'dropbox/metadata'
require_relative 'dropbox/upload_session_cursor'

module Dropbox
API = 'https://api.dropboxapi.com/2'
CONTENT_API = 'https://content.dropboxapi.com/2'
# The main API endpoint used for most calls.
API = 'https://api.dropboxapi.com/2'.freeze

# The content API endpoint used for upload/download calls.
CONTENT_API = 'https://content.dropboxapi.com/2'.freeze
end
60 changes: 48 additions & 12 deletions lib/dropbox/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
module Dropbox
# Client contains all the methods that map to the Dropbox API endpoints.
class Client
# Initialize a new client.
#
# @param [String] access_token
def initialize(access_token)
unless access_token =~ /^[a-z0-9_-]{64}$/i
Expand All @@ -18,7 +16,7 @@ def initialize(access_token)

# Check the status of a save_url job.
#
# @param[String] async_job_id
# @param [String] async_job_id
# @return [nil] if the job is still in progress.
# @return [Dropbox::FileMetadata] if the job is complete.
# @return [String] an error message, if the job failed.
Expand Down Expand Up @@ -248,15 +246,53 @@ def search(path, query, start=0, max_results=100, mode='filename')
#
# @param [String] path
# @param [String, Enumerable] body
# @param [String] mode
# @param [Boolean] autorename
# @param [String, Time] client_modified
# @param [Boolean] mute
# @option options [String] :mode
# @option options [Boolean] :autorename
# @option options [Boolean] :mute
# @return [Dropbox::FileMetadata]
def upload(path, body, options={})
options[:client_modified] = Time.now.utc.iso8601
options[:path] = path
resp = upload_request('/files/upload', body, options.merge(path: path))
FileMetadata.new(resp)
end

# Start an upload session to upload a file using multiple requests.
#
# @param [String, Enumerable] body
# @param [Boolean] close
# @return [Dropbox::UploadSessionCursor] cursor
def start_upload_session(body, close=false)
resp = upload_request('/files/upload_session/start', body, close: close)
UploadSessionCursor.new(resp['session_id'], body.length)
end

# Append more data to an upload session.
#
# @param [Dropbox::UploadSessionCursor] cursor
# @param [String, Enumerable] body
# @param [Boolean] close
# @return [Dropbox::UploadSessionCursor] cursor
def append_upload_session(cursor, body, close=false)
args = {cursor: cursor.to_h, close: close}
resp = upload_request('/files/upload_session/append_v2', body, args)
cursor.offset += body.length
cursor
end

# Finish an upload session and save the uploaded data to the given file path.
#
# @param [Dropbox::UploadSessionCursor] cursor
# @param [String] path
# @param [String, Enumerable] body
# @param [Hash] options
# @option (see #upload)
# @return [Dropbox::FileMetadata]
def upload(path, body, mode='add', autorename=false, client_modified=nil, mute=false)
client_modified = client_modified.iso8601 if client_modified.is_a?(Time)
resp = upload_request('/files/upload', body, path: path, mode: mode,
autorename: autorename, client_modified: client_modified, mute: mute)
def finish_upload_session(cursor, path, body, options={})
options[:client_modified] = Time.now.utc.iso8601
options[:path] = path
args = {cursor: cursor.to_h, commit: options}
resp = upload_request('/files/upload_session/finish', body, args)
FileMetadata.new(resp)
end

Expand Down Expand Up @@ -321,7 +357,7 @@ def upload_request(action, body, args={})
}).post(CONTENT_API + action, body: body)

raise APIError.new(resp) if resp.code != 200
JSON.parse(resp.to_s)
JSON.parse(resp.to_s) unless resp.to_s == 'null'
end
end
end
22 changes: 22 additions & 0 deletions lib/dropbox/upload_session_cursor.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
module Dropbox
# UploadSessionCursor holds information about an in-progress upload session.
#
# @attr [String] session_id A unique identifier for the session.
# @attr [Integer] offset The size of the data uploaded so far.
class UploadSessionCursor
attr_reader :session_id
attr_accessor :offset

# @param [String] session_id
# @param [Integer] offset
def initialize(session_id, offset)
@session_id = session_id
@offset = offset
end

# @return [Hash]
def to_h
{session_id: session_id, offset: offset}
end
end
end
25 changes: 16 additions & 9 deletions test/test_files.rb
Original file line number Diff line number Diff line change
Expand Up @@ -249,26 +249,33 @@ def test_search_error
end

def test_upload_string
modified = Time.parse('2007-07-07T00:00:00Z')
file = @client.upload('/uploaded_string.txt', 'dropbox', 'overwrite', false, modified)
now = Time.now.utc
file = @client.upload('/uploaded_string.txt', 'dropbox', mode: 'overwrite',
autorename: false)

assert file.is_a?(Dropbox::FileMetadata)
assert_equal 7, file.size
assert_equal modified, file.client_modified
assert_instance_of(Dropbox::FileMetadata, file)
assert_equal(7, file.size)
assert_in_delta(now.to_i, file.client_modified.to_i, 5, "Expected #{file.client_modified} to be near #{now}")

@client.delete('/uploaded_string.txt')
end

def test_upload_file
File.open('LICENSE') do |f|
meta = @client.upload('/license.txt', f, 'overwrite')
now = Time.now.utc
meta = @client.upload('/license.txt', f, mode: 'overwrite')

assert meta.is_a?(Dropbox::FileMetadata)
assert_equal 1078, meta.size
assert_instance_of(Dropbox::FileMetadata, meta)
assert_equal(1078, meta.size)
assert_in_delta(now.to_i, meta.client_modified.to_i, 5, "Expected #{meta.client_modified} to be near #{now}")
end

@client.delete('/license.txt')
end

def test_upload_conflict
assert_raises(Dropbox::APIError) do
@client.upload('/uploaded_file.txt', 'dropbocks', 'add', false)
@client.upload('/uploaded_file.txt', 'dropbocks', mode: 'add', autorename: false)
end
end
end
43 changes: 27 additions & 16 deletions test/test_integration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,23 @@ def teardown

def test_files
file = @client.upload(@box.path_lower + '/file.txt', "The file contents.\n")
assert file.is_a?(Dropbox::FileMetadata)
assert_instance_of Dropbox::FileMetadata, file
assert_equal file.name, 'file.txt'
assert_equal 19, file.size
assert_in_delta Time.now.to_i, file.client_modified.to_i, 5

metadata, body = @client.download(file.path_lower)
assert_equal file, metadata
assert_equal "The file contents.\n", body.to_s

metadata, link = @client.get_temporary_link(file.path_lower)
assert metadata.is_a?(Dropbox::FileMetadata)
assert_instance_of Dropbox::FileMetadata, metadata
assert_match 'https://dl.dropboxusercontent.com/apitl/1', link

@client.delete(file.path_lower)
revs, is_deleted = @client.list_revisions(file.path_lower)
assert revs.length > 0
assert revs[0].is_a?(Dropbox::FileMetadata)
assert_instance_of Dropbox::FileMetadata, revs[0]
assert_equal true, is_deleted

restored = @client.restore(file.path_lower, file.rev)
Expand All @@ -40,36 +41,29 @@ def test_files
while status == nil
status = @client.check_save_url_job_status(job_id)
end
assert status.is_a?(Dropbox::FileMetadata)
assert_instance_of Dropbox::FileMetadata, status
assert_equal 'robots.txt', status.name

job_id = @client.save_url('/nothing.txt', 'https://www.google.com/404')
status = nil
while status == nil
status = @client.check_save_url_job_status(job_id)
end
assert status.is_a?(String)
end

def test_folders
folder = @client.create_folder(@box.path_lower + '/folder')
assert folder.is_a?(Dropbox::FolderMetadata)
assert_instance_of Dropbox::FolderMetadata, folder

subfolder = @client.create_folder(folder.path_lower + '/subfolder')
assert subfolder.is_a?(Dropbox::FolderMetadata)
assert_instance_of Dropbox::FolderMetadata, subfolder

copied = @client.copy(folder.path_lower, @box.path_lower + '/copied_folder')
assert copied.is_a?(Dropbox::FolderMetadata)
assert_instance_of Dropbox::FolderMetadata, copied
assert_equal @box.path_lower + '/copied_folder', copied.path_lower

moved = @client.move(folder.path_lower, @box.path_lower + '/moved_folder')
assert moved.is_a?(Dropbox::FolderMetadata)
assert_instance_of Dropbox::FolderMetadata, moved
assert_equal @box.path_lower + '/moved_folder', moved.path_lower
@client.move(moved.path_lower, folder.path_lower)

entries = @client.list_folder(folder.path_lower)
assert_equal 1, entries.length
assert entries[0].is_a?(Dropbox::FolderMetadata)
assert_instance_of Dropbox::FolderMetadata, entries[0]

matches = @client.search(@box.path_lower, 'nothing')
assert_equal 0, matches.length
Expand All @@ -78,4 +72,21 @@ def test_folders
assert_equal folder, deleted_folder
assert_raises(Dropbox::APIError) { @client.delete(folder.path_lower) }
end

def test_upload_session
cursor = @client.start_upload_session("Upload session part 1\n")
assert_instance_of Dropbox::UploadSessionCursor, cursor
assert cursor.session_id.is_a?(String)
assert_equal 22, cursor.offset

cursor = @client.append_upload_session(cursor, "Upload session part 2\n", false)
assert_equal 44, cursor.offset

cursor = @client.append_upload_session(cursor, "Upload session part 3\n", false)
assert_equal 66, cursor.offset

file = @client.finish_upload_session(cursor, "/large_file.txt", "Finished\n")
assert_instance_of Dropbox::FileMetadata, file
assert_equal 75, file.size
end
end
13 changes: 13 additions & 0 deletions test/test_upload_session_cursor.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
require 'minitest/autorun'
require 'dropbox'

class DropboxUploadSessionCursorTest < Minitest::Test
def test_upload_session_cursor
cursor = Dropbox::UploadSessionCursor.new('id:123', 10)
hash = cursor.to_h
correct = {session_id: 'id:123', offset: 10}

assert_instance_of Hash, hash
assert_equal correct, hash
end
end

0 comments on commit 49f26e1

Please sign in to comment.