files.com 1.1.783 → 1.1.785

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7dc95932e61df91c8b29ba13bab73b9e81c4e272d999cad352a79b9af5880e8b
4
- data.tar.gz: 5a84da44b445097b6e4741cd3e969cc8eabbc281d75188908a1c3fcb7443b789
3
+ metadata.gz: 826a8d11a190abdb6361a77d984ee6fa10edd1a00ae53d6899e1d4caa82f9c95
4
+ data.tar.gz: 8b3e481cee2fff8e991e19af4fb18490e2a177f96cca16aabefdcc1b74a76efd
5
5
  SHA512:
6
- metadata.gz: e0da3ee8e594c0049fd065e1d5300d5d71d67000af4acb8a10ef8409fce08874ab785c69b0f57692f512bb3b3e1b37fec0e5c5aed44e3fcbdf84410d5ed0398b
7
- data.tar.gz: 82a14f3125a5317085b9261de7e4baf35dd1459921fd2c436855fc1f34f50fc1774fb4ea46576775d948e0e460b5c278949a6eea90fabd773fa4afffa1757f30
6
+ metadata.gz: 400c4fce89d964bf7b2ee6fba2059474fc5d54b5858ab07c8e5e7c7a5c21c62ed2998921d2b479dbbe4e22c89a58340d360b54156c8fab01be150a04c085308c
7
+ data.tar.gz: 7ec1d135b18a5f02b7c5b68d9dd02249608617e7bc4dc14a36093c602aa862bc4054f7ea7bdbbc8f7331d0a4bba39b626b691731f56cd6ca7f44a2f1fd6e7b9e
data/_VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.783
1
+ 1.1.785
@@ -8,8 +8,35 @@ module Files
8
8
  QUERY_METHODS = %i[get head delete].freeze
9
9
  private_constant :QUERY_METHODS
10
10
 
11
- def initialize(conn = nil)
11
+ # The default for an option this client does not set itself.
12
+ USE_GLOBAL = Object.new.freeze
13
+ private_constant :USE_GLOBAL
14
+
15
+ # Creates a client that sends requests through +conn+, or through a new default connection when +conn+ is nil.
16
+ #
17
+ # By default the client logs its ordinary API requests, responses and errors through the library-wide Files.logger and
18
+ # Files.log_level, read as each message is logged. Pass either option to give this client its own setting
19
+ # instead, without changing the library-wide one or any other client:
20
+ #
21
+ # logger:: A Logger, or any object with the same +log+ method, that receives this client's ordinary API messages. Its own
22
+ # level decides which are written, and the client never changes it. Pass nil for no logger even when
23
+ # Files.logger is set.
24
+ # log_level:: Without a logger, the lowest level written to standard output and error: nil, Files::LEVEL_DEBUG,
25
+ # Files::LEVEL_INFO or Files::LEVEL_ERROR, or "debug" or "info", as Files.log_level= accepts. It
26
+ # is ignored when the client has a logger. Pass nil to write nothing there even when
27
+ # Files.log_level is set.
28
+ #
29
+ # A client created with logger: nil, log_level: nil logs no ordinary API messages.
30
+ #
31
+ # Its API requests likewise name the library-wide Files.workspace_id, read for each request, unless
32
+ # +workspace_id+ gives this client its own workspace; 0 is sent as "0". A request's own workspace_id option still
33
+ # takes precedence, and a request whose workspace_id is nil uses this client's. Pass workspace_id: nil to name no
34
+ # workspace for this client's requests even when Files.workspace_id is set.
35
+ def initialize(conn = nil, logger: USE_GLOBAL, log_level: USE_GLOBAL, workspace_id: USE_GLOBAL)
12
36
  self.conn = conn || self.class.default_conn
37
+ @logger = logger
38
+ @log_level = log_level.equal?(USE_GLOBAL) ? log_level : Util.normalize_log_level(log_level)
39
+ @workspace_id = workspace_id
13
40
  @system_profiler = SystemProfiler.new
14
41
  @last_request_metrics = nil
15
42
  end
@@ -104,7 +131,7 @@ module Files
104
131
  else
