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.
@@ -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
@@ -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
@@ -0,0 +1,19 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /.ruby-version
5
+ /.ruby-gemset
6
+ /.env.test
7
+ /.env
8
+ /_yardoc/
9
+ /coverage/
10
+ /doc/
11
+ /pkg/
12
+ /spec/reports/
13
+ /tmp/
14
+ *.gem
15
+ *.bundle
16
+ *.so
17
+ *.o
18
+ *.a
19
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in boxr.gemspec
4
+ gemspec
@@ -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.
@@ -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
@@ -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
+
@@ -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
@@ -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
@@ -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