This repository has been archived by the owner on Dec 11, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
DRYed up File and MiniIO interaction
- - DRY up request headers - NullMiniIO: all methods are empty - rename old spec files so they wont run, move them to a specific directory transform request params to JSON as late (and as once) as possible add more old spec files to the ignore list - Add RemoteFile and RemoteTransfer modules that change the corresponding instances *in place*. It sets instance variables and getter methods. - Transfer.find works, very limited test, though - Make upgrading a transfer (a bit) less magical. finalize call for transfers operational File upload implemented Implement file completion Refactorings for readability, less complexity Move communication to CommunicationHelper class More focus in integration test Expose #to_json on a transfer Refactoring * use class<<self instead of private_class_method on def self.foobar things refactor: Remove duplication remove unused image refactor get rid of useless example
- Loading branch information
1 parent
274b4f1
commit 9ee34dc
Showing
46 changed files
with
816 additions
and
1,752 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
inherit_gem: | ||
wetransfer_style: ruby/default.yml | ||
wetransfer_style: ruby/default.yml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,82 +1,167 @@ | ||
module WeTransfer | ||
class CommunicationError < StandardError; end | ||
|
||
module CommunicationHelper | ||
API_URI_BASE = 'https://dev.wetransfer.com'.freeze | ||
extend Forwardable | ||
|
||
API_URL_BASE = "https://dev.wetransfer.com" | ||
DEFAULT_HEADERS = { | ||
"User-Agent" => "WetransferRubySdk/#{WeTransfer::VERSION} Ruby #{RUBY_VERSION}", | ||
"Content-Type" => "application/json" | ||
}.freeze | ||
|
||
class << self | ||
attr_accessor :logger, :api_key | ||
end | ||
attr_accessor :logger, :api_key, :bearer_token | ||
|
||
def logger | ||
@logger ||= CommunicationHelper.logger | ||
end | ||
def reset_authentication! | ||
@api_key = nil | ||
@bearer_token = nil | ||
@request_as = nil | ||
end | ||
|
||
def api_key | ||
@api_key ||= CommunicationHelper.api_key | ||
end | ||
def find_transfer(transfer_id) | ||
response = request_as.get("/v2/transfers/%s" % [transfer_id]) | ||
ensure_ok_status!(response) | ||
response_body = remote_transfer_params(response.body) | ||
found_transfer = Transfer.new(message: response_body[:message]) | ||
setup_transfer( | ||
transfer: found_transfer, | ||
data: response_body | ||
) | ||
end | ||
|
||
def request_as | ||
authorize_if_no_bearer_token! | ||
def upload_url_for_chunk(transfer_id, file_id, chunk) | ||
response = request_as.get("/v2/transfers/%s/files/%s/upload-url/%s" % [transfer_id, file_id, chunk]) | ||
ensure_ok_status!(response) | ||
|
||
@request_as ||= Faraday.new(API_URI_BASE) do |c| | ||
c.response :logger, logger | ||
c.adapter Faraday.default_adapter | ||
c.headers = auth_headers.merge( | ||
"User-Agent" => "WetransferRubySdk/#{WeTransfer::VERSION} Ruby #{RUBY_VERSION}", | ||
"Content-Type" => "application/json", | ||
JSON.parse(response.body).fetch("url") | ||
end | ||
|
||
def persist_transfer(transfer) | ||
response = request_as.post( | ||
"/v2/transfers", | ||
transfer.as_request_params.to_json, | ||
) | ||
ensure_ok_status!(response) | ||
|
||
handle_new_transfer_data( | ||
transfer: transfer, | ||
data: remote_transfer_params(response.body) | ||
) | ||
end | ||
end | ||
|
||
private | ||
def finalize_transfer(transfer) | ||
response = request_as.put("/v2/transfers/%s/finalize" % transfer.id) | ||
ensure_ok_status!(response) | ||
handle_new_transfer_data( | ||
transfer: transfer, | ||
data: remote_transfer_params(response.body) | ||
) | ||
end | ||
|
||
def auth_headers | ||
authorize_if_no_bearer_token! | ||
def remote_transfer_params(response_body) | ||
JSON.parse(response_body, symbolize_names: true) | ||
end | ||
|
||
{ | ||
'X-API-Key' => api_key, | ||
'Authorization' => ('Bearer %s' % @bearer_token), | ||
} | ||
end | ||
def upload_chunk(put_url, chunk_contents) | ||
@chunk_uploader ||= Faraday.new { |c| minimal_faraday_config(c) } | ||
|
||
def ensure_ok_status!(response) | ||
case response.status | ||
when 200..299 | ||
true | ||
when 400..499 | ||
logger.error response | ||
raise WeTransfer::CommunicationError, JSON.parse(response.body)["message"] | ||
when 500..504 | ||
logger.error response | ||
raise WeTransfer::CommunicationError, "Response had a #{response.status} code, we could retry" | ||
else | ||
logger.error response | ||
raise WeTransfer::CommunicationError, "Response had a #{response.status} code, no idea what to do with that" | ||
@chunk_uploader.put( | ||
put_url, | ||
chunk_contents.read, | ||
'Content-Type' => 'binary/octet-stream', | ||
'Content-Length' => chunk_contents.size.to_s | ||
) | ||
end | ||
end | ||
|
||
def authorize_if_no_bearer_token! | ||
return @bearer_token if @bearer_token | ||
def complete_file(transfer_id, file_id, chunks) | ||
response = request_as.put( | ||
"/v2/transfers/%s/files/%s/upload-complete" % [transfer_id, file_id], | ||
{ part_numbers: chunks }.to_json | ||
) | ||
|
||
ensure_ok_status!(response) | ||
remote_transfer_params(response.body) | ||
end | ||
|
||
private | ||
|
||
def request_as | ||
@request_as ||= Faraday.new(API_URL_BASE) do |c| | ||
minimal_faraday_config(c) | ||
c.headers = auth_headers.merge DEFAULT_HEADERS | ||
end | ||
end | ||
|
||
def setup_transfer(transfer:, data:) | ||
data[:files].each do |file_params| | ||
transfer.add_file( | ||
name: file_params[:name], | ||
size: file_params[:size], | ||
) | ||
end | ||
|
||
handle_new_transfer_data(transfer: transfer, data: data) | ||
end | ||
|
||
def handle_new_transfer_data(transfer:, data:) | ||
%i[id state url].each do |i_var| | ||
transfer.instance_variable_set "@#{i_var}", data[i_var] | ||
end | ||
|
||
RemoteFile.upgrade( | ||
transfer: transfer, | ||
files_response: data[:files] | ||
) | ||
transfer | ||
end | ||
|
||
response = Faraday.new(API_URI_BASE) do |c| | ||
c.response :logger, logger | ||
c.adapter Faraday.default_adapter | ||
c.headers = { | ||
"User-Agent" => "WetransferRubySdk/#{WeTransfer::VERSION} Ruby #{RUBY_VERSION}", | ||
"Content-Type" => "application/json", | ||
def auth_headers | ||
authorize_if_no_bearer_token! | ||
|
||
{ | ||
'X-API-Key' => api_key, | ||
'Authorization' => "Bearer #{@bearer_token}" | ||
} | ||
end.post( | ||
'/v2/authorize', | ||
'', | ||
'Content-Type' => 'application/json', | ||
'X-API-Key' => CommunicationHelper.api_key, | ||
) | ||
ensure_ok_status!(response) | ||
bearer_token = JSON.parse(response.body)['token'] | ||
if bearer_token.nil? || bearer_token.empty? | ||
raise WeTransfer::CommunicationError, "The authorization call returned #{response.body} and no usable :token key could be found there" | ||
end | ||
@bearer_token = bearer_token | ||
|
||
def ensure_ok_status!(response) | ||
case response.status | ||
when 200..299 | ||
true | ||
when 400..499 | ||
logger.error response | ||
raise WeTransfer::CommunicationError, JSON.parse(response.body)["message"] | ||
when 500..504 | ||
logger.error response | ||
raise WeTransfer::CommunicationError, "Response had a #{response.status} code, we could retry" | ||
else | ||
logger.error response | ||
raise WeTransfer::CommunicationError, "Response had a #{response.status} code, no idea what to do with that" | ||
end | ||
end | ||
|
||
def authorize_if_no_bearer_token! | ||
return @bearer_token if @bearer_token | ||
|
||
response = Faraday.new(API_URL_BASE) do |c| | ||
minimal_faraday_config(c) | ||
c.headers = DEFAULT_HEADERS.merge('X-API-Key' => api_key) | ||
end.post( | ||
'/v2/authorize', | ||
) | ||
ensure_ok_status!(response) | ||
bearer_token = JSON.parse(response.body)['token'] | ||
raise WeTransfer::CommunicationError, "The authorization call returned #{response.body} and no usable :token key could be found there" if bearer_token.nil? || bearer_token.empty? | ||
@bearer_token = bearer_token | ||
end | ||
|
||
def minimal_faraday_config(config) | ||
config.response :logger, logger | ||
config.adapter Faraday.default_adapter | ||
end | ||
end | ||
|
||
def_delegator self, :minimal_faraday_config | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
module WeTransfer | ||
module RemoteFile | ||
class FileMismatchError < StandardError; end | ||
class NoIoError < StandardError; end | ||
|
||
class Multipart < ::Ks.strict(:chunks, :chunk_size) | ||
def to_h | ||
%i[chunks chunk_size].each_with_object({}) do |prop, memo| | ||
memo[prop] = send(prop) | ||
end | ||
end | ||
end | ||
|
||
def self.upgrade(files_response:, transfer:) | ||
files_response.each do |file_response| | ||
local_file = transfer.find_file_by_name(file_response[:name]) | ||
|
||
local_file.instance_variable_set( | ||
:@id, | ||
file_response[:id] | ||
) | ||
|
||
local_file.instance_variable_set( | ||
:@multipart, | ||
Multipart.new( | ||
chunks: file_response[:multipart][:part_numbers], | ||
chunk_size: file_response[:multipart][:chunk_size], | ||
) | ||
) | ||
end | ||
end | ||
end | ||
end |
Oops, something went wrong.