files.com 1.1.744 → 1.1.746
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 +4 -4
- data/_VERSION +1 -1
- data/docs/behavior.md +1 -1
- data/lib/files.com/api_client.rb +33 -16
- data/lib/files.com/models/behavior.rb +1 -1
- data/lib/files.com/version.rb +1 -1
- data/spec/lib/api_client_spec.rb +61 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 55f688340c1d5973051508e5030a9eaa3a068cd65b27358049faeaf9443350ef
|
|
4
|
+
data.tar.gz: e9bc4c9ab0eaa231d1d789feb4a6033e46ea497ac83bb29f725ceaddca79eb68
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2086b7cdd11439fead79be67ec193427434fc2beedc231242953c485c9e6549bc5c1713f7e3d8be952398073ef51acd579180b112a552469504416913a7e5446
|
|
7
|
+
data.tar.gz: 9e403669a84f1914de24bc82296fd6b770cf67f80bad8d3886cfa0e479e6763e67a87cc61420eb4136fa2f21f17911daafefb31b03a57bbb6f7274f08103a4f6
|
data/_VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.1.
|
|
1
|
+
1.1.746
|
data/docs/behavior.md
CHANGED
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
* `name` (string): Name for this behavior.
|
|
34
34
|
* `description` (string): Description for this behavior.
|
|
35
35
|
* `value` (object): Settings for this behavior. See the section above for an example value to provide here. Formatting is different for each Behavior type. May be sent as nested JSON or a single JSON-encoded string. If using XML encoding for the API call, this data must be sent as a JSON-encoded string.
|
|
36
|
-
* `public_hosting_url` (string): Public URL for this publicly hosted folder when the `Serve Publicly` behavior has a key configured. When a Custom Domain with `public_hosting` destination is attached to this behavior, the URL uses that domain. Otherwise it uses the site's `subdomain.hosted-by-files.com` host, unless `custom_domain_only` is enabled, in which case no URL is returned.
|
|
36
|
+
* `public_hosting_url` (string): Public URL for this publicly hosted folder when the `Serve Publicly` behavior has a key configured. Only returned to site admins or users with admin permission on this folder. When a Custom Domain with `public_hosting` destination is attached to this behavior, the URL uses that domain. Otherwise it uses the site's `subdomain.hosted-by-files.com` host, unless `custom_domain_only` is enabled, in which case no URL is returned.
|
|
37
37
|
* `disable_parent_folder_behavior` (boolean): If true, disables the inherited behavior for this folder and its children. Valid only for behavior types that child folders may override, and requires recursive to be true. Rejected for all other behavior types.
|
|
38
38
|
* `recursive` (boolean): Whether this behavior is recursive for this record. `always` behaviors are always `true`, `never` behaviors are always `false`, and `sometimes` behaviors may be either value.
|
|
39
39
|
* `inherited` (boolean): If true, this behavior is inherited from a higher scope rather than owned by the requested workspace.
|
data/lib/files.com/api_client.rb
CHANGED
|
@@ -150,9 +150,10 @@ module Files
|
|
|
150
150
|
def remote_request(method, url, headers = {}, body = nil)
|
|
151
151
|
context = RequestLogContext.new
|
|
152
152
|
context.method = method
|
|
153
|
-
context.path =
|
|
153
|
+
context.path = "[transfer]"
|
|
154
|
+
Util.log_debug("Transfer request", method: method, url: url)
|
|
154
155
|
|
|
155
|
-
execute_request_with_rescues(Files.base_url, context, skip_body_logging: true) do
|
|
156
|
+
execute_request_with_rescues(Files.base_url, context, skip_body_logging: true, is_transfer: true) do
|
|
156
157
|
conn.run_request(method, url, body, headers) do |req|
|
|
157
158
|
req.options.open_timeout = Files.open_timeout
|
|
158
159
|
req.options.timeout = Files.read_timeout
|
|
@@ -215,7 +216,7 @@ module Files
|
|
|
215
216
|
raise AuthenticationError, "The provided Session ID is invalid (it contains whitespace)"
|
|
216
217
|
end
|
|
217
218
|
|
|
218
|
-
def execute_request_with_rescues(base_url, context, skip_body_logging: false)
|
|
219
|
+
def execute_request_with_rescues(base_url, context, skip_body_logging: false, is_transfer: false)
|
|
219
220
|
num_retries = 0
|
|
220
221
|
begin
|
|
221
222
|
request_start = Time.now
|
|
@@ -240,14 +241,18 @@ module Files
|
|
|
240
241
|
retry
|
|
241
242
|
end
|
|
242
243
|
|
|
244
|
+
Util.log_debug("Transfer error details", error_message: e.message) if is_transfer
|
|
245
|
+
|
|
243
246
|
case e
|
|
244
247
|
when Faraday::ClientError, Faraday::ServerError
|
|
245
248
|
if e.response and has_error_type?(e)
|
|
246
|
-
handle_error_response(e.response, error_context)
|
|
249
|
+
handle_error_response(e.response, error_context, is_transfer: is_transfer)
|
|
247
250
|
else
|
|
248
|
-
handle_network_error(e, error_context, num_retries, base_url)
|
|
251
|
+
handle_network_error(e, error_context, num_retries, base_url, is_transfer: is_transfer)
|
|
249
252
|
end
|
|
250
253
|
else
|
|
254
|
+
raise e.exception("Transfer request failed (#{e.class})"), cause: nil if is_transfer
|
|
255
|
+
|
|
251
256
|
raise
|
|
252
257
|
end
|
|
253
258
|
end
|
|
@@ -266,7 +271,7 @@ module Files
|
|
|
266
271
|
str
|
|
267
272
|
end
|
|
268
273
|
|
|
269
|
-
private def handle_error_response(http_resp, context)
|
|
274
|
+
private def handle_error_response(http_resp, context, is_transfer: false)
|
|
270
275
|
begin
|
|
271
276
|
resp = Response.from_faraday_hash(http_resp)
|
|
272
277
|
error_data = resp.data[:error] || resp.data[:errors]
|
|
@@ -275,17 +280,22 @@ module Files
|
|
|
275
280
|
|
|
276
281
|
raise Error, "Unknown error" unless error_data
|
|
277
282
|
rescue JSON::ParserError, Error
|
|
283
|
+
raise APIError.new("Transfer request failed", http_status: http_resp[:status]), cause: nil if is_transfer
|
|
284
|
+
|
|
278
285
|
raise general_api_error(http_resp[:status], http_resp[:body])
|
|
279
286
|
end
|
|
280
287
|
|
|
281
|
-
error = specific_api_error(resp, error_data, context)
|
|
288
|
+
error = specific_api_error(resp, error_data, context, is_transfer: is_transfer)
|
|
282
289
|
|
|
283
290
|
error.response = resp
|
|
284
|
-
raise
|
|
291
|
+
raise error, cause: nil if is_transfer
|
|
292
|
+
|
|
293
|
+
raise error
|
|
285
294
|
end
|
|
286
295
|
|
|
287
|
-
private def specific_api_error(resp, error_data, _context)
|
|
288
|
-
|
|
296
|
+
private def specific_api_error(resp, error_data, _context, is_transfer: false)
|
|
297
|
+
message = is_transfer ? "Transfer request failed" : error_data[:message]
|
|
298
|
+
Util.log_error("API error", status: resp.http_status, error_message: message)
|
|
289
299
|
|
|
290
300
|
opts = {
|
|
291
301
|
http_body: resp.http_body,
|
|
@@ -295,28 +305,34 @@ module Files
|
|
|
295
305
|
code: error_data[:code] || resp.http_status,
|
|
296
306
|
}
|
|
297
307
|
|
|
298
|
-
return APIError.new(
|
|
308
|
+
return APIError.new(message, **opts) unless resp&.data&.dig(:type)
|
|
299
309
|
|
|
300
310
|
begin
|
|
301
311
|
error_type = resp.data[:type].split("/").last
|
|
302
312
|
error_class_name = "#{error_type.split("-").map(&:capitalize).join}Error"
|
|
303
313
|
error_class = Files.const_get(error_class_name)
|
|
304
|
-
error_class.new(
|
|
314
|
+
error_class.new(message, **opts)
|
|
305
315
|
rescue NameError
|
|
306
|
-
APIError.new(
|
|
316
|
+
APIError.new(message, **opts)
|
|
307
317
|
end
|
|
308
318
|
end
|
|
309
319
|
|
|
310
|
-
private def handle_network_error(error, _context, num_retries, base_url = nil)
|
|
320
|
+
private def handle_network_error(error, _context, num_retries, base_url = nil, is_transfer: false)
|
|
311
321
|
base_url ||= Files.base_url
|
|
312
322
|
|
|
313
|
-
error_message =
|
|
323
|
+
error_message = if is_transfer
|
|
324
|
+
error.class.name
|
|
325
|
+
else
|
|
326
|
+
error.message.empty? ? error.response[:body] : error.message
|
|
327
|
+
end
|
|
314
328
|
|
|
315
329
|
Util.log_error("Network error", error_message: error_message)
|
|
316
330
|
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."
|
|
317
331
|
message += " Request was retried #{num_retries} times." if num_retries > 0
|
|
318
332
|
message += "\n\n(Network error: #{error_message})"
|
|
319
333
|
|
|
334
|
+
raise APIConnectionError, message, cause: nil if is_transfer
|
|
335
|
+
|
|
320
336
|
raise APIConnectionError, message
|
|
321
337
|
end
|
|
322
338
|
|
|
@@ -372,7 +388,8 @@ module Files
|
|
|
372
388
|
end
|
|
373
389
|
|
|
374
390
|
private def log_response_error(context, request_start, error)
|
|
375
|
-
Util.log_error("Error", elapsed: Time.now - request_start,
|
|
391
|
+
Util.log_error("Error", elapsed: Time.now - request_start, error_type: error.class, method: context.method, path: context.path)
|
|
392
|
+
Util.log_debug("Error details", error_message: error.message)
|
|
376
393
|
end
|
|
377
394
|
|
|
378
395
|
class RequestLogContext
|
|
@@ -72,7 +72,7 @@ module Files
|
|
|
72
72
|
@attributes[:value] = value
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
-
# string - Public URL for this publicly hosted folder when the `Serve Publicly` behavior has a key configured. When a Custom Domain with `public_hosting` destination is attached to this behavior, the URL uses that domain. Otherwise it uses the site's `subdomain.hosted-by-files.com` host, unless `custom_domain_only` is enabled, in which case no URL is returned.
|
|
75
|
+
# string - Public URL for this publicly hosted folder when the `Serve Publicly` behavior has a key configured. Only returned to site admins or users with admin permission on this folder. When a Custom Domain with `public_hosting` destination is attached to this behavior, the URL uses that domain. Otherwise it uses the site's `subdomain.hosted-by-files.com` host, unless `custom_domain_only` is enabled, in which case no URL is returned.
|
|
76
76
|
def public_hosting_url
|
|
77
77
|
@attributes[:public_hosting_url]
|
|
78
78
|
end
|
data/lib/files.com/version.rb
CHANGED
data/spec/lib/api_client_spec.rb
CHANGED
|
@@ -3,6 +3,67 @@ require "spec_helper"
|
|
|
3
3
|
RSpec.describe Files::ApiClient do
|
|
4
4
|
let(:subject) { described_class.new }
|
|
5
5
|
|
|
6
|
+
describe "signed transfer privacy" do
|
|
7
|
+
it "keeps API error types without echoing a transfer URL" do
|
|
8
|
+
url = "https://transfer.example.test/private-part?signature=secret"
|
|
9
|
+
output = StringIO.new
|
|
10
|
+
original_logger = Files.logger
|
|
11
|
+
Files.logger = Logger.new(output, level: Logger::INFO)
|
|
12
|
+
connection = Faraday.new do |builder|
|
|
13
|
+
builder.response :raise_error
|
|
14
|
+
builder.adapter :test do |stub|
|
|
15
|
+
stub.put(url) { [ 404, {}, { type: "not-found", error: url }.to_json ] }
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
expect { described_class.new(connection).remote_request(:put, url, {}, "part") }.to raise_error(Files::NotFoundError) { |error|
|
|
20
|
+
expect(error.http_status).to eq(404)
|
|
21
|
+
expect(error.full_message).not_to include(url, "private-part", "secret")
|
|
22
|
+
}
|
|
23
|
+
expect(output.string).not_to include(url, "private-part", "secret")
|
|
24
|
+
ensure
|
|
25
|
+
Files.logger = original_logger
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "keeps the URL out of normal logs and errors while retaining retries and debug details" do
|
|
29
|
+
url = "https://transfer.example.test/private-part?X-Amz-Credential=credential&X-Amz-Signature=signature"
|
|
30
|
+
original_logger = Files.logger
|
|
31
|
+
original_retries = Files.max_network_retries
|
|
32
|
+
original_delay = Files.initial_network_retry_delay
|
|
33
|
+
Files.max_network_retries = 1
|
|
34
|
+
Files.initial_network_retry_delay = 0
|
|
35
|
+
|
|
36
|
+
[ Logger::INFO, Logger::DEBUG ].each do |level|
|
|
37
|
+
output = StringIO.new
|
|
38
|
+
Files.logger = Logger.new(output, level: level)
|
|
39
|
+
attempts = 0
|
|
40
|
+
connection = Faraday.new do |builder|
|
|
41
|
+
builder.adapter :test do |stub|
|
|
42
|
+
stub.put(url) do
|
|
43
|
+
attempts += 1
|
|
44
|
+
raise Faraday::ConnectionFailed, "connection reset for #{url}"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
expect { described_class.new(connection).remote_request(:put, url, {}, "part") }.to raise_error(Faraday::ConnectionFailed) { |error|
|
|
50
|
+
expect(error.full_message).not_to include(url, "private-part", "credential", "signature")
|
|
51
|
+
}
|
|
52
|
+
expect(attempts).to eq(2)
|
|
53
|
+
if level == Logger::INFO
|
|
54
|
+
expect(output.string).to include("Request", "Error")
|
|
55
|
+
expect(output.string).not_to include(url, "private-part", "credential", "signature")
|
|
56
|
+
else
|
|
57
|
+
expect(output.string).to include(url, "connection reset")
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
ensure
|
|
61
|
+
Files.logger = original_logger
|
|
62
|
+
Files.max_network_retries = original_retries
|
|
63
|
+
Files.initial_network_retry_delay = original_delay
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
6
67
|
describe "#execute_request_with_rescues" do
|
|
7
68
|
let(:context) { double('context', method: 'some method', path: 'some path') }
|
|
8
69
|
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: files.com
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.1.
|
|
4
|
+
version: 1.1.746
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- files.com
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-09-
|
|
11
|
+
date: 2026-09-24 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: addressable
|