files.com 1.0.92
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/CONTRIBUTORS +4 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +82 -0
- data/LICENSE +21 -0
- data/README.md +119 -0
- data/Rakefile +12 -0
- data/SECURITY.md +24 -0
- data/_VERSION +1 -0
- data/bin/files +8 -0
- data/bin/files-console +16 -0
- data/docs/account_line_item.md +41 -0
- data/docs/action.md +37 -0
- data/docs/api_key.md +202 -0
- data/docs/app.md +59 -0
- data/docs/as2_key.md +133 -0
- data/docs/auto.md +11 -0
- data/docs/automation.md +190 -0
- data/docs/behavior.md +208 -0
- data/docs/bundle.md +252 -0
- data/docs/bundle_download.md +35 -0
- data/docs/clickwrap.md +143 -0
- data/docs/dns_record.md +35 -0
- data/docs/errors.md +17 -0
- data/docs/file.md +204 -0
- data/docs/file_action.md +126 -0
- data/docs/file_comment.md +116 -0
- data/docs/file_comment_reaction.md +62 -0
- data/docs/file_part_upload.md +37 -0
- data/docs/file_utils.md +4 -0
- data/docs/folder.md +90 -0
- data/docs/group.md +153 -0
- data/docs/group_user.md +124 -0
- data/docs/history.md +171 -0
- data/docs/history_export.md +174 -0
- data/docs/image.md +13 -0
- data/docs/invoice.md +72 -0
- data/docs/invoice_line_item.md +27 -0
- data/docs/ip_address.md +55 -0
- data/docs/lock.md +98 -0
- data/docs/message.md +147 -0
- data/docs/message_comment.md +132 -0
- data/docs/message_comment_reaction.md +94 -0
- data/docs/message_reaction.md +94 -0
- data/docs/notification.md +177 -0
- data/docs/payment.md +72 -0
- data/docs/payment_line_item.md +19 -0
- data/docs/permission.md +95 -0
- data/docs/preview.md +19 -0
- data/docs/project.md +121 -0
- data/docs/public_ip_address.md +13 -0
- data/docs/public_key.md +133 -0
- data/docs/remote_server.md +356 -0
- data/docs/request.md +100 -0
- data/docs/session.md +78 -0
- data/docs/site.md +448 -0
- data/docs/sso_strategy.md +114 -0
- data/docs/status.md +21 -0
- data/docs/style.md +93 -0
- data/docs/usage_daily_snapshot.md +45 -0
- data/docs/usage_snapshot.md +53 -0
- data/docs/user.md +535 -0
- data/docs/user_cipher_use.md +41 -0
- data/docs/user_request.md +93 -0
- data/files.com.gemspec +22 -0
- data/lib/files.com.rb +184 -0
- data/lib/files.com/api.rb +38 -0
- data/lib/files.com/api_client.rb +340 -0
- data/lib/files.com/errors.rb +41 -0
- data/lib/files.com/list.rb +95 -0
- data/lib/files.com/models/account_line_item.rb +82 -0
- data/lib/files.com/models/action.rb +77 -0
- data/lib/files.com/models/api_key.rb +270 -0
- data/lib/files.com/models/app.rb +106 -0
- data/lib/files.com/models/as2_key.rb +179 -0
- data/lib/files.com/models/auto.rb +17 -0
- data/lib/files.com/models/automation.rb +304 -0
- data/lib/files.com/models/behavior.rb +266 -0
- data/lib/files.com/models/bundle.rb +371 -0
- data/lib/files.com/models/bundle_download.rb +49 -0
- data/lib/files.com/models/clickwrap.rb +197 -0
- data/lib/files.com/models/dir.rb +3 -0
- data/lib/files.com/models/dns_record.rb +51 -0
- data/lib/files.com/models/errors.rb +22 -0
- data/lib/files.com/models/file.rb +968 -0
- data/lib/files.com/models/file_action.rb +126 -0
- data/lib/files.com/models/file_comment.rb +146 -0
- data/lib/files.com/models/file_comment_reaction.rb +100 -0
- data/lib/files.com/models/file_part_upload.rb +82 -0
- data/lib/files.com/models/file_utils.rb +118 -0
- data/lib/files.com/models/folder.rb +357 -0
- data/lib/files.com/models/group.rb +208 -0
- data/lib/files.com/models/group_user.rb +171 -0
- data/lib/files.com/models/history.rb +228 -0
- data/lib/files.com/models/history_export.rb +353 -0
- data/lib/files.com/models/image.rb +22 -0
- data/lib/files.com/models/invoice.rb +117 -0
- data/lib/files.com/models/invoice_line_item.rb +57 -0
- data/lib/files.com/models/ip_address.rb +66 -0
- data/lib/files.com/models/lock.rb +173 -0
- data/lib/files.com/models/message.rb +201 -0
- data/lib/files.com/models/message_comment.rb +165 -0
- data/lib/files.com/models/message_comment_reaction.rb +128 -0
- data/lib/files.com/models/message_reaction.rb +128 -0
- data/lib/files.com/models/notification.rb +263 -0
- data/lib/files.com/models/payment.rb +117 -0
- data/lib/files.com/models/payment_line_item.rb +37 -0
- data/lib/files.com/models/permission.rb +172 -0
- data/lib/files.com/models/preview.rb +37 -0
- data/lib/files.com/models/project.rb +140 -0
- data/lib/files.com/models/public_ip_address.rb +22 -0
- data/lib/files.com/models/public_key.rb +179 -0
- data/lib/files.com/models/remote_server.rb +680 -0
- data/lib/files.com/models/request.rb +179 -0
- data/lib/files.com/models/session.rb +247 -0
- data/lib/files.com/models/site.rb +733 -0
- data/lib/files.com/models/sso_strategy.rb +227 -0
- data/lib/files.com/models/status.rb +37 -0
- data/lib/files.com/models/style.rb +131 -0
- data/lib/files.com/models/usage_daily_snapshot.rb +66 -0
- data/lib/files.com/models/usage_snapshot.rb +96 -0
- data/lib/files.com/models/user.rb +876 -0
- data/lib/files.com/models/user_cipher_use.rb +63 -0
- data/lib/files.com/models/user_request.rb +127 -0
- data/lib/files.com/response.rb +25 -0
- data/lib/files.com/sizable_io.rb +32 -0
- data/lib/files.com/system_profiler.rb +56 -0
- data/lib/files.com/util.rb +106 -0
- data/lib/files.com/version.rb +5 -0
- data/spec/list_spec.rb +214 -0
- data/spec/models/file_spec.rb +68 -0
- data/spec/models/folder_spec.rb +40 -0
- data/spec/spec_helper.rb +36 -0
- data/test.sh +8 -0
- data/test/test.rb +75 -0
- metadata +235 -0
@@ -0,0 +1,41 @@
|
|
1
|
+
# UserCipherUse
|
2
|
+
|
3
|
+
## Example UserCipherUse Object
|
4
|
+
|
5
|
+
```
|
6
|
+
{
|
7
|
+
"id": 1,
|
8
|
+
"protocol_cipher": "TLSv1.2; ECDHE-RSA-AES256-GCM-SHA384",
|
9
|
+
"created_at": "2000-01-01T01:00:00Z",
|
10
|
+
"interface": "restapi",
|
11
|
+
"updated_at": "2000-01-01T01:00:00Z",
|
12
|
+
"user_id": 1
|
13
|
+
}
|
14
|
+
```
|
15
|
+
|
16
|
+
* `id` (int64): UserCipherUse ID
|
17
|
+
* `protocol_cipher` (string): The protocol and cipher employed
|
18
|
+
* `created_at` (date-time): The earliest recorded use of this combination of interface and protocol and cipher (for this user)
|
19
|
+
* `interface` (string): The interface accessed
|
20
|
+
* `updated_at` (date-time): The most recent use of this combination of interface and protocol and cipher (for this user)
|
21
|
+
* `user_id` (int64): ID of the user who performed this access
|
22
|
+
|
23
|
+
|
24
|
+
---
|
25
|
+
|
26
|
+
## List User Cipher Uses
|
27
|
+
|
28
|
+
```
|
29
|
+
Files::UserCipherUse.list(
|
30
|
+
user_id: 1,
|
31
|
+
page: 1,
|
32
|
+
per_page: 1
|
33
|
+
)
|
34
|
+
```
|
35
|
+
|
36
|
+
### Parameters
|
37
|
+
|
38
|
+
* `user_id` (int64): User ID. Provide a value of `0` to operate the current session's user.
|
39
|
+
* `page` (int64): Current page number.
|
40
|
+
* `per_page` (int64): Number of records to show per page. (Max: 10,000, 1,000 or less is recommended).
|
41
|
+
* `action` (string): Deprecated: If set to `count` returns a count of matching records rather than the records themselves.
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# UserRequest
|
2
|
+
|
3
|
+
## Example UserRequest Object
|
4
|
+
|
5
|
+
```
|
6
|
+
{
|
7
|
+
"name": "John Doe",
|
8
|
+
"email": "john.doe@files.com",
|
9
|
+
"details": "Changed Departments"
|
10
|
+
}
|
11
|
+
```
|
12
|
+
|
13
|
+
* `name` (string): User's full name
|
14
|
+
* `email` (email): User email address
|
15
|
+
* `details` (string): Details of the user's request
|
16
|
+
|
17
|
+
|
18
|
+
---
|
19
|
+
|
20
|
+
## List User Requests
|
21
|
+
|
22
|
+
```
|
23
|
+
Files::UserRequest.list(
|
24
|
+
page: 1,
|
25
|
+
per_page: 1
|
26
|
+
)
|
27
|
+
```
|
28
|
+
|
29
|
+
### Parameters
|
30
|
+
|
31
|
+
* `page` (int64): Current page number.
|
32
|
+
* `per_page` (int64): Number of records to show per page. (Max: 10,000, 1,000 or less is recommended).
|
33
|
+
* `action` (string): Deprecated: If set to `count` returns a count of matching records rather than the records themselves.
|
34
|
+
|
35
|
+
|
36
|
+
---
|
37
|
+
|
38
|
+
## Show User Request
|
39
|
+
|
40
|
+
```
|
41
|
+
Files::UserRequest.find(id)
|
42
|
+
```
|
43
|
+
|
44
|
+
### Parameters
|
45
|
+
|
46
|
+
* `id` (int64): Required - User Request ID.
|
47
|
+
|
48
|
+
|
49
|
+
---
|
50
|
+
|
51
|
+
## Create User Request
|
52
|
+
|
53
|
+
```
|
54
|
+
Files::UserRequest.create(
|
55
|
+
name: "name",
|
56
|
+
email: "email",
|
57
|
+
details: "details"
|
58
|
+
)
|
59
|
+
```
|
60
|
+
|
61
|
+
### Parameters
|
62
|
+
|
63
|
+
* `name` (string): Required - Name of user requested
|
64
|
+
* `email` (string): Required - Email of user requested
|
65
|
+
* `details` (string): Required - Details of the user request
|
66
|
+
|
67
|
+
|
68
|
+
---
|
69
|
+
|
70
|
+
## Delete User Request
|
71
|
+
|
72
|
+
```
|
73
|
+
Files::UserRequest.delete(id)
|
74
|
+
```
|
75
|
+
|
76
|
+
### Parameters
|
77
|
+
|
78
|
+
* `id` (int64): Required - User Request ID.
|
79
|
+
|
80
|
+
|
81
|
+
---
|
82
|
+
|
83
|
+
## Delete User Request
|
84
|
+
|
85
|
+
```
|
86
|
+
user_request = Files::UserRequest.list_for(path).first
|
87
|
+
|
88
|
+
user_request.delete
|
89
|
+
```
|
90
|
+
|
91
|
+
### Parameters
|
92
|
+
|
93
|
+
* `id` (int64): Required - User Request ID.
|
data/files.com.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
$LOAD_PATH.push File.expand_path('lib', __dir__)
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "files.com"
|
5
|
+
s.version = File.open(File.expand_path('_VERSION', __dir__)).read
|
6
|
+
s.platform = Gem::Platform::RUBY
|
7
|
+
s.authors = [ "files.com" ]
|
8
|
+
s.email = [ "support@files.com" ]
|
9
|
+
s.homepage = "https://www.files.com"
|
10
|
+
s.summary = "Files.com Ruby client."
|
11
|
+
s.description = "The Files.com Ruby client."
|
12
|
+
s.license = "MIT"
|
13
|
+
s.required_ruby_version = ">= 2.5"
|
14
|
+
s.add_dependency 'addressable', ">= 2.7.0"
|
15
|
+
s.add_dependency 'concurrent-ruby', ">= 1.1.3"
|
16
|
+
s.add_dependency 'faraday', ">= 1.0.1"
|
17
|
+
s.add_dependency 'net-http-persistent'
|
18
|
+
|
19
|
+
s.files = `find *`.split("\n").uniq.sort.reject(&:empty?)
|
20
|
+
s.executables = [ "files", "files-console" ]
|
21
|
+
s.require_paths = [ "lib" ]
|
22
|
+
end
|
data/lib/files.com.rb
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "cgi"
|
4
|
+
require "faraday"
|
5
|
+
require "json"
|
6
|
+
require "logger"
|
7
|
+
require "openssl"
|
8
|
+
require "rbconfig"
|
9
|
+
require "securerandom"
|
10
|
+
require "set"
|
11
|
+
require "socket"
|
12
|
+
require "uri"
|
13
|
+
require "addressable/uri"
|
14
|
+
require "concurrent/promise"
|
15
|
+
|
16
|
+
$LOAD_PATH.push __dir__
|
17
|
+
|
18
|
+
require "files.com/version"
|
19
|
+
|
20
|
+
require "files.com/sizable_io"
|
21
|
+
require "files.com/api"
|
22
|
+
require "files.com/api_client"
|
23
|
+
require "files.com/errors"
|
24
|
+
require "files.com/response"
|
25
|
+
require "files.com/system_profiler"
|
26
|
+
require "files.com/util"
|
27
|
+
require "files.com/list"
|
28
|
+
|
29
|
+
require "files.com/models/account_line_item"
|
30
|
+
require "files.com/models/action"
|
31
|
+
require "files.com/models/api_key"
|
32
|
+
require "files.com/models/app"
|
33
|
+
require "files.com/models/as2_key"
|
34
|
+
require "files.com/models/auto"
|
35
|
+
require "files.com/models/automation"
|
36
|
+
require "files.com/models/behavior"
|
37
|
+
require "files.com/models/bundle"
|
38
|
+
require "files.com/models/bundle_download"
|
39
|
+
require "files.com/models/clickwrap"
|
40
|
+
require "files.com/models/dns_record"
|
41
|
+
require "files.com/models/errors"
|
42
|
+
require "files.com/models/file"
|
43
|
+
require "files.com/models/file_action"
|
44
|
+
require "files.com/models/file_comment"
|
45
|
+
require "files.com/models/file_comment_reaction"
|
46
|
+
require "files.com/models/file_part_upload"
|
47
|
+
require "files.com/models/folder"
|
48
|
+
require "files.com/models/group"
|
49
|
+
require "files.com/models/group_user"
|
50
|
+
require "files.com/models/history"
|
51
|
+
require "files.com/models/history_export"
|
52
|
+
require "files.com/models/image"
|
53
|
+
require "files.com/models/invoice"
|
54
|
+
require "files.com/models/invoice_line_item"
|
55
|
+
require "files.com/models/ip_address"
|
56
|
+
require "files.com/models/lock"
|
57
|
+
require "files.com/models/message"
|
58
|
+
require "files.com/models/message_comment"
|
59
|
+
require "files.com/models/message_comment_reaction"
|
60
|
+
require "files.com/models/message_reaction"
|
61
|
+
require "files.com/models/notification"
|
62
|
+
require "files.com/models/payment"
|
63
|
+
require "files.com/models/payment_line_item"
|
64
|
+
require "files.com/models/permission"
|
65
|
+
require "files.com/models/preview"
|
66
|
+
require "files.com/models/project"
|
67
|
+
require "files.com/models/public_ip_address"
|
68
|
+
require "files.com/models/public_key"
|
69
|
+
require "files.com/models/remote_server"
|
70
|
+
require "files.com/models/request"
|
71
|
+
require "files.com/models/session"
|
72
|
+
require "files.com/models/site"
|
73
|
+
require "files.com/models/sso_strategy"
|
74
|
+
require "files.com/models/status"
|
75
|
+
require "files.com/models/style"
|
76
|
+
require "files.com/models/usage_daily_snapshot"
|
77
|
+
require "files.com/models/usage_snapshot"
|
78
|
+
require "files.com/models/user"
|
79
|
+
require "files.com/models/user_cipher_use"
|
80
|
+
require "files.com/models/user_request"
|
81
|
+
|
82
|
+
require "files.com/models/dir"
|
83
|
+
require "files.com/models/file_utils"
|
84
|
+
|
85
|
+
module Files
|
86
|
+
@api_key = nil
|
87
|
+
@app_info = nil
|
88
|
+
@base_url = "https://app.files.com"
|
89
|
+
@log_level = nil
|
90
|
+
@logger = nil
|
91
|
+
@proxy = nil
|
92
|
+
@session_id = nil
|
93
|
+
|
94
|
+
@max_network_retries = 3
|
95
|
+
@max_network_retry_delay = 2
|
96
|
+
@initial_network_retry_delay = 0.5
|
97
|
+
|
98
|
+
@open_timeout = 30
|
99
|
+
@read_timeout = 80
|
100
|
+
|
101
|
+
class << self
|
102
|
+
attr_accessor :api_key, :base_url, :initial_network_retry_delay, :max_network_retry_delay, :open_timeout, :read_timeout, :proxy, :session_id
|
103
|
+
end
|
104
|
+
|
105
|
+
# map to the same values as the standard library's logger
|
106
|
+
LEVEL_DEBUG = Logger::DEBUG
|
107
|
+
LEVEL_ERROR = Logger::ERROR
|
108
|
+
LEVEL_INFO = Logger::INFO
|
109
|
+
|
110
|
+
# When set prompts the library to log some extra information to $stdout and
|
111
|
+
# $stderr about what it's doing. For example, it'll produce information about
|
112
|
+
# requests, responses, and errors that are received. Valid log levels are
|
113
|
+
# `debug` and `info`, with `debug` being a little more verbose in places.
|
114
|
+
#
|
115
|
+
# Use of this configuration is only useful when `.logger` is _not_ set. When
|
116
|
+
# it is, the decision what levels to print is entirely deferred to the logger.
|
117
|
+
def self.log_level
|
118
|
+
@log_level
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.log_level=(val)
|
122
|
+
# Backwards compatibility for values that we briefly allowed
|
123
|
+
if val == "debug"
|
124
|
+
val = LEVEL_DEBUG
|
125
|
+
elsif val == "info"
|
126
|
+
val = LEVEL_INFO
|
127
|
+
end
|
128
|
+
|
129
|
+
if !val.nil? && ![ LEVEL_DEBUG, LEVEL_ERROR, LEVEL_INFO ].include?(val)
|
130
|
+
raise ArgumentError,
|
131
|
+
"log_level should only be set to `nil`, `debug` or `info`"
|
132
|
+
end
|
133
|
+
@log_level = val
|
134
|
+
end
|
135
|
+
|
136
|
+
# Sets a logger to which logging output will be sent. The logger should
|
137
|
+
# support the same interface as the `Logger` class that's part of Ruby's
|
138
|
+
# standard library (hint, anything in `Rails.logger` will likely be
|
139
|
+
# suitable).
|
140
|
+
#
|
141
|
+
# If `.logger` is set, the value of `.log_level` is ignored. The decision on
|
142
|
+
# what levels to print is entirely deferred to the logger.
|
143
|
+
def self.logger
|
144
|
+
@logger
|
145
|
+
end
|
146
|
+
|
147
|
+
def self.logger=(val)
|
148
|
+
@logger = val
|
149
|
+
end
|
150
|
+
|
151
|
+
def self.max_network_retries
|
152
|
+
@max_network_retries
|
153
|
+
end
|
154
|
+
|
155
|
+
def self.max_network_retries=(val)
|
156
|
+
@max_network_retries = val.to_i
|
157
|
+
end
|
158
|
+
|
159
|
+
def self.session=(session)
|
160
|
+
session.save unless session.id
|
161
|
+
self.session_id = session.id
|
162
|
+
end
|
163
|
+
|
164
|
+
# Sets some basic information about the running application that's sent along
|
165
|
+
# with API requests.
|
166
|
+
#
|
167
|
+
# Takes a name and optional partner program ID, plugin URL, and version.
|
168
|
+
def self.set_app_info(name, partner_id: nil, url: nil, version: nil)
|
169
|
+
@app_info = {
|
170
|
+
name: name,
|
171
|
+
partner_id: partner_id,
|
172
|
+
url: url,
|
173
|
+
version: version,
|
174
|
+
}
|
175
|
+
end
|
176
|
+
|
177
|
+
def self.app_info
|
178
|
+
@app_info
|
179
|
+
end
|
180
|
+
|
181
|
+
def self.app_info=(info)
|
182
|
+
@app_info = info
|
183
|
+
end
|
184
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Files
|
4
|
+
class Api
|
5
|
+
def self.send_request(path, verb, params, options)
|
6
|
+
warn_on_options_in_params(params)
|
7
|
+
|
8
|
+
options[:client] ||= ApiClient.active_client
|
9
|
+
|
10
|
+
headers = options.clone
|
11
|
+
api_key = headers.delete(:api_key)
|
12
|
+
client = headers.delete(:client)
|
13
|
+
session_id = headers.delete(:session_id)
|
14
|
+
if session = headers.delete(:session)
|
15
|
+
session.save unless session.id
|
16
|
+
session_id = session.id
|
17
|
+
end
|
18
|
+
|
19
|
+
resp, options[:api_key], options[:session_id] = client.execute_request(
|
20
|
+
verb, path, api_key: api_key, headers: headers, params: params, session_id: session_id
|
21
|
+
)
|
22
|
+
|
23
|
+
# Hash#select returns an array before 1.9
|
24
|
+
options_to_persist = {}
|
25
|
+
options.each do |k, v|
|
26
|
+
options_to_persist[k] = v if Util::OPTS.include?(k)
|
27
|
+
end
|
28
|
+
|
29
|
+
[ resp, options_to_persist ]
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.warn_on_options_in_params(params)
|
33
|
+
Util::OPTS.each do |opt|
|
34
|
+
warn("WARNING: #{opt} should be in the options hash, not the params hash. You may need to create a second hash that goes after params.)") if params.key?(opt)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,340 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Files
|
4
|
+
class ApiClient
|
5
|
+
attr_accessor :conn
|
6
|
+
|
7
|
+
def initialize(conn = nil)
|
8
|
+
self.conn = conn || self.class.default_conn
|
9
|
+
@system_profiler = SystemProfiler.new
|
10
|
+
@last_request_metrics = nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.active_client
|
14
|
+
Thread.current[:files_api_client] || default_client
|
15
|
+
end
|
16
|
+
|
17
|
+
# net_http_persistent does not support streaming downloads with faraday when directly downloading from S3
|
18
|
+
# falling back to net_http.
|
19
|
+
def self.download_client
|
20
|
+
Thread.current[:files_api_client_download_client] ||= ApiClient.new(download_conn)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.download_conn
|
24
|
+
Thread.current[:files_api_client_download_conn] ||= build_default_conn(force_net_http: true)
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.default_client
|
28
|
+
Thread.current[:files_api_client_default_client] ||= ApiClient.new(default_conn)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.default_conn
|
32
|
+
Thread.current[:files_api_client_default_conn] ||= build_default_conn
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.build_default_conn(force_net_http: false)
|
36
|
+
conn = Faraday.new do |builder|
|
37
|
+
builder.use Faraday::Request::Multipart
|
38
|
+
builder.use Faraday::Request::UrlEncoded
|
39
|
+
builder.use Faraday::Response::RaiseError
|
40
|
+
|
41
|
+
if Gem.win_platform? || RUBY_PLATFORM == "java" || force_net_http
|
42
|
+
builder.adapter :net_http
|
43
|
+
else
|
44
|
+
builder.adapter :net_http_persistent
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
conn.proxy = Files.proxy if Files.proxy
|
49
|
+
conn.ssl.verify = true
|
50
|
+
|
51
|
+
conn
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.should_retry?(error, num_retries)
|
55
|
+
return false if num_retries >= Files.max_network_retries
|
56
|
+
return true if error.is_a?(Faraday::TimeoutError)
|
57
|
+
return true if error.is_a?(Faraday::ConnectionFailed)
|
58
|
+
|
59
|
+
false
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.sleep_time(num_retries)
|
63
|
+
sleep_seconds = [
|
64
|
+
Files.initial_network_retry_delay * (2**(num_retries - 1)),
|
65
|
+
Files.max_network_retry_delay
|
66
|
+
].min
|
67
|
+
sleep_seconds *= (0.5 * (1 + rand))
|
68
|
+
[ Files.initial_network_retry_delay, sleep_seconds ].max
|
69
|
+
end
|
70
|
+
|
71
|
+
def request
|
72
|
+
@last_response = nil
|
73
|
+
old_files_api_client = Thread.current[:files_api_client]
|
74
|
+
Thread.current[:files_api_client] = self
|
75
|
+
|
76
|
+
begin
|
77
|
+
res = yield
|
78
|
+
[ res, @last_response ]
|
79
|
+
ensure
|
80
|
+
Thread.current[:files_api_client] = old_files_api_client
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def execute_request(method, path, base_url: nil, api_key: nil, session_id: nil, headers: {}, params: {})
|
85
|
+
base_url ||= Files.base_url
|
86
|
+
session_id ||= Files.session_id
|
87
|
+
|
88
|
+
if session_id and session_id != ""
|
89
|
+
check_session_id!(session_id)
|
90
|
+
elsif path !~ /^\/sessions/ # TODO: automate this to refer to any unauthenticated endpoint
|
91
|
+
api_key ||= Files.api_key
|
92
|
+
check_api_key!(api_key)
|
93
|
+
end
|
94
|
+
|
95
|
+
body = nil
|
96
|
+
query_params = nil
|
97
|
+
case method.to_s.downcase.to_sym
|
98
|
+
when :get, :head, :delete
|
99
|
+
query_params = params
|
100
|
+
else
|
101
|
+
body = params
|
102
|
+
end
|
103
|
+
|
104
|
+
headers = request_headers(api_key, session_id, method).update(headers)
|
105
|
+
url = api_url(path, base_url)
|
106
|
+
|
107
|
+
context = RequestLogContext.new
|
108
|
+
context.api_key = api_key
|
109
|
+
context.body = body
|
110
|
+
context.method = method
|
111
|
+
context.path = path
|
112
|
+
context.query_params = query_params if query_params
|
113
|
+
context.session_id = session_id
|
114
|
+
|
115
|
+
http_resp = execute_request_with_rescues(base_url, context) do
|
116
|
+
conn.run_request(method, url, body, headers) do |req|
|
117
|
+
req.options.open_timeout = Files.open_timeout
|
118
|
+
req.options.timeout = Files.read_timeout
|
119
|
+
req.params = query_params unless query_params.nil?
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
begin
|
124
|
+
resp = Response.from_faraday_response(http_resp)
|
125
|
+
rescue JSON::ParserError
|
126
|
+
raise general_api_error(http_resp.status, http_resp.body)
|
127
|
+
end
|
128
|
+
|
129
|
+
@last_response = resp
|
130
|
+
[ resp, api_key, session_id ]
|
131
|
+
end
|
132
|
+
|
133
|
+
def remote_request(method, url, headers = {}, body = nil)
|
134
|
+
context = RequestLogContext.new
|
135
|
+
context.method = method
|
136
|
+
context.path = url
|
137
|
+
|
138
|
+
execute_request_with_rescues(Files.base_url, context, true) do
|
139
|
+
conn.run_request(method, url, body, headers) do |req|
|
140
|
+
req.options.open_timeout = Files.open_timeout
|
141
|
+
req.options.timeout = Files.read_timeout
|
142
|
+
yield(req) if block_given?
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def stream_download(uri, io)
|
148
|
+
if conn.adapter == Faraday::Adapter::NetHttp
|
149
|
+
uri = URI(uri)
|
150
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
|
151
|
+
request = Net::HTTP::Get.new uri
|
152
|
+
http.request request do |response|
|
153
|
+
io.fulfill_content_length(response.content_length) if io.respond_to?(:fulfill_content_length)
|
154
|
+
response.read_body do |chunk|
|
155
|
+
io << chunk.encode!
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
else
|
160
|
+
response = remote_request(:get, uri)
|
161
|
+
io.fulfill_content_length(response.content_length) if io.respond_to?(:fulfill_content_length)
|
162
|
+
io.write(response.body)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def cursor
|
167
|
+
@last_response.http_headers["x-files-cursor"]
|
168
|
+
end
|
169
|
+
|
170
|
+
private def api_url(url = "", base_url = nil)
|
171
|
+
(base_url || Files.base_url) + "/api/rest/v1" + url
|
172
|
+
end
|
173
|
+
|
174
|
+
private def check_api_key!(api_key)
|
175
|
+
unless api_key
|
176
|
+
raise AuthenticationError, "No Files.com API key provided. " \
|
177
|
+
'Set your API key using "Files.api_key = <API-KEY>". ' \
|
178
|
+
"You can generate API keys from the Files.com's web interface. "
|
179
|
+
end
|
180
|
+
|
181
|
+
return unless api_key =~ /\s/
|
182
|
+
|
183
|
+
raise AuthenticationError, "Your API key is invalid (it contains whitespace)"
|
184
|
+
end
|
185
|
+
|
186
|
+
private def check_session_id!(session_id)
|
187
|
+
return unless session_id =~ /\s/
|
188
|
+
|
189
|
+
raise AuthenticationError, "The provided Session ID is invalid (it contains whitespace)"
|
190
|
+
end
|
191
|
+
|
192
|
+
def execute_request_with_rescues(base_url, context, skip_body_logging = false)
|
193
|
+
num_retries = 0
|
194
|
+
begin
|
195
|
+
request_start = Time.now
|
196
|
+
log_request(context, num_retries, skip_body_logging)
|
197
|
+
resp = yield
|
198
|
+
log_response(context, request_start, resp.status, resp.body, skip_body_logging)
|
199
|
+
rescue StandardError => e
|
200
|
+
error_context = context
|
201
|
+
|
202
|
+
if e.respond_to?(:response) && e.response
|
203
|
+
error_context = context
|
204
|
+
log_response(error_context, request_start,
|
205
|
+
e.response[:status], e.response[:body], skip_body_logging
|
206
|
+
)
|
207
|
+
else
|
208
|
+
log_response_error(error_context, request_start, e)
|
209
|
+
end
|
210
|
+
|
211
|
+
if self.class.should_retry?(e, num_retries)
|
212
|
+
num_retries += 1
|
213
|
+
sleep self.class.sleep_time(num_retries)
|
214
|
+
retry
|
215
|
+
end
|
216
|
+
|
217
|
+
case e
|
218
|
+
when Faraday::ClientError
|
219
|
+
if e.response
|
220
|
+
handle_error_response(e.response, error_context)
|
221
|
+
else
|
222
|
+
handle_network_error(e, error_context, num_retries, base_url)
|
223
|
+
end
|
224
|
+
|
225
|
+
else
|
226
|
+
raise
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
resp
|
231
|
+
end
|
232
|
+
|
233
|
+
private def general_api_error(status, body)
|
234
|
+
APIError.new("Unexpected response object from API: #{body.inspect} (HTTP response code was #{status})", http_status: status, http_body: body)
|
235
|
+
end
|
236
|
+
|
237
|
+
private def format_app_info(info)
|
238
|
+
str = info[:name]
|
239
|
+
str = "#{str}/#{info[:version]}" unless info[:version].nil?
|
240
|
+
str = "#{str} (#{info[:url]})" unless info[:url].nil?
|
241
|
+
str
|
242
|
+
end
|
243
|
+
|
244
|
+
private def handle_error_response(http_resp, context)
|
245
|
+
begin
|
246
|
+
resp = Response.from_faraday_hash(http_resp)
|
247
|
+
error_data = resp.data[:error] || resp.data[:errors]
|
248
|
+
error_data = error_data.first if error_data.is_a?(Array)
|
249
|
+
error_data = { message: error_data } if error_data.is_a?(String)
|
250
|
+
|
251
|
+
raise Error, "Unknown error" unless error_data
|
252
|
+
rescue JSON::ParserError, Error
|
253
|
+
raise general_api_error(http_resp[:status], http_resp[:body])
|
254
|
+
end
|
255
|
+
|
256
|
+
error = specific_api_error(resp, error_data, context)
|
257
|
+
|
258
|
+
error.response = resp
|
259
|
+
raise(error)
|
260
|
+
end
|
261
|
+
|
262
|
+
private def specific_api_error(resp, error_data, _context)
|
263
|
+
Util.log_error("API error", status: resp.http_status, error_message: error_data[:message])
|
264
|
+
|
265
|
+
opts = {
|
266
|
+
http_body: resp.http_body,
|
267
|
+
http_headers: resp.http_headers,
|
268
|
+
http_status: resp.http_status,
|
269
|
+
json_body: resp.data,
|
270
|
+
code: error_data[:code] || resp.http_status,
|
271
|
+
}
|
272
|
+
|
273
|
+
case resp.http_status
|
274
|
+
when 400, 404
|
275
|
+
InvalidRequestError.new(error_data[:message], opts)
|
276
|
+
when 401
|
277
|
+
AuthenticationError.new(error_data[:message], opts)
|
278
|
+
when 403
|
279
|
+
PermissionError.new(error_data[:message], opts)
|
280
|
+
when 429
|
281
|
+
RateLimitError.new(error_data[:message], opts)
|
282
|
+
else
|
283
|
+
APIError.new(error_data[:message], opts)
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
private def handle_network_error(error, context, num_retries, base_url = nil)
|
288
|
+
base_url ||= Files.base_url
|
289
|
+
|
290
|
+
Util.log_error("Network error", error_message: error.message, request_id: context.request_id)
|
291
|
+
message = "Could not connect to Files.com at URL #{base_url}. Please check your internet connection and try again. If this problem persists, you should check Files.com's service status at https://status.files.com, or contact your primary account representative."
|
292
|
+
message += " Request was retried #{num_retries} times." if num_retries > 0
|
293
|
+
message += "\n\n(Network error: #{error.message})"
|
294
|
+
|
295
|
+
raise APIConnectionError, message
|
296
|
+
end
|
297
|
+
|
298
|
+
private def request_headers(api_key, session_id, _method)
|
299
|
+
user_agent = "Files.com Ruby SDK v#{Files::VERSION}"
|
300
|
+
user_agent += " " + format_app_info(Files.app_info) unless Files.app_info.nil?
|
301
|
+
|
302
|
+
headers = {
|
303
|
+
"User-Agent" => user_agent,
|
304
|
+
"Content-Type" => "application/x-www-form-urlencoded",
|
305
|
+
}
|
306
|
+
headers["X-FilesAPI-Key"] = api_key if api_key
|
307
|
+
headers["X-FilesAPI-Auth"] = session_id if session_id
|
308
|
+
|
309
|
+
user_agent = @system_profiler.user_agent
|
310
|
+
begin
|
311
|
+
headers.update("X-Files-Client-User-Agent" => JSON.generate(user_agent))
|
312
|
+
rescue StandardError => e
|
313
|
+
headers.update(
|
314
|
+
"X-Files-Client-Raw-User-Agent" => user_agent.inspect,
|
315
|
+
error: "#{e} (#{e.class})"
|
316
|
+
)
|
317
|
+
end
|
318
|
+
|
319
|
+
headers
|
320
|
+
end
|
321
|
+
|
322
|
+
private def log_request(context, num_retries, no_body = false)
|
323
|
+
Util.log_info("Request", method: context.method, num_retries: num_retries, path: context.path)
|
324
|
+
Util.log_debug("Request details", body: context.body, query_params: context.query_params) unless no_body
|
325
|
+
end
|
326
|
+
|
327
|
+
private def log_response(context, request_start, status, body, no_body = false)
|
328
|
+
Util.log_info("Response", elapsed: Time.now - request_start, method: context.method, path: context.path, status: status)
|
329
|
+
Util.log_debug("Response details", body: body) unless no_body
|
330
|
+
end
|
331
|
+
|
332
|
+
private def log_response_error(context, request_start, error)
|
333
|
+
Util.log_error("Error", elapsed: Time.now - request_start, error_message: error.message, method: context.method, path: context.path)
|
334
|
+
end
|
335
|
+
|
336
|
+
class RequestLogContext
|
337
|
+
attr_accessor :body, :api_key, :method, :path, :query_params, :session_id
|
338
|
+
end
|
339
|
+
end
|
340
|
+
end
|