105
132
  session_id = nil
106
133
  end
107
- workspace_id = Files.workspace_id if workspace_id.nil?
134
+ workspace_id = default_workspace_id if workspace_id.nil?
108
135
 
109
136
  if session_id and session_id != ""
110
137
  check_session_id!(session_id)
@@ -375,7 +402,7 @@ module Files
375
402
  else
376
403
  "The Files.com API returned an error without a message (HTTP response code was #{resp.http_status})"
377
404
  end
378
- Util.log_error("API error", status: resp.http_status, error_message: message)
405
+ Util.log_error("API error", { status: resp.http_status, error_message: message }, log_settings)
379
406
 
380
407
  api_error_class(resp.data[:type]).new(message,
381
408
  http_body: resp.http_body,
@@ -462,7 +489,7 @@ module Files
462
489
  error.message.empty? ? error.response[:body] : error.message
463
490
  end
464
491
 
465
- Util.log_error("Network error", error_message: error_message)
492
+ Util.log_error("Network error", { error_message: error_message }, log_settings)
466
493
  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."
467
494
  message += " Request was retried #{num_retries} times." if num_retries > 0
468
495
  message += "\n\n(Network error: #{error_message})"
@@ -508,18 +535,30 @@ module Files
508
535
  end
509
536
 
510
537
  private def log_request(context, num_retries, no_body: false)
511
- Util.log_info("Request", method: context.method, num_retries: num_retries, path: context.path)
512
- Util.log_debug("Request details", body: context.body, query_params: context.query_params) unless no_body
538
+ Util.log_info("Request", { method: context.method, num_retries: num_retries, path: context.path }, log_settings)
539
+ Util.log_debug("Request details", { body: context.body, query_params: context.query_params }, log_settings) unless no_body
513
540
  end
514
541
 
515
542
  private def log_response(context, request_start, status, body, no_body: false)
516
- Util.log_info("Response", elapsed: Time.now - request_start, method: context.method, path: context.path, status: status)
517
- Util.log_debug("Response details", body: body) unless no_body
543
+ Util.log_info("Response", { elapsed: Time.now - request_start, method: context.method, path: context.path, status: status }, log_settings)
544
+ Util.log_debug("Response details", { body: body }, log_settings) unless no_body
518
545
  end
519
546
 
520
547
  private def log_response_error(context, request_start, error)
521
- Util.log_error("Error", elapsed: Time.now - request_start, error_type: error.class, method: context.method, path: context.path)
522
- Util.log_debug("Error details", error_message: error.message)
548
+ Util.log_error("Error", { elapsed: Time.now - request_start, error_type: error.class, method: context.method, path: context.path }, log_settings)
549
+ Util.log_debug("Error details", { error_message: error.message }, log_settings)
550
+ end
551
+
552
+ # This client's own logger and level where it set them, and the library-wide ones otherwise.
553
+ private def log_settings
554
+ Util::LogSettings.new(@logger.equal?(USE_GLOBAL) ? Files.logger : @logger,
555
+ @log_level.equal?(USE_GLOBAL) ? Files.log_level : @log_level
556
+ )
557
+ end
558
+
559
+ # This client's own workspace where it set one, and the library-wide one otherwise.
560
+ private def default_workspace_id
561
+ @workspace_id.equal?(USE_GLOBAL) ? Files.workspace_id : @workspace_id
523
562
  end
524
563
 
525
564
  class RequestLogContext
@@ -34,29 +34,48 @@ module Files
34
34
  end
35
35
  end
36
36
 
37
- def self.log_error(message, data = {})
38
- if !Files.logger.nil? ||
39
- (!Files.log_level.nil? && Files.log_level <= Files::LEVEL_ERROR)
37
+ # Where log messages go: to +logger+ when there is one, which then filters by its own level, and otherwise to
38
+ # standard output or error at +log_level+ and above. A nil +log_level+ without a logger logs nothing.
39
+ LogSettings = Struct.new(:logger, :log_level)
40
+
41
+ # The library-wide Files.logger and Files.log_level, read when each message is logged.
42
+ def self.global_log_settings
43
+ LogSettings.new(Files.logger, Files.log_level)
44
+ end
45
+
46
+ # The same values Files.log_level= accepts: nil, a level constant, or "debug" or "info".
47
+ def self.normalize_log_level(val)
48
+ val = { "debug" => LEVEL_DEBUG, "info" => LEVEL_INFO }.fetch(val, val)
49
+ if !val.nil? && ![ LEVEL_DEBUG, LEVEL_ERROR, LEVEL_INFO ].include?(val)
50
+ raise ArgumentError,
51
+ "log_level should only be set to `nil`, `debug` or `info`"
52
+ end
53
+ val
54
+ end
55
+
56
+ def self.log_error(message, data = {}, settings = global_log_settings)
57
+ if !settings.logger.nil? ||
58
+ (!settings.log_level.nil? && settings.log_level <= Files::LEVEL_ERROR)
40
59
  log_internal(message, data, color: :cyan, level: Files::LEVEL_ERROR,
41
- logger: Files.logger, out: $stderr
60
+ logger: settings.logger, out: $stderr
42
61
  )
43
62
  end
44
63
  end
45
64
 
46
- def self.log_info(message, data = {})
47
- if !Files.logger.nil? ||
48
- (!Files.log_level.nil? && Files.log_level <= Files::LEVEL_INFO)
65
+ def self.log_info(message, data = {}, settings = global_log_settings)
66
+ if !settings.logger.nil? ||
67
+ (!settings.log_level.nil? && settings.log_level <= Files::LEVEL_INFO)
49
68
  log_internal(message, data, color: :cyan, level: Files::LEVEL_INFO,
50
- logger: Files.logger, out: $stdout
69
+ logger: settings.logger, out: $stdout
51
70
  )
52
71
  end
53
72
  end
54
73
 
55
- def self.log_debug(message, data = {})
56
- if !Files.logger.nil? ||
57
- (!Files.log_level.nil? && Files.log_level <= Files::LEVEL_DEBUG)
74
+ def self.log_debug(message, data = {}, settings = global_log_settings)
75
+ if !settings.logger.nil? ||
76
+ (!settings.log_level.nil? && settings.log_level <= Files::LEVEL_DEBUG)
58
77
  log_internal(message, data, color: :blue, level: Files::LEVEL_DEBUG,
59
- logger: Files.logger, out: $stdout
78
+ logger: settings.logger, out: $stdout
60
79
  )
61
80
  end
62
81
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Files
4
- VERSION = "1.1.783"
4
+ VERSION = "1.1.785"
5
5
  end
@@ -344,6 +344,103 @@ RSpec.describe Files::ApiClient do
344
344
  end
345
345
  end
346
346
 
347
+ describe "client loggers" do
348
+ around do |example|
349
+ original = [ Files.logger, Files.log_level, Files.max_network_retries ]
350
+ Files.max_network_retries = 0
351
+ example.run
352
+ ensure
353
+ Files.logger, Files.log_level, Files.max_network_retries = original
354
+ end
355
+
356
+ it "logs each client's requests to its own logger without changing the library-wide settings" do
357
+ connection = Faraday.new do |builder|
358
+ builder.response :raise_error
359
+ builder.adapter :test do |stub|
360
+ (1..5).each do |id|
361
+ stub.get("/api/rest/v1/workspaces/#{id}") { [ 200, { "Content-Type" => "application/json" }, { id: id }.to_json ] }
362
+ end
363
+ stub.get("/api/rest/v1/workspaces/6") { [ 404, { "Content-Type" => "application/json" }, { error: "Not Found", type: "not-found" }.to_json ] }
364
+ end
365
+ end
366
+ global_output = StringIO.new
367
+ global_logger = Logger.new(global_output, level: Logger::INFO)
368
+ Files.logger = global_logger
369
+ Files.log_level = Files::LEVEL_INFO
370
+ a_output = StringIO.new
371
+ a_logger = Logger.new(a_output, level: Logger::INFO)
372
+ b_output = StringIO.new
373
+ b_logger = Logger.new(b_output, level: Logger::ERROR)
374
+ client_a = described_class.new(connection, logger: a_logger)
375
+ client_b = described_class.new(connection, logger: b_logger, log_level: Files::LEVEL_DEBUG)
376
+
377
+ Files::Workspace.find(1, {}, api_key: "key", client: client_a)
378
+ Files::Workspace.find(2, {}, api_key: "key", client: client_b)
379
+ Files::Workspace.find(3, {}, api_key: "key", client: client_a)
380
+
381
+ expect(a_output.string).to include("message=Request method=get num_retries=0 path=/workspaces/1", "message=Request method=get num_retries=0 path=/workspaces/3")
382
+ expect(a_output.string.scan(/message=Response .*status=200/).size).to eq(2)
383
+ expect(a_output.string).not_to include("/workspaces/2")
384
+ expect(b_output.string).to be_empty
385
+ expect(global_output.string).to be_empty
386
+ expect([ a_logger.level, b_logger.level ]).to eq([ Logger::INFO, Logger::ERROR ])
387
+ expect(Files.logger).to equal(global_logger)
388
+ expect(Files.log_level).to eq(Files::LEVEL_INFO)
389
+
390
+ expect { Files::Workspace.find(6, {}, api_key: "key", client: client_b) }.to raise_error(Files::NotFoundError)
391
+ expect(b_output.string).to include("message=API error status=404")
392
+ expect(a_output.string).not_to include("API error")
393
+ expect(global_output.string).to be_empty
394
+ expect([ a_logger.level, b_logger.level ]).to eq([ Logger::INFO, Logger::ERROR ])
395
+
396
+ Files::Workspace.find(4, {}, api_key: "key", client: described_class.new(connection))
397
+ expect(global_output.string).to include("message=Request method=get num_retries=0 path=/workspaces/4")
398
+
399
+ quiet_client = described_class.new(connection, logger: nil, log_level: nil)
400
+ expect { Files::Workspace.find(5, {}, api_key: "key", client: quiet_client) }.not_to output.to_stdout
401
+ expect(global_output.string).not_to include("/workspaces/5")
402
+ end
403
+ end
404
+
405
+ describe "client workspaces" do
406
+ around do |example|
407
+ original = Files.workspace_id
408
+ example.run
409
+ ensure
410
+ Files.workspace_id = original
411
+ end
412
+
413
+ it "sends each client's own workspace, and the library-wide one for clients without their own" do
414
+ workspace_headers = []
415
+ connection = Faraday.new do |builder|
416
+ builder.response :raise_error
417
+ builder.adapter :test do |stub|
418
+ stub.get("/api/rest/v1/workspaces/1") do |env|
419
+ workspace_headers << env.request_headers["X-Files-Workspace-Id"]
420
+ [ 200, { "Content-Type" => "application/json" }, { id: 1 }.to_json ]
421
+ end
422
+ end
423
+ end
424
+ find_workspace = ->(client, **options) { Files::Workspace.find(1, {}, api_key: "key", client: client, **options) }
425
+ Files.workspace_id = 5
426
+ client_a = described_class.new(connection, workspace_id: 0)
427
+ client_b = described_class.new(connection, workspace_id: 7)
428
+
429
+ find_workspace.call(client_a)
430
+ find_workspace.call(client_b)
431
+ Files.workspace_id = 6
432
+ find_workspace.call(client_a)
433
+ find_workspace.call(described_class.new(connection))
434
+ find_workspace.call(described_class.new(connection, workspace_id: nil))
435
+ find_workspace.call(client_b, workspace_id: 0)
436
+ find_workspace.call(client_b, workspace_id: "")
437
+ find_workspace.call(client_b, workspace_id: nil)
438
+
439
+ expect(workspace_headers).to eq([ "0", "7", "0", "6", nil, "0", nil, "7" ])
440
+ expect(Files.workspace_id).to eq(6)
441
+ end
442
+ end
443
+
347
444
  describe "network timeouts" do
348
445
  around do |example|
349
446
  original = [ Files.open_timeout, Files.read_timeout ]
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: files.com
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.783
4
+ version: 1.1.785
5
5
  platform: ruby
6
6
  authors:
7
7
  - files.com