class PopUpArchive::Client

Attributes

agent[RW]
api_endpoint[RW]
cookies[RW]
croak_on_404[RW]
debug[RW]
host[RW]
user_agent[RW]

Public Class Methods

new(args) click to toggle source
# File lib/popuparchive.rb, line 52
def initialize(args)
  #puts args.inspect
  @un                  = args[:username]
  @pw                  = args[:password]
  @oauth_id            = args[:id]
  @oauth_secret        = args[:secret]
  @oauth_redir_uri     = args[:redir_uri] || 'urn:ietf:wg:oauth:2.0:oob'
  @host                = args[:host] || 'https://www.popuparchive.com'
  @debug               = args[:debug]
  @user_agent          = args[:user_agent] || 'popuparchive-ruby-client/'+version()
  @api_endpoint        = args[:api_endpoint] || '/api'
  @croak_on_404        = args[:croak_on_404] || false

  # normalize host
  @host.gsub!(/\/$/, '')

  # sanity check
  begin
    uri = URI.parse(@host)
  rescue URI::InvalidURIError => err
    raise "Bad :host value " + err
  end
  if (!uri.host || !uri.port)
    raise "Bad :host value " + @server
  end

  @agent = get_agent()

end

Public Instance Methods

create_audio_file(item, attrs={}) click to toggle source
# File lib/popuparchive.rb, line 191
def create_audio_file(item, attrs={})
  file = attrs.has_key?(:file) ? URI::encode(attrs.delete(:file)) : nil
  uri  = "/items/#{item.id}/audio_files"
  uri  += "?file=#{file}" if file
  body = JSON.generate({:audio_file => attrs})
  resp = post(uri, body)
  #puts "audio_file:"
  #puts pp resp
  return resp.http_resp.body
end
create_item(coll, attrs) click to toggle source
# File lib/popuparchive.rb, line 186
def create_item(coll, attrs)
  resp = post("/collections/#{coll.id}/items", JSON.generate(attrs))
  return resp.http_resp.body
end
finish_upload(upload) click to toggle source
# File lib/popuparchive.rb, line 310
def finish_upload(upload)
  puts "finish_upload:"
  puts pp upload

  # workflow assumes file has been successfully PUT to AWS
  # (1) GET aws_url?uploadId=$uploadId
  # parse xml response for ListPartsResult
  # (2) POST aws_url?uplaodId=$uploadId with body
  #  <CompleteMultipartUpload>
  #   <Part><PartNumber>1</PartNumber><ETag>"$etag_from_ListPartsResult"</ETag></Part>
  #  </CompleteMultipartUpload>
  # response to POST contains CompleteMultipartUploadResult with final location
  # (3) GET pua_url/upload_finished/? with params:
  #  filename => $filename,
  #  filesize => $n,
  #  key      => $sig_key,
  #  last_modified => $e,
  #  upload_id => $aws_uploadId,

  # sanity check upload object
  [:signatures, :params, :fileattrs, :upload_to, :init_sig, :aws_url, :aws_agent].each do|p|
    if !upload.has_key? p
      raise ":#{p} missing from upload"
    end
  end

  aws_upload_id = upload[:params][:upload_id]
  aws_agent = upload[:aws_agent]
  parts_resp = aws_agent.get(upload[:aws_url], {:uploadId => aws_upload_id})

  aws_parts = []
  parts_resp.ListPartsResult.Part.each do|part|
    aws_parts.push( { PartNumber: part.PartNumber, ETag: part.ETag })
  end
  aws_parts_xml = XmlSimple.xml_out({Part: aws_parts}, {rootname: 'CompleteMultipartUpload', noattr: true})
  puts "aws_parts_xml: #{aws_parts_xml}"
  aws_finish_resp = aws_agent.post do|req|
    req.url "#{upload[:aws_url]}?uploadId=#{aws_upload_id}"
    req.headers['Content-Type'] = upload[:fileattrs][:mime_type]
    req.headers['Content-Disposition'] = "attachment; filename=" + upload[:fileattrs][:filename]
    req.body = aws_parts_xml
  end

  puts "aws_finish_resp:"
  puts pp aws_finish_resp
  
end
get(path, params={}) click to toggle source
# File lib/popuparchive.rb, line 139
def get(path, params={})
  resp = @agent.get @api_endpoint + path, params
  @debug and puts pp(resp)
  return PopUpArchive::Response.new resp
end
get_agent() click to toggle source
# File lib/popuparchive.rb, line 109
def get_agent()
  uri = @host + @api_endpoint
  opts = {
    :url => uri,
    :ssl => {:verify => false},
    :headers => {
      'User-Agent'   => @user_agent,
      'Accept'       => 'application/json',
      'Cookie'       => @cookies
    }
  }
  @token = get_oauth_token
  #puts "token="
  #puts pp(@token)
  conn = Faraday.new(opts) do |faraday|
    faraday.request :url_encoded
    [:mashify, :json].each{|mw| faraday.response(mw) }
    if !@croak_on_404
      faraday.use PopUpArchive::FaradayErrHandler
    else 
      faraday.response(:raise_error)
    end
    faraday.request :authorization, 'Bearer', @token.token
    faraday.response :logger if @debug
    faraday.adapter  :excon   # IMPORTANT this is last
  end

  return conn
end
get_collection(coll_id) click to toggle source
# File lib/popuparchive.rb, line 160
def get_collection(coll_id)
  resp = get('/collections/'+coll_id.to_s)
  return resp.http_resp.body
end
get_item(coll_id, item_id) click to toggle source
# File lib/popuparchive.rb, line 170
def get_item(coll_id, item_id)
  resp = get('/collections/'+coll_id.to_s+'/items/'+item_id.to_s)
  return resp.http_resp.body
end
get_items(coll_id) click to toggle source
# File lib/popuparchive.rb, line 165
def get_items(coll_id)
  resp = get('/collections/'+coll_id.to_s+'/items')
  return resp.http_resp.body.items
end
get_my_uploads() click to toggle source
# File lib/popuparchive.rb, line 175
def get_my_uploads
  resp = get('/collections')
  my_uploads = nil
  resp.collections.each do|coll|
    if coll.title == 'My Uploads'
      my_uploads = coll
    end
  end
  return my_uploads
end
get_oauth_token(options={}) click to toggle source
# File lib/popuparchive.rb, line 82
def get_oauth_token(options={})
  oauth_options = {
    site:            @host + @api_endpoint,
    authorize_url:   @host + '/oauth/authorize',
    token_url:       @host + '/oauth/token',
    redirect_uri:    @oauth_redir_uri,
    connection_opts: options.merge( { :ssl => {:verify => false}, } )
  }

  # TODO

  client = OAuth2::Client.new(@oauth_id, @oauth_secret, oauth_options) do |faraday|
    faraday.request  :url_encoded
    faraday.response :logger if @debug
    faraday.adapter  :excon
  end

  token = nil
  if @un && @pw
    # TODO 3-legged oauth to @authorize_url
  else
    token = client.client_credentials.get_token()
  end

  return token
end
get_or_create_item(filename) click to toggle source
# File lib/popuparchive.rb, line 155
def get_or_create_item(filename)


end
post(path, body, content_type='application/json') click to toggle source
# File lib/popuparchive.rb, line 145
def post(path, body, content_type='application/json')
  uri = @api_endpoint + path
  resp = @agent.post do|req|
    req.url uri
    req.body = body
    req.headers['Content-Type'] = content_type
  end
  return PopUpArchive::Response.new resp
end
start_upload(item, af_id, fileattrs) click to toggle source
# File lib/popuparchive.rb, line 202
def start_upload(item, af_id, fileattrs)
  
  # workflow is:
  # (1) GET upload_to with audio_file.id
  # (2) using response, GET get_init_signature
  #  (example: "key"=>"$token/$filename", "mime_type"=>"image/jpeg", "filename"=>"$filename", "filesize"=>"$n", "last_modified"=>"$e", "item_id"=>"$iid", "audio_file_id"=>"$afid"}
  # (3) using upload_id, GET get_all_signatures
  #  (example: {"key"=>"$token/$filename", "mime_type"=>"image/jpeg", "num_chunks"=>"1", "upload_id"=>"$upid", "filename"=>"$filename", "filesize"=>"$n", "last_modified"=>"$e", "item_id"=>"$iid", "audio_file_id"=>"$afid"}
  # (4) foreach chunk, GET chunk_loaded (optional, since only UI needs this, not a SDK)
  # (5) finally, GET upload_finished (see finish_upload() below)

  # check 'item' for validity
  if !item.has_key? :token 
    raise ":token missing on item"
  end
  if !item.has_key? :id
    raise ":id missing on item"
  end

  # check 'fileattrs' for validity
  if !fileattrs.has_key? :filename
    raise ":filename missing on fileattrs"
  end
  if !fileattrs.has_key? :lastmod
    raise ":lastmod missing on fileattrs"
  end
  if !fileattrs.has_key? :size
    raise ":size missing on fileattrs"
  end
  if !fileattrs.has_key? :mime_type
    raise ":mime_type missing on fileattrs"
  end

  base_uri = "/items/#{item.id}/audio_files/#{af_id}"

  upload_to_uri = "#{base_uri}/upload_to"
  upload_to_resp = get(upload_to_uri)
  puts "upload_to_resp:"
  puts pp upload_to_resp

  sig_key = "#{item[:token]}/#{fileattrs[:filename]}"
  sig_params = { 
    :key           => sig_key,
    :mime_type     => fileattrs[:mime_type],
    :filename      => fileattrs[:filename],
    :filesize      => fileattrs[:size],
    :last_modified => fileattrs[:lastmod],
    :item_id       => item.id,
    :audio_file_id => af_id,
  }
  init_sig_uri = "#{base_uri}/get_init_signature?" + Faraday::FlatParamsEncoder.encode(sig_params)
  init_sig_resp = get(init_sig_uri)
  puts "init_sig_resp:"
  puts pp init_sig_resp

  # build the authorization key
  upload_key = upload_to_resp['key']
  authz_key = "#{upload_to_resp.provider} #{upload_key}:#{init_sig_resp.signature}"

  # special agent for aws
  aws_url = "https://#{upload_to_resp.bucket}.s3.amazonaws.com/#{sig_key}"
  aws_opts = { 
    :headers => {
      'User-Agent' => @user_agent,
      'Authorization' => authz_key,
      'x-amz-date' => init_sig_resp.date,
    },
    :ssl => { :verify => false }   
  }   
  aws_agent = Faraday.new(aws_opts) do |faraday|
    [:mashify, :xml, :raise_error].each{|mw| faraday.response(mw) }
    faraday.response :logger if @debug
    faraday.adapter  :excon   # IMPORTANT this is last
  end

  # post to provider to get the upload_id
  puts "aws_agent.post"
  aws_resp = aws_agent.post do |req|
    req.url "#{aws_url}?uploads"
    req.headers['Content-Type'] = fileattrs[:mime_type]
    req.headers['Content-Disposition'] = "attachment; filename=" + fileattrs[:filename]
    req.headers['x-amz-acl']  = 'public-read'  # TODO
  end
  puts "aws response:"
  puts pp aws_resp

  # pull out the AWS uploadId
  sig_params[:upload_id] = aws_resp.body.InitiateMultipartUploadResult.UploadId

  # how many chunks do we expect?
  sig_params[:num_chunks] = fileattrs.has_key?(:num_chunks) ? fileattrs[:num_chunks] : 1
 
  all_sig_resp = get("#{base_uri}/get_all_signatures?" + Faraday::FlatParamsEncoder.encode(sig_params))
  puts "all_sig_resp"
  puts pp all_sig_resp

  return { 
    :signatures => all_sig_resp.http_resp.body,
    :params     => sig_params, 
    :fileattrs  => fileattrs,
    :upload_to  => upload_to_resp.http_resp.body,
    :init_sig   => init_sig_resp.http_resp.body,
    :aws_url    => aws_url,
    :aws_agent  => aws_agent,
  } 
  
end
version() click to toggle source
# File lib/popuparchive.rb, line 48
def version
  return "1.0.0"
end