boxr 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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