Skip to content
This repository has been archived by the owner on Dec 11, 2020. It is now read-only.

Simple interface #59

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open

Simple interface #59

wants to merge 18 commits into from

Conversation

arnoFleming
Copy link
Contributor

@arnoFleming arnoFleming commented Feb 21, 2019

Proposed changes

Describe the big picture of your changes here to communicate to the maintainers why we should accept this pull request. If it fixes a bug or resolves a feature request, be sure to link to that issue.

Types of changes

What types of changes does your code introduce to wetransfer_ruby_sdk?
Put an x in the boxes that apply

  • Docs (missing or updated docs)
  • Bugfix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Improvement / maintenance (removing technical debt, performance, tooling, etc...)

Checklist

Put an x in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your code.

  • I have read the CONTRIBUTING doc
  • I have added necessary documentation (if appropriate)
  • Code changes are covered by unit tests.
  • Any dependent changes have been merged and published in downstream modules

Further comments

If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution you did and what alternatives you considered, etc...

@arnoFleming arnoFleming changed the title --wip-- [skip ci] Simple interface Feb 22, 2019
@arnoFleming arnoFleming requested review from julik, davidenko87, arkaitzgarro and a team and removed request for julik and davidenko87 March 1, 2019 15:58
@arnoFleming arnoFleming marked this pull request as ready for review March 1, 2019 16:02
- a Client instance can create a tranfer (and add files on the go)
- split Transfers' .new and .create,
  - new only initializes,
  - create also sends it to WeTransfers' Public API
  - need to have a seperate add_file method to make this work
- MiniIO introduced to get wrap all things IO, in a very slim interface
-

- 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
small refactorings for chainging methods on a transfer, and
unfuddle the :ensure_ok_status!
Document that 0.10.x only offers transfer support
Document high level usage better.
Copy link
Contributor

@julik julik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great progress 🚀 I would say the only thing I am wary of is the reuse of file names as identifiers for files when uploading "in steps". The rest is cosmetic.

upload.add_file(name: 'README.txt', io: StringIO.new("You should read All the Things!"))
transfer = client.create_transfer_and_upload_files(message: 'All the Things') do |transfer|
# Add a file using File.open. If you do it like this, :name and :io params are optional
transfer.add_file(io: File.open('Gemfile'))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we provide auto-DWIM-ing of the filename from the File object we pass in io: it would be good to document how it works exactly. For example, will it only use the basename? Will it call #path on the file? I think I would even go as far as make the method "overloaded" - hit a different code path when you pass in a file: keyword argument and nothing else, and raise if you, say, pass both file: and name:. The rationale being mostly: if we provide "'magic'"™® it should be easy for the end-user to understand.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, The SDK relies on File.basename deeper buried inside as a fallback only. You can rename the file by passing in a name.
Will add documentation to express this behaviour.

