files.com 1.0.90
Sign up to get free protection for your applications and to get access to all the features.
- 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 +57 -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 +101 -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
|