Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion lib/dropbox/account.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ class Account

def initialize(attrs={})
@account_id = attrs['account_id']
@display_name = attrs['name']['display_name']
@display_name = attrs['name']['display_name'] if attrs['name']
@email = attrs['email']
@email_verified = attrs['email_verified']
@disabled = attrs['disabled']
Expand Down Expand Up @@ -44,4 +44,14 @@ def initialize(attrs={})
@allocated = attrs['allocation']['allocated'] # Space allocated in bytes
end
end

class UserMembershipInfo
attr_reader :access_type, :account_id, :same_team, :team_member_id

def initialize(attrs={})
@access_type = attrs['access_type']['.tag']
@account_id = attrs['user']['account_id']
@same_team = attrs['user']['same_team']
end
end
end
119 changes: 117 additions & 2 deletions lib/dropbox/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,115 @@ def get_space_usage
SpaceUsage.new(resp)
end

# List shared folders owned by the current user
# @param limit [Integer] The maximum number of results to return. Defaults to 1000
# @return [Array<Dropbox::FileMetadata>] entries
# @return [String] cursor
def list_shared_folders(limit = 1000)
resp = request('/sharing/list_folders', limit: limit)
entries = resp['entries'].map { |entry| FolderMetadata.new(entry) }
[entries, resp['cursor']]
end

# Continue the list of shared folders
# @param cursor [String]
# @return [Array<Dropbox::FileMetadata>] entries
# @return [String] cursor
def continue_list_shared_folders(cursor)
resp = request('/sharing/list_folders/continue', cursor: cursor)
entries = resp['entries'].map { |entry| FolderMetadata.new(entry) }
[entries, resp['cursor']]
end

# Make a folder shared
# @param path [String] The path to the folder to be made shared
# @param member_policy [String] Can be 'anyone' or 'team'. Defaults to 'anyone'.
# @param acl_update_policy [String] Can be 'owner' or 'editors'. Defaults to 'owner'.
# @param shared_link_policy [String] Can be 'anyone' or 'editors'. Defaults to 'anyone'.
# @param force_async [Boolean] Defaults to false.
# @return [String] the job id, if the processing is asynchronous.
# @return [Dropbox::SharedFolderMetadata] if the processing is synchronous
def share_folder(path, member_policy: 'anyone', acl_update_policy: 'owner', shared_link_policy: 'anyone', force_async: false)
resp = request('/sharing/share_folder', path: path, member_policy: member_policy, acl_update_policy: acl_update_policy, shared_link_policy: shared_link_policy, force_async: force_async)
parse_tagged_response(resp)
end

# Update the sharing policies for a shared folder.
# @params shared_folder_id [String] The shared_id of the folder
# @params member_policy [String]. Can be nil, 'anyone' or 'team'. Defaults to nil
# @params acl_update_policy [String]. Can be nil, 'owner' or 'editors'. Defaults to nil.
# @params shared_link_policy [String]. Can be nil, 'anyone' or 'members'. Defaults to nil.
# @return [Dropbox::SharedFolderMetadata]
def update_folder_policy(shared_folder_id, member_policy: nil, acl_update_policy: nil, shared_link_policy: nil)
data = {shared_folder_id: shared_folder_id}
data[:member_policy] = member_policy if member_policy
data[:acl_update_policy] = acl_update_policy if acl_update_policy
data[:shared_link_policy] = shared_link_policy if shared_link_policy
resp = request('/sharing/update_folder_policy', data)
SharedFolderMetadata.new(resp)
end

# Add a member to a shared folder
# @param shared_folder_id [String] The shared_id of the folder
# @param members [Array<String>] An array of emails as Strings.
# @param quiet [Boolean] defaults to false
# @param custom_message [String] A custom message to be sent to all of the members. Defaults to nil.
# @param access_level [String] The access level given to all members. Can be 'editor', 'viewer' or 'viewer_no_comment'. Defaults to 'editor'.
# @return [void]
def add_folder_member(shared_folder_id, members, quiet: false, custom_message: nil, access_level: 'editor')
params = {shared_folder_id: shared_folder_id, quiet: quiet}
params[:members] = members.map do |member|
{
'member' => {
'.tag' => 'email',
'email' => member
},
'access_level' => {
'.tag' => access_level
}
}
end
params[:custom_message] = custom_message if custom_message

request('/sharing/add_folder_member', params)
nil
end

# Mount a folder (accept a share request)
# @param shared_folder_id [String]
# @return [Dropbox::SharedFolderMetadata]
def mount_folder(shared_folder_id)
resp = request('/sharing/mount_folder', shared_folder_id: shared_folder_id)
SharedFolderMetadata.new(resp)
end

# List members of a shared folder
# @param shared_folder_id [String]
# @return [Array<Dropbox::UserMembershipInfo>]
def list_folder_members(shared_folder_id)
resp = request('/sharing/list_folder_members', shared_folder_id: shared_folder_id)
resp['users'].map {|user| UserMembershipInfo.new(user)}
end

# Relinquish membership of a shared folder you are a member of
# @param shared_folder_id [String]
# @param leave_a_copy [String] default false
# @return [String] 'complete' if the processing is complete
# @return [String] the job id, if the processing is asynchronous.
def relinquish_folder_membership(shared_folder_id, leave_a_copy: false)
resp = request('/sharing/relinquish_folder_membership', shared_folder_id: shared_folder_id, leave_a_copy: leave_a_copy)
parse_tagged_response(resp)
end

# Transfer ownership of a shared folder to another member of the folder
# @param shared_folder_id [String]
# @param to_dropbox_id [String]
# @return [void]
def transfer_folder(shared_folder_id, to_dropbox_id)
request('/sharing/transfer_folder', shared_folder_id: shared_folder_id, to_dropbox_id: to_dropbox_id)
nil
end

private
def parse_tagged_response(resp)
case resp['.tag']
Expand All @@ -328,7 +437,13 @@ def parse_tagged_response(resp)
when 'full_account'
FullAccount.new(resp)
when 'complete'
FileMetadata.new(resp)
if resp['time_invited']
SharedFolderMetadata.new(resp)
elsif resp['client_modified']
FileMetadata.new(resp)
else
'complete'
end
when 'async_job_id'
resp['async_job_id']
when 'in_progress'
Expand All @@ -347,7 +462,7 @@ def request(action, data=nil)
.post(url, json: data)

raise ApiError.new(resp) if resp.code != 200
JSON.parse(resp.to_s)
resp.parse
end

def content_request(action, args={})
Expand Down
15 changes: 13 additions & 2 deletions lib/dropbox/metadata.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def initialize(attrs={})

# Contains the metadata (but not contents) of a file.
class FileMetadata < Metadata
attr_reader :id, :client_modified, :server_modified, :rev, :size
attr_reader :id, :client_modified, :server_modified, :rev, :size, :parent_shared_folder_id

def initialize(attrs={})
@id = attrs.delete('id')
Expand All @@ -24,6 +24,7 @@ def initialize(attrs={})
@server_modified = Time.parse(attrs.delete('server_modified'))
@rev = attrs.delete('rev')
@size = attrs.delete('size')
@parent_shared_folder_id = attrs.delete('parent_shared_folder_id')
super(attrs)
end

Expand All @@ -34,10 +35,12 @@ def ==(cmp)

# Contains the metadata (but not contents) of a folder.
class FolderMetadata < Metadata
attr_reader :id
attr_reader :id, :shared_folder_id, :parent_shared_folder_id

def initialize(attrs={})
@id = attrs.delete('id')
@shared_folder_id = attrs.delete('shared_folder_id')
@parent_shared_folder_id = attrs.delete('parent_shared_folder_id')
super(attrs)
end

Expand All @@ -49,4 +52,12 @@ def ==(cmp)
# Contains the metadata of a deleted file.
class DeletedMetadata < Metadata
end

class SharedFolderMetadata < Metadata
attr_reader :shared_folder_id
def initialize(attrs={})
@shared_folder_id = attrs.delete('shared_folder_id')
super(attrs)
end
end
end
6 changes: 6 additions & 0 deletions test/stubs/complete.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
HTTP/1.1 200 OK
Content-Type: application/json

{
".tag": "complete"
}
11 changes: 11 additions & 0 deletions test/stubs/list_folder_members.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
HTTP/1.1 200 OK
Content-Type: application/json

{
"users":
[{"access_type": {".tag": "owner"}, "is_inherited": false, "user": {"account_id": "dbid:user1", "same_team": false}},
{"access_type": {".tag": "editor"}, "is_inherited": false, "user": {"account_id": "dbid:user2", "same_team": false}},
{"access_type": {".tag": "editor"}, "is_inherited": false, "user": {"account_id": "dbid:user3", "same_team": false}}],
"groups": [],
"invitees": []
}
23 changes: 23 additions & 0 deletions test/stubs/list_shared_folders.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
HTTP/1.1 200 OK
Content-Type: application/json

{"entries":
[{"access_type": {".tag": "editor"},
"is_team_folder": false,
"policy": {"acl_update_policy": {".tag": "editors"}, "shared_link_policy": {".tag": "anyone"}},
"path_lower": "/shared_file_1",
"name": "shared_file_1",
"shared_folder_id": "1234",
"permissions": [],
"time_invited": "2016-09-12T23:07:11Z",
"preview_url": "https://www.dropbox.com/scl/fo/abcd/1234"},
{"access_type": {".tag": "owner"},
"is_team_folder": false,
"policy": {"acl_update_policy": {".tag": "editors"}, "shared_link_policy": {".tag": "anyone"}},
"path_lower": "/shared_file_2",
"name": "shared_file_2",
"shared_folder_id": "1235",
"permissions": [],
"time_invited": "2016-09-12T22:36:20Z",
"preview_url": "https://www.dropbox.com/scl/fo/abce/1235"}],
"cursor": "cursor123"}
4 changes: 4 additions & 0 deletions test/stubs/null.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
HTTP/1.1 200 OK
Content-Type: application/json

null
13 changes: 13 additions & 0 deletions test/stubs/share_folder.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
HTTP/1.1 200 OK
Content-Type: application/json

{
".tag": "complete",
"access_type": {".tag": "owner"},
"is_team_folder": false,
"policy": {"acl_update_policy": {".tag": "editors"}, "shared_link_policy": {".tag": "anyone"}},
"path_lower": "/test/share_me",
"name": "share_me",
"shared_folder_id": "123123123",
"time_invited": "2016-09-13T23:38:32Z",
"preview_url": "https://www.dropbox.com/scl/fo/123123123/AADAASDKJASDASD"}
35 changes: 35 additions & 0 deletions test/test_integration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,15 @@ def setup
WebMock.allow_net_connect!
@client = Dropbox::Client.new(ENV['DROPBOX_SDK_ACCESS_TOKEN'])
@box = @client.create_folder('/integration_test_container')
if ENV['DROPBOX_SDK_ACCESS_TOKEN_2']
@client2 = Dropbox::Client.new(ENV['DROPBOX_SDK_ACCESS_TOKEN_2'])
@box2 = @client2.create_folder('/integration_test_container')
end
end

def teardown
@client.delete(@box.path_lower)
@client2.delete(@box2.path_lower) if @client2
WebMock.disable_net_connect!
end

Expand Down Expand Up @@ -96,4 +101,34 @@ def test_upload_session
assert_instance_of Dropbox::FileMetadata, file
assert_equal 75, file.size
end

def test_sharing
skip('Please provide a second access token with the DROPBOX_SDK_ACCESS_TOKEN_2 environment variable to run the sharing tests') unless @client2
folder = @client.create_folder(@box.path_lower + '/testing_share')
shared = @client.share_folder(folder.path_lower)
client1_account = @client.get_current_account
client2_account = @client2.get_current_account

# @client shares a folder with @client2, and client2 accepts the share
@client.add_folder_member(shared.shared_folder_id, [client2_account.email])
@client2.mount_folder(shared.shared_folder_id)
@client2.move('/testing_share', @box2.path_lower + '/testing_share')
folder_members = @client2.list_folder_members(shared.shared_folder_id)
assert_equal 2, folder_members.length
assert_includes folder_members.map(&:account_id), client2_account.account_id
assert_includes folder_members.map(&:account_id), client1_account.account_id

# @client2 uploads a file, and @client can see it
uploaded_path = @box.path_lower + '/testing_share/uploaded.txt'
@client2.upload(uploaded_path, 'uploaded')
files = @client.list_folder(@box.path_lower + '/testing_share')
assert_equal files.first.name, 'uploaded.txt'

# @client transfers ownership to @client2 and then @client
# removes themselves from the folder
@client.transfer_folder(shared.shared_folder_id, client2_account.account_id)
@client.relinquish_folder_membership(shared.shared_folder_id, leave_a_copy: false)
folder_members = @client2.list_folder_members(shared.shared_folder_id)
assert_equal 1, folder_members.length
end
end
14 changes: 12 additions & 2 deletions test/test_metadata.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,32 @@
class DropboxMetadataTest < Minitest::Test
def test_folder_initialize
folder = Dropbox::FolderMetadata.new('id' => 'id:123', 'name' => 'child',
'path_lower' => '/parent/middle/child', 'path_display' => '/parent/middle/child')
'path_lower' => '/parent/middle/child', 'path_display' => '/parent/middle/child', 'shared_folder_id' => '1234', 'parent_shared_folder_id' => 'abcd')
assert_equal 'id:123', folder.id
assert_equal 'child', folder.name
assert_equal '/parent/middle/child', folder.path_lower
assert_equal '/parent/middle/child', folder.path_display
assert_equal '1234', folder.shared_folder_id
assert_equal 'abcd', folder.parent_shared_folder_id
end

def test_file_initialize
file = Dropbox::FileMetadata.new('id' => 'id:123', 'name' => 'file',
'path_lower' => '/folder/file', 'path_display' => '/folder/file',
'size' => 11, 'server_modified' => '2007-07-07T00:00:00Z')
'size' => 11, 'server_modified' => '2007-07-07T00:00:00Z', 'parent_shared_folder_id' => 'abcd')
assert_equal 'id:123', file.id
assert_equal 'file', file.name
assert_equal '/folder/file', file.path_lower
assert_equal '/folder/file', file.path_display
assert_equal 11, file.size
assert_equal 'abcd', file.parent_shared_folder_id
end

def test_shared_folder_initialize
file = Dropbox::SharedFolderMetadata.new('path_lower'=>'/folder/file', 'name'=>'file', 'shared_folder_id'=>'1234')
assert_equal '/folder/file', file.path_lower
assert_equal 'file', file.name
assert_equal '1234', file.shared_folder_id
end

def test_folder_equality
Expand Down
Loading