boxr 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.env.example +9 -0
- data/.gitignore +19 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +31 -0
- data/Rakefile +11 -0
- data/boxr.gemspec +29 -0
- data/lib/boxr.rb +38 -0
- data/lib/boxr/client.rb +215 -0
- data/lib/boxr/collaborations.rb +52 -0
- data/lib/boxr/comments.rb +45 -0
- data/lib/boxr/exceptions.rb +39 -0
- data/lib/boxr/files.rb +200 -0
- data/lib/boxr/folders.rb +105 -0
- data/lib/boxr/groups.rb +76 -0
- data/lib/boxr/metadata.rb +29 -0
- data/lib/boxr/search.rb +26 -0
- data/lib/boxr/shared_items.rb +13 -0
- data/lib/boxr/tasks.rb +77 -0
- data/lib/boxr/users.rb +92 -0
- data/lib/boxr/version.rb +3 -0
- data/spec/boxr_spec.rb +15 -0
- data/spec/spec_helper.rb +2 -0
- metadata +168 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: fe63a17bee50c141a055587190b08b1487de5f5e
|
4
|
+
data.tar.gz: 0ddb73b50600453f306c7c8409c31f29c915083c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ecd0548d4c4d068ed54c8e96a7fa8374d8516819dff0ac0b296ba6942c48a54093519dd890104c458dbdee1b19e1ebabdb08f910eab2fd4cc65ca082d2b04f3d
|
7
|
+
data.tar.gz: 81b0c14fa6c5d01bed3fd2630de985a6f0869d2dee77c148f33de968d05fc9e2c20b1494181610190acab484bfb100fa17da39c5138f1c209f1bf46a989183f3
|
data/.env.example
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
#1. go to https://developers.box.com,
|
2
|
+
#2. find or create your Box Content API app for testing
|
3
|
+
#3. click 'Edit Application'
|
4
|
+
#4. check the boxes for 'Read and write all files and folders' and 'Manage an enterprise'
|
5
|
+
#5. click 'Create a developer token'
|
6
|
+
#6. paste the value below
|
7
|
+
#7. save this file as .env
|
8
|
+
|
9
|
+
BOX_DEVELOPER_TOKEN=bsuwpLRJYN123NJTOisOvdvBCloboMqW
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Chad Burnette
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# Boxr
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'boxr'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install boxr
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
TODO: Write usage instructions here
|
24
|
+
|
25
|
+
## Contributing
|
26
|
+
|
27
|
+
1. Fork it ( https://github.com/[my-github-username]/boxr/fork )
|
28
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
29
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
30
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
31
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'rspec/core/rake_task'
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
|
4
|
+
# Default directory to look in is `/specs`
|
5
|
+
# Run with `rake spec`
|
6
|
+
RSpec::Core::RakeTask.new(:spec) do |task|
|
7
|
+
task.rspec_opts = ['--color', '--format', 'documentation']
|
8
|
+
end
|
9
|
+
|
10
|
+
task :default => :spec
|
11
|
+
|
data/boxr.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'boxr/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "boxr"
|
8
|
+
spec.version = Boxr::VERSION
|
9
|
+
spec.authors = ["Chad Burnette"]
|
10
|
+
spec.email = ["chadburnette@me.com"]
|
11
|
+
spec.summary = "A Ruby client library for the Box V2 Content API that covers 100% of the underlying REST API."
|
12
|
+
spec.description = ""
|
13
|
+
spec.homepage = "https://github.com/cburnette/boxr"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
22
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
+
spec.add_development_dependency "rspec", "~> 3.1"
|
24
|
+
spec.add_development_dependency "dotenv", "~> 0.11"
|
25
|
+
|
26
|
+
spec.add_runtime_dependency "oj", "~> 2.11"
|
27
|
+
spec.add_runtime_dependency "httpclient", "~> 2.5"
|
28
|
+
spec.add_runtime_dependency "hashie", "~> 3.3"
|
29
|
+
end
|
data/lib/boxr.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'oj'
|
2
|
+
require 'httpclient'
|
3
|
+
require 'hashie'
|
4
|
+
|
5
|
+
require "boxr/version"
|
6
|
+
require 'boxr/exceptions'
|
7
|
+
require 'boxr/client'
|
8
|
+
require 'boxr/folders'
|
9
|
+
require 'boxr/files'
|
10
|
+
require 'boxr/comments'
|
11
|
+
require 'boxr/users'
|
12
|
+
require 'boxr/groups'
|
13
|
+
require 'boxr/collaborations'
|
14
|
+
require 'boxr/search'
|
15
|
+
require 'boxr/tasks'
|
16
|
+
require 'boxr/shared_items'
|
17
|
+
require 'boxr/metadata'
|
18
|
+
|
19
|
+
module Enumerable
|
20
|
+
def files
|
21
|
+
self.select{|i| i.type == 'file'}
|
22
|
+
end
|
23
|
+
|
24
|
+
def folders
|
25
|
+
self.select{|i| i.type == 'folder'}
|
26
|
+
end
|
27
|
+
|
28
|
+
def web_links
|
29
|
+
self.select{|i| i.type == 'web_link'}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
module Boxr
|
34
|
+
Oj.default_options = {:mode => :compat }
|
35
|
+
|
36
|
+
#The root folder in Box is always identified by 0
|
37
|
+
ROOT = 0
|
38
|
+
end
|
data/lib/boxr/client.rb
ADDED
@@ -0,0 +1,215 @@
|
|
1
|
+
module Boxr
|
2
|
+
|
3
|
+
class Client
|
4
|
+
|
5
|
+
API_URI = "https://api.box.com/2.0"
|
6
|
+
UPLOAD_URI = "https://upload.box.com/api/2.0"
|
7
|
+
FILES_URI = "#{API_URI}/files"
|
8
|
+
FILES_UPLOAD_URI = "#{UPLOAD_URI}/files/content"
|
9
|
+
FOLDERS_URI = "#{API_URI}/folders"
|
10
|
+
USERS_URI = "#{API_URI}/users"
|
11
|
+
GROUPS_URI = "#{API_URI}/groups"
|
12
|
+
GROUP_MEMBERSHIPS_URI = "#{API_URI}/group_memberships"
|
13
|
+
COLLABORATIONS_URI = "#{API_URI}/collaborations"
|
14
|
+
COMMENTS_URI = "#{API_URI}/comments"
|
15
|
+
SEARCH_URI = "#{API_URI}/search"
|
16
|
+
TASKS_URI = "#{API_URI}/tasks"
|
17
|
+
TASK_ASSIGNMENTS_URI = "#{API_URI}/task_assignments"
|
18
|
+
SHARED_ITEMS_URI = "#{API_URI}/shared_items"
|
19
|
+
METADATA_URI = "#{API_URI}/files"
|
20
|
+
|
21
|
+
DEFAULT_LIMIT = 100
|
22
|
+
FOLDER_ITEMS_LIMIT = 1000
|
23
|
+
|
24
|
+
FOLDER_AND_FILE_FIELDS = [:type,:id,:sequence_id,:etag,:name,:created_at,:modified_at,:description,
|
25
|
+
:size,:path_collection,:created_by,:modified_by,:trashed_at,:purged_at,
|
26
|
+
:content_created_at,:content_modified_at,:owned_by,:shared_link,:folder_upload_email,
|
27
|
+
:parent,:item_status,:item_collection,:sync_state,:has_collaborations,:permissions,:tags,
|
28
|
+
:sha1,:shared_link,:version_number,:comment_count,:lock,:extension,:is_package,:can_non_owners_invite]
|
29
|
+
FOLDER_AND_FILE_FIELDS_QUERY = FOLDER_AND_FILE_FIELDS.join(',')
|
30
|
+
|
31
|
+
COMMENT_FIELDS = [:type,:id,:is_reply_comment,:message,:tagged_message,:created_by,:created_at,:item,:modified_at]
|
32
|
+
COMMENT_FIELDS_QUERY = COMMENT_FIELDS.join(',')
|
33
|
+
|
34
|
+
TASK_FIELDS = [:type,:id,:item,:due_at,:action,:message,:task_assignment_collection,:is_completed,:created_by,:created_at]
|
35
|
+
TASK_FIELDS_QUERY = TASK_FIELDS.join(',')
|
36
|
+
|
37
|
+
COLLABORATION_FIELDS = [:type,:id,:created_by,:created_at,:modified_at,:expires_at,:status,:accessible_by,:role,:acknowledged_at,:item]
|
38
|
+
COLLABORATION_FIELDS_QUERY = COLLABORATION_FIELDS.join(',')
|
39
|
+
|
40
|
+
USER_FIELDS = [:type,:id,:name,:login,:created_at,:modified_at,:role,:language,:timezone,:space_amount,:space_used,
|
41
|
+
:max_upload_size,:tracking_codes,:can_see_managed_users,:is_sync_enabled,:is_external_collab_restricted,
|
42
|
+
:status,:job_title,:phone,:address,:avatar_uri,:is_exempt_from_device_limits,:is_exempt_from_login_verification,
|
43
|
+
:enterprise,:my_tags]
|
44
|
+
USER_FIELDS_QUERY = USER_FIELDS.join(',')
|
45
|
+
|
46
|
+
GROUP_FIELDS = [:type, :id, :name, :created_at, :modified_at]
|
47
|
+
GROUP_FIELDS_QUERY = GROUP_FIELDS.join(',')
|
48
|
+
|
49
|
+
#Read this to see why the httpclient gem was chosen: http://bibwild.wordpress.com/2012/04/30/ruby-http-performance-shootout-redux/
|
50
|
+
#All instances of Boxr::Client will use this one class instance of HTTPClient; that way persistent HTTPS connections work.
|
51
|
+
#Plus, httpclient is thread-safe so we can use the same class instance with multiple instances of Boxr::Client
|
52
|
+
BOX_CLIENT = HTTPClient.new
|
53
|
+
BOX_CLIENT.transparent_gzip_decompression = true
|
54
|
+
|
55
|
+
def initialize(token, as_user_id: nil)
|
56
|
+
@token = token
|
57
|
+
@as_user_id = as_user_id
|
58
|
+
end
|
59
|
+
|
60
|
+
def debug_device=(device)
|
61
|
+
if device
|
62
|
+
BOX_CLIENT.debug_dev = device
|
63
|
+
BOX_CLIENT.transparent_gzip_decompression = false
|
64
|
+
else
|
65
|
+
BOX_CLIENT.debug_dev = nil
|
66
|
+
BOX_CLIENT.transparent_gzip_decompression = true
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def get(uri, query: nil, success_codes: [200], process_response: true, if_match: nil, box_api_header: nil)
|
75
|
+
headers = standard_headers()
|
76
|
+
headers['If-Match'] = if_match unless if_match.nil?
|
77
|
+
headers['BoxApi'] = box_api_header unless box_api_header.nil?
|
78
|
+
|
79
|
+
res = BOX_CLIENT.get(uri, query: query, header: headers)
|
80
|
+
check_response_status(res, success_codes)
|
81
|
+
|
82
|
+
if process_response
|
83
|
+
return processed_response res
|
84
|
+
else
|
85
|
+
return res.body, res
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def get_with_pagination(uri, query: {}, limit: DEFAULT_LIMIT)
|
90
|
+
entries = []
|
91
|
+
offset = 0
|
92
|
+
headers = standard_headers()
|
93
|
+
|
94
|
+
begin
|
95
|
+
query[:limit] = limit
|
96
|
+
query[:offset] = offset
|
97
|
+
res = BOX_CLIENT.get(uri, query: query, header: headers)
|
98
|
+
|
99
|
+
if (res.status==200)
|
100
|
+
body_json = Oj.load(res.body)
|
101
|
+
total_count = body_json["total_count"]
|
102
|
+
offset = offset + limit
|
103
|
+
|
104
|
+
entries << body_json["entries"]
|
105
|
+
else
|
106
|
+
raise BoxrException.new(res.status, res.body, res.header)
|
107
|
+
end
|
108
|
+
end until offset - total_count >= 0
|
109
|
+
|
110
|
+
entries.flatten.map{|i| Hashie::Mash.new(i)}
|
111
|
+
end
|
112
|
+
|
113
|
+
def post(uri, body, query: nil, success_codes: [201], process_body: true, content_md5: nil, content_type: nil, if_match: nil)
|
114
|
+
body = Oj.dump(body) if process_body
|
115
|
+
|
116
|
+
headers = standard_headers()
|
117
|
+
headers['If-Match'] = if_match unless if_match.nil?
|
118
|
+
headers["Content-MD5"] = content_md5 unless content_md5.nil?
|
119
|
+
headers["Content-Type"] = content_type unless content_type.nil?
|
120
|
+
|
121
|
+
res = BOX_CLIENT.post(uri, body: body, query: query, header: headers)
|
122
|
+
check_response_status(res, success_codes)
|
123
|
+
|
124
|
+
processed_response res
|
125
|
+
end
|
126
|
+
|
127
|
+
def put(uri, body, query: nil, success_codes: [200], content_type: nil, if_match: nil)
|
128
|
+
headers = standard_headers()
|
129
|
+
headers['If-Match'] = if_match unless if_match.nil?
|
130
|
+
headers["Content-Type"] = content_type unless content_type.nil?
|
131
|
+
|
132
|
+
res = BOX_CLIENT.put(uri, body: Oj.dump(body), query: query, header: headers)
|
133
|
+
check_response_status(res, success_codes)
|
134
|
+
|
135
|
+
processed_response res
|
136
|
+
end
|
137
|
+
|
138
|
+
def delete(uri, query: nil, success_codes: [204], if_match: nil)
|
139
|
+
headers = standard_headers()
|
140
|
+
headers['If-Match'] = if_match unless if_match.nil?
|
141
|
+
|
142
|
+
res = BOX_CLIENT.delete(uri, query: query, header: headers)
|
143
|
+
check_response_status(res, success_codes)
|
144
|
+
|
145
|
+
processed_response res
|
146
|
+
end
|
147
|
+
|
148
|
+
def options(uri, body, success_codes: [200])
|
149
|
+
headers = standard_headers()
|
150
|
+
|
151
|
+
res = BOX_CLIENT.options(uri, body: Oj.dump(body), header: headers)
|
152
|
+
check_response_status(res, success_codes)
|
153
|
+
|
154
|
+
processed_response res
|
155
|
+
end
|
156
|
+
|
157
|
+
def standard_headers()
|
158
|
+
headers = {"Authorization" => "Bearer #{@token}"}
|
159
|
+
headers['As-User'] = "#{@as_user_id}" unless @as_user_id.nil?
|
160
|
+
headers
|
161
|
+
end
|
162
|
+
|
163
|
+
def check_response_status(res, success_codes)
|
164
|
+
raise BoxrException.new(res.status, res.body, res.header) unless success_codes.include?(res.status)
|
165
|
+
end
|
166
|
+
|
167
|
+
def processed_response(res)
|
168
|
+
body_json = Oj.load(res.body)
|
169
|
+
return Hashie::Mash.new(body_json), res
|
170
|
+
end
|
171
|
+
|
172
|
+
def build_fields_query(fields, all_fields_query)
|
173
|
+
if fields == :all
|
174
|
+
{:fields => all_fields_query}
|
175
|
+
elsif fields.is_a?(Array) && fields.length > 0
|
176
|
+
{:fields => fields.join(',')}
|
177
|
+
else
|
178
|
+
{}
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def create_shared_link(uri, item_id, access, unshared_at, can_download, can_preview)
|
183
|
+
if access.nil?
|
184
|
+
attributes = {shared_link: {}}
|
185
|
+
else
|
186
|
+
attributes = {shared_link: {access: access}}
|
187
|
+
attributes[:shared_link][:unshared_at] = unshared_at.to_datetime.rfc3339 if unshared_at
|
188
|
+
attributes[:shared_link][:permissions] = {} unless can_download.nil? && can_preview.nil?
|
189
|
+
attributes[:shared_link][:permissions][:can_download] = can_download unless can_download.nil?
|
190
|
+
attributes[:shared_link][:permissions][:can_preview] = can_preview unless can_preview.nil?
|
191
|
+
end
|
192
|
+
|
193
|
+
updated_item, response = put uri, attributes
|
194
|
+
updated_item
|
195
|
+
end
|
196
|
+
|
197
|
+
def disable_shared_link(uri, item_id)
|
198
|
+
attributes = {shared_link: nil}
|
199
|
+
|
200
|
+
updated_item, response = put uri, attributes
|
201
|
+
updated_item
|
202
|
+
end
|
203
|
+
|
204
|
+
def restore_trashed_item(uri, name, parent_id)
|
205
|
+
attributes = {}
|
206
|
+
attributes[:name] = name if name
|
207
|
+
attributes[:parent] = {id: parent_id} if parent_id
|
208
|
+
|
209
|
+
restored_item, response = post uri, attributes
|
210
|
+
restored_item
|
211
|
+
end
|
212
|
+
|
213
|
+
end
|
214
|
+
|
215
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Boxr
|
2
|
+
class Client
|
3
|
+
|
4
|
+
#make sure 'role' value is a string as Box has role values with spaces and dashes; e.g. 'previewer uploader'
|
5
|
+
def add_collaboration(folder_id, accessible_by, role, fields: [], notify: nil)
|
6
|
+
query = build_fields_query(fields, COLLABORATION_FIELDS_QUERY)
|
7
|
+
query[:notify] = :notify unless notify.nil?
|
8
|
+
|
9
|
+
attributes = {item: {id: folder_id, type: :folder}}
|
10
|
+
attributes[:accessible_by] = accessible_by
|
11
|
+
attributes[:role] = role
|
12
|
+
|
13
|
+
collaboration, response = post(COLLABORATIONS_URI, attributes, query: query)
|
14
|
+
collaboration
|
15
|
+
end
|
16
|
+
|
17
|
+
def edit_collaboration(collaboration_id, role: nil, status: nil)
|
18
|
+
uri = "#{COLLABORATIONS_URI}/#{collaboration_id}"
|
19
|
+
attributes = {}
|
20
|
+
attributes[:role] = role unless role.nil?
|
21
|
+
attributes[:status] = status unless status.nil?
|
22
|
+
|
23
|
+
updated_collaboration, response = put(uri, attributes)
|
24
|
+
updated_collaboration
|
25
|
+
end
|
26
|
+
|
27
|
+
def remove_collaboration(collaboration_id)
|
28
|
+
uri = "#{COLLABORATIONS_URI}/#{collaboration_id}"
|
29
|
+
result, response = delete(uri)
|
30
|
+
result
|
31
|
+
end
|
32
|
+
|
33
|
+
def collaboration(collaboration_id, fields: [], status: nil)
|
34
|
+
uri = "#{COLLABORATIONS_URI}/#{collaboration_id}"
|
35
|
+
|
36
|
+
query = build_fields_query(fields, COLLABORATION_FIELDS_QUERY)
|
37
|
+
query[:status] = status unless status.nil?
|
38
|
+
|
39
|
+
collaboration, response = get(uri, query: query)
|
40
|
+
collaboration
|
41
|
+
end
|
42
|
+
|
43
|
+
#these are pending collaborations for the current users; use the As-User Header to request for different users
|
44
|
+
def pending_collaborations
|
45
|
+
query = {status: :pending}
|
46
|
+
pending_collaborations, response = get(COLLABORATIONS_URI, query: query)
|
47
|
+
pending_collaborations['entries']
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|