# Add a file with File.open, but give it a different name inside the transfer
transfer.add_file(
name: 'hello_world.rb',
io: File.open('path/to/file/with/different_name.rb')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The README in this case encourages leaking file descriptors. For example, if I encountered a README like this I would probably assume that it is the add_file call that is going to perform #read on the passed IO objects, and I can (and should) actually call it like this:

File.open('/path/to/myfile', 'rb') do |fi|
   transfer.add_file(name: 'myfile', io: fi)
end

but with this example this will probably break as the actual reading of the file data will happen at block exit. Can there be a way to demonstrate it in the readme?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's have an IRL conversation, since I'm unsure if I understand the ins and outs of your point.

# Specifying the size is not very useful in this case, but feel free to explicitly
# communicate the size of the coming io.
#
# The :name param is compulsory if it cannot be derived from the IO.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we say "cannot be derived" we probably need to explain how it will be derived if that is possible (by calling #path?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to self: See here for a closely related issue.

end

# Upload the file. The Ruby SDK will upload the file in chunks.
transfer.upload_file(name: "small_file", io: StringIO.new("#" * 80))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This enforces addressing files "by name" whereas in practice the server gives you file IDs at transfer create. It is in our best interest to stimulate the consumer of the SDK to either use an abstraction layer that handles ID binding for them:

transfer.files.first.upload(io: ...)

or encourage them to explore the related file metadata:

transfer.files #=> [<RemoteFile id: ...>]

Our approach to "auto-intuiting" the file IDs from the filenames has led to problems as we destructively sanitize the filenames when they enter our systems

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name that the user provided may not be equal to what the WT stack makes of it. This is documented in the Public API. The sdk allows you to call your file 'the-weather-is:.jpg', and reusing that name, while it will be
But from the high-level view of the SDK, I want users to not worry about this. Just as the sdk hides the uploading in chunks, this method hides the file naming details.

Can you agree to this if this is documented better, or do you want the SDK to get rid of this, and use the WT file naming logic only?

## Boards

**NOTE**: **Boards are disabled from version 0.10.x** of the Ruby SDK. The latest releases that include **board support is found in version 0.9.x**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we remove Boards support from this version probably the documentation has to be removed as well

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will remove the docs, but keep the comment about version 0.9, so peeps will know where to go to.

transfer.instance_variable_set "@#{i_var}", data[i_var]
end

RemoteFile.upgrade(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still on the fence about the "upgrade" terminology. What does it mean that a transfer has been "upgraded" versus when it hasn't?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will express intent better. This is cruft I overlooked after fixing the Remotefile .upgrade fubar.

# WeTransferFile
#
def name
File.basename(@io)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you really do File.basename(<#File>)? I thought it only worked with path strings

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will investigate


let(:client) { WeTransfer::Client.new(api_key: ENV.fetch("WT_API_KEY")) }

it "Convenience method create_transfer_and_upload_files does it all!" do
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok this is probably being too annoying but when naming RSpec stuff the following guideline can often be useful:

If we string all the describe/context/it block titles together, does it create a "Noun + verb" sentence that makes sense when printed? For example:

describe "An invigorated Shlobulator" do
 context "when connected to a stuffed Frobnicator" do
   it "confabricates correctly" do
     ....
   end
  end
end

gives us the following output:

An invigorated Shlobulator when connected to a stuffed Frobnicator confabricates correctly

which would make sense when printed in one line. With a wording like this we would see

transfer integration Convenience method create_transfer_and_upload_files does it all!

which does not form a sentence. It's a small thing but one of the gains from well-named examples/contexts is that they usually look good no matter which Rspec formatter is used ;)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had big issues naming this spec, so I am on your side. Will reinvestigate.

@@ -37,3 +25,5 @@ def test_logger
config.default_formatter = 'doc'
config.order = :random
end

RSpec::Mocks.configuration.verify_partial_doubles = true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😍

.to raise_error(WeTransfer::CommunicationError)
end

it "showing the error from the API to the user" do
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above re. example titles

Copy link
Contributor Author

@arnoFleming arnoFleming left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for all good tips, will push some changes.


def request_as
@request_as ||= Faraday.new(API_URL_BASE) do |c|
minimal_faraday_config(c)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

transfer.instance_variable_set "@#{i_var}", data[i_var]
end

RemoteFile.upgrade(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will express intent better. This is cruft I overlooked after fixing the Remotefile .upgrade fubar.

# WeTransferFile
#
def name
File.basename(@io)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will investigate


let(:client) { WeTransfer::Client.new(api_key: ENV.fetch("WT_API_KEY")) }

it "Convenience method create_transfer_and_upload_files does it all!" do
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had big issues naming this spec, so I am on your side. Will reinvestigate.

upload.add_file_at(path: '/path/to/another/local/file.jpg')
upload.add_file(name: 'README.txt', io: StringIO.new("You should read All the Things!"))
transfer = client.create_transfer_and_upload_files(message: 'All the Things') do |transfer|
# Add a file using File.open. If you do it like this, :name and :io params are optional
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will propose a change, keep you posted

end

# Upload the file. The Ruby SDK will upload the file in chunks.
transfer.upload_file(name: "small_file", io: StringIO.new("#" * 80))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name that the user provided may not be equal to what the WT stack makes of it. This is documented in the Public API. The sdk allows you to call your file 'the-weather-is:.jpg', and reusing that name, while it will be
But from the high-level view of the SDK, I want users to not worry about this. Just as the sdk hides the uploading in chunks, this method hides the file naming details.

Can you agree to this if this is documented better, or do you want the SDK to get rid of this, and use the WT file naming logic only?

## Boards

**NOTE**: **Boards are disabled from version 0.10.x** of the Ruby SDK. The latest releases that include **board support is found in version 0.9.x**
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will remove the docs, but keep the comment about version 0.9, so peeps will know where to go to.

transfer
.upload_files
.complete_files
.finalize
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could even get rid of the transfer helper var, but it expresses intent so beautifully :)

UPLOAD_URL_URI = "/v2/transfers/%s/files/%s/upload-url/%s"

DEFAULT_HEADERS = {
"User-Agent" => "WetransferRubySdk/#{WeTransfer::VERSION} Ruby #{RUBY_VERSION}",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea, from both a debugging and spying perspective 🕵️‍♂️.
although the Public API doesn't store it anywhere, and I cannot seem to find it in transfer either.

# frozen_string_literal: true

module WeTransfer
# Wrapper around an IO object, delegating only methods we need for creating
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reword: creating files and sending in chunks

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants