class Dbox::API

Attributes

client[R]

Public Class Methods

authorize() click to toggle source
# File lib/dbox/api.rb, line 14
def self.authorize
  app_key = ENV["DROPBOX_APP_KEY"]
  app_secret = ENV["DROPBOX_APP_SECRET"]
  
  raise(ConfigurationError, "Please set the DROPBOX_APP_KEY environment variable to a Dropbox application key") unless app_key
  raise(ConfigurationError, "Please set the DROPBOX_APP_SECRET environment variable to a Dropbox application secret") unless app_secret


  flow = DropboxOAuth2FlowNoRedirect.new(app_key, app_secret)
  authorize_url = flow.start()

  puts '1. Go to: ' + authorize_url
  puts '2. Click "Allow" (you might have to log in first)'
  puts '3. Copy the authorization code'
  print 'Enter the authorization code here: '
  code = STDIN.readline.strip

  # This will fail if the user gave us an invalid authorization code
  access_token, user_id = flow.finish(code)
  
  puts "export DROPBOX_ACCESS_TOKEN=#{access_token}"
  puts "export DROPBOX_USER_ID=#{user_id}"
  puts
  puts "This auth token will last for 10 years, or when you choose to invalidate it, whichever comes first."
  puts
  puts "Now either include these constants in yours calls to dbox, or set them as environment variables."
  puts "In bash, including them in calls looks like:"
  puts "$ DROPBOX_ACCESS_TOKEN=#{access_token} DROPBOX_USER_ID=#{user_id} dbox ..."
end
connect() click to toggle source
# File lib/dbox/api.rb, line 44
def self.connect
  api = new()
  api.connect
  api
end
new() click to toggle source

IMPORTANT: API.new is private. Please use API.authorize or API.connect as the entry point.

# File lib/dbox/api.rb, line 54
def initialize
end

Public Instance Methods

connect() click to toggle source
# File lib/dbox/api.rb, line 61
def connect
  access_token = ENV["DROPBOX_ACCESS_TOKEN"]
  access_type = ENV["DROPBOX_ACCESS_TYPE"] || "dropbox"
  
  raise(ConfigurationError, "Please set the DROPBOX_ACCESS_TOKEN environment variable to a Dropbox access token") unless access_token
  raise(ConfigurationError, "Please set the DROPBOX_ACCESS_TYPE environment variable either dropbox (full access) or sandbox (App access)") unless access_type == "dropbox" || access_type == "app_folder"
  @client = DropboxClient.new(access_token)
end
create_dir(path) click to toggle source
# File lib/dbox/api.rb, line 133
def create_dir(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"
      else
        raise e
      end
    end
  end
end
delete_dir(path) click to toggle source
# File lib/dbox/api.rb, line 148
def delete_dir(path)
  run(path) do
    log.info "Deleting #{path}"
    @client.file_delete(path)
  end
end
delete_file(path) click to toggle source
# File lib/dbox/api.rb, line 189
def delete_file(path)
  run(path) do
    log.info "Deleting #{path}"
    @client.file_delete(path)
  end
end
get_file(path, file_obj, stream=false) click to toggle source
# File lib/dbox/api.rb, line 155
def get_file(path, file_obj, stream=false)
  unless stream
    # just download directly using the get_file API
    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}")
    end
  else
    # 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
handle_response(path, res, &else_proc) click to toggle source
# File lib/dbox/api.rb, line 104
def handle_response(path, res, &else_proc)
  case res
  when Hash
    InsensitiveHash[res]
  when String
    res
  when Net::HTTPNotFound
    raise RemoteMissing, "#{path} does not exist on Dropbox"
  when Net::HTTPForbidden
    raise RequestDenied, "Operation on #{path} denied"
  when Net::HTTPNotModified
    :not_modified
  when true
    true
  else
    else_proc.call()
  end
end
initialize_copy(other) click to toggle source
# File lib/dbox/api.rb, line 57
def initialize_copy(other)
  @client = other.client.clone()
end
metadata(path = "/", hash = nil, list=true) click to toggle source
# File lib/dbox/api.rb, line 123
def metadata(path = "/", hash = nil, list=true)
  run(path) do
    log.debug "Fetching metadata for #{path}"
    res = @client.metadata(path, 10000, list, hash)
    log.debug res.inspect
    raise Dbox::RemoteMissing, "#{path} has been deleted on Dropbox" if res["is_deleted"]
    res
  end
end
move(old_path, new_path) click to toggle source
# File lib/dbox/api.rb, line 196
def move(old_path, 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}"
      else
        raise e
      end
    end
  end
end
put_file(path, local_path, previous_revision=nil) click to toggle source
# File lib/dbox/api.rb, line 182
def put_file(path, local_path, previous_revision=nil)
  run(path) do
    log.info "Uploading #{path}"
    File.open(local_path, "r") {|f| @client.put_file(path, f, false, previous_revision) }
  end
end
run(path, tries = NUM_TRIES, &proc) click to toggle source
# File lib/dbox/api.rb, line 70
def run(path, tries = NUM_TRIES, &proc)
  begin
    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
    if tries > 0
      if e.http_response.kind_of?(Net::HTTPServiceUnavailable)
        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.info "Headers: #{e.http_response.to_hash.inspect}"
      else
        log.info "Encountered a dropbox error. Sleeping #{TIME_BETWEEN_TRIES}s and trying again. Error: #{e.inspect}"
        log.info "Headers: #{e.http_response.to_hash.inspect}"
      end
      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
  rescue => e
    if tries > 0
      log.info "Encounted an unknown error. Sleeping #{TIME_BETWEEN_TRIES}s and trying again. Error: #{e.inspect}"
      sleep TIME_BETWEEN_TRIES
      run(path, tries - 1, &proc)
    else
      raise e
    end
  end
end
streaming_download(url, io, num_redirects = 0) click to toggle source
# File lib/dbox/api.rb, line 211
def streaming_download(url, io, num_redirects = 0)
  url = URI.parse(url)
  http = Net::HTTP.new(url.host, url.port)
  http.use_ssl = true
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE
  http.ca_file = Dropbox::TRUSTED_CERT_FILE

  req = Net::HTTP::Get.new(url.request_uri)
  req["User-Agent"] = "OfficialDropboxRubySDK/#{Dropbox::SDK_VERSION}"

  http.request(req) do |res|
    if res.kind_of?(Net::HTTPSuccess)
      # stream into given io
      res.read_body {|chunk| io.write(chunk) }
      true
    else
      if res.kind_of?(Net::HTTPRedirection) && res.header['location'] && num_redirects < 10
        log.info("following redirect, num_redirects = #{num_redirects}")
        log.info("redirect url: #{res.header['location']}")
        streaming_download(res.header['location'], io, num_redirects + 1)
      else
        raise DropboxError.new("Invalid response #{res.inspect}")
      end
    end
  end
end