lib/dbox/api.rb in dbox-0.6.7 vs lib/dbox/api.rb in dbox-0.6.8

- old
+ new

@@ -1,6 +1,9 @@ module Dbox + NUM_TRIES = 3 + TIME_BETWEEN_TRIES = 3 # in seconds + class ConfigurationError < RuntimeError; end class ServerError < RuntimeError; end class RemoteMissing < RuntimeError; end class RemoteAlreadyExists < RuntimeError; end class RequestDenied < RuntimeError; end @@ -60,20 +63,28 @@ @session = DropboxSession.new(app_key, app_secret) @session.set_access_token(auth_key, auth_secret) @client = DropboxClient.new(@session, 'dropbox') end - def run(path) + def run(path, tries = NUM_TRIES, &proc) begin - res = yield + res = proc.call handle_response(path, res) { raise RuntimeError, "Unexpected result: #{res.inspect}" } rescue DropboxNotModified => e :not_modified rescue DropboxAuthError => e raise e rescue DropboxError => e - handle_response(path, e.http_response) { raise ServerError, "Server error -- might be a hiccup, please try your request again (#{e.message})" } + if e.http_response.kind_of?(Net::HTTPServiceUnavailable) && tries > 0 + log.info "Encountered 503 on #{path} (likely rate limiting). Sleeping #{TIME_BETWEEN_TRIES}s and trying again." + # TODO check for "Retry-After" header and use that for sleep instead of TIME_BETWEEN_TRIES + log.debug "Headers: #{e.http_response.to_hash.inspect}" + sleep TIME_BETWEEN_TRIES + run(path, tries - 1, &proc) + else + handle_response(path, e.http_response) { raise ServerError, "Server error -- might be a hiccup, please try your request again (#{e.message})" } + end end end def handle_response(path, res, &else_proc) case res @@ -93,21 +104,21 @@ else_proc.call() end end def metadata(path = "/", hash = nil, list=true) - log.debug "Fetching metadata for #{path}" run(path) do + log.debug "Fetching metadata for #{path}" res = @client.metadata(path, 10000, list, hash) log.debug res.inspect res end end def create_dir(path) - log.info "Creating #{path}" run(path) do + log.info "Creating #{path}" begin @client.file_create_folder(path) rescue DropboxError => e if e.http_response.kind_of?(Net::HTTPForbidden) raise RemoteAlreadyExists, "Either the directory at #{path} already exists, or it has invalid characters in the name" @@ -117,21 +128,23 @@ end end end def delete_dir(path) - log.info "Deleting #{path}" run(path) do + log.info "Deleting #{path}" @client.file_delete(path) end end def get_file(path, file_obj, stream=false) - log.info "Downloading #{path}" unless stream # just download directly using the get_file API - res = run(path) { @client.get_file(path) } + res = run(path) do + log.info "Downloading #{path}" + @client.get_file(path) + end if res.kind_of?(String) file_obj << res true else raise DropboxError.new("Invalid response #{res.inspect}") @@ -140,33 +153,34 @@ # use the media API to get a URL that we can stream from, and # then stream the file to disk res = run(path) { @client.media(path) } url = res[:url] if res && res.kind_of?(Hash) if url + log.info "Downloading #{path}" streaming_download(url, file_obj) else get_file(path, file_obj, false) end end end - def put_file(path, file_obj, previous_revision=nil) - log.info "Uploading #{path}" + def put_file(path, local_path, previous_revision=nil) run(path) do - @client.put_file(path, file_obj, false, previous_revision) + log.info "Uploading #{path}" + File.open(local_path, "r") {|f| @client.put_file(path, f, false, previous_revision) } end end def delete_file(path) - log.info "Deleting #{path}" run(path) do + log.info "Deleting #{path}" @client.file_delete(path) end end def move(old_path, new_path) - log.info "Moving #{old_path} to #{new_path}" run(old_path) do + log.info "Moving #{old_path} to #{new_path}" begin @client.file_move(old_path, new_path) rescue DropboxError => e if e.http_response.kind_of?(Net::HTTPForbidden) raise RemoteAlreadyExists, "Error during move -- there may already be a Dropbox folder at #{new_path}"