vers-sdk 0.1.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: fcd95ab2bbc99d7f862a7e7f7077df93bac046a3608c0bfafe49bbe67a418565
4
+ data.tar.gz: 4fda1d96575e7df3a08dd58d5bca38800778d3fe8400702903d5e0e07d7221e9
5
+ SHA512:
6
+ metadata.gz: db9b02fc029aa89031586a14970458f656c2de5f7d02aabd4832417155765e7bfb521a387728b8b6a433b6c17b6549b510d9a2dd683f6f9bd517a4072aa9e8a9
7
+ data.tar.gz: 3f36deb765eea09d2fabbe31891b3a07788a990c9129cdf201d538bd48af8cf9dbaef72abb31062c38c488df1dec09a98e103bc9adecb3385ecf936cbd2daff1
@@ -0,0 +1,593 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Generated by Sterling SDK Generator
4
+ # Orchestrator Control Plane API v0.1.0
5
+
6
+ require "net/http"
7
+ require "uri"
8
+ require "json"
9
+ require "securerandom"
10
+ require "logger"
11
+ require "time"
12
+
13
+ require_relative "errors"
14
+ require_relative "models"
15
+
16
+ module VersSdk
17
+ # Per-request options that override client defaults.
18
+ class RequestOptions
19
+ attr_accessor :headers, :timeout
20
+
21
+ def initialize(headers: nil, timeout: nil)
22
+ @headers = headers
23
+ @timeout = timeout
24
+ end
25
+ end
26
+
27
+ LOG_LEVEL_MAP = {
28
+ "debug" => Logger::DEBUG,
29
+ "info" => Logger::INFO,
30
+ "warn" => Logger::WARN,
31
+ "error" => Logger::ERROR,
32
+ "off" => Logger::UNKNOWN
33
+ }.freeze
34
+
35
+ RETRYABLE_STATUS_CODES = [408, 409, 429].freeze
36
+
37
+ # HTTP client for Orchestrator Control Plane API.
38
+ class VersSdkClient
39
+ attr_reader :base_url, :max_retries, :timeout
40
+
41
+ # @param base_url [String, nil] API base URL (default: VERS_BASE_URL env or https://api.vers.sh)
42
+ # @param api_key [String, nil] Bearer token (default: VERS_API_KEY env)
43
+ # @param max_retries [Integer] Maximum number of retries (default: 2)
44
+ # @param timeout [Numeric] Request timeout in seconds (default: 30)
45
+ def initialize(base_url: nil, api_key: nil, max_retries: 2, timeout: 30)
46
+ @base_url = base_url || ENV.fetch("VERS_BASE_URL", "https://api.vers.sh")
47
+ @api_key = api_key || ENV["VERS_API_KEY"]
48
+ @max_retries = max_retries
49
+ @timeout = timeout
50
+ @logger = self.class.build_logger
51
+ @default_headers = {
52
+ "Content-Type" => "application/json",
53
+ "Accept" => "application/json",
54
+ "User-Agent" => "vers-sdk/0.1.1 ruby/#{RUBY_VERSION} #{RUBY_PLATFORM}"
55
+ }
56
+ @default_headers["Authorization"] = "Bearer #{@api_key}" if @api_key
57
+ end
58
+
59
+ def self.build_logger
60
+ logger = Logger.new($stdout)
61
+ env_level = ENV.fetch("VERS_LOG", "warn").downcase
62
+ logger.level = LOG_LEVEL_MAP.fetch(env_level, Logger::WARN)
63
+ logger.formatter = proc { |severity, datetime, _progname, msg|
64
+ "#{datetime} [vers_sdk #{severity}] #{msg}\n"
65
+ }
66
+ logger
67
+ end
68
+
69
+ #
70
+ def resize_vm_disk(vm_id, body: nil, params: nil, options: nil)
71
+ path = sprintf("/api/v1/vm/%s/disk", vm_id)
72
+ query = {}
73
+ if params
74
+ h = params.is_a?(Hash) ? params : params.to_h
75
+ h.each { |k, v| query[k.to_s] = v.to_s unless v.nil? }
76
+ end
77
+ request("PATCH", path, options: options, body: body, query: query)
78
+ end
79
+
80
+ #
81
+ def exec_vm_stream_attach(vm_id, body: nil, options: nil)
82
+ path = sprintf("/api/v1/vm/%s/exec/stream/attach", vm_id)
83
+ request("POST", path, options: options, body: body)
84
+ end
85
+
86
+ #
87
+ def create_new_root_vm(body: nil, params: nil, options: nil)
88
+ path = "/api/v1/vm/new_root"
89
+ query = {}
90
+ if params
91
+ h = params.is_a?(Hash) ? params : params.to_h
92
+ h.each { |k, v| query[k.to_s] = v.to_s unless v.nil? }
93
+ end
94
+ request("POST", path, options: options, body: body, query: query)
95
+ end
96
+
97
+ #
98
+ def vm_logs(vm_id, params: nil, options: nil)
99
+ path = sprintf("/api/v1/vm/%s/logs", vm_id)
100
+ query = {}
101
+ if params
102
+ h = params.is_a?(Hash) ? params : params.to_h
103
+ h.each { |k, v| query[k.to_s] = v.to_s unless v.nil? }
104
+ end
105
+ request("GET", path, options: options, query: query)
106
+ end
107
+
108
+ #
109
+ def branch_by_ref(repo_name, tag_name, body: nil, params: nil, options: nil)
110
+ path = sprintf("/api/v1/vm/branch/by_ref/%s/%s", repo_name, tag_name)
111
+ query = {}
112
+ if params
113
+ h = params.is_a?(Hash) ? params : params.to_h
114
+ h.each { |k, v| query[k.to_s] = v.to_s unless v.nil? }
115
+ end
116
+ request("POST", path, options: options, body: body, query: query)
117
+ end
118
+
119
+ #
120
+ def list_public_commits(options: nil)
121
+ path = "/api/v1/commits/public"
122
+ request("GET", path, options: options)
123
+ end
124
+
125
+ #
126
+ def list_public_repo_tags(org_name, repo_name, options: nil)
127
+ path = sprintf("/api/v1/public/repositories/%s/%s/tags", org_name, repo_name)
128
+ request("GET", path, options: options)
129
+ end
130
+
131
+ #
132
+ def branch_by_tag(tag_name, body: nil, params: nil, options: nil)
133
+ path = sprintf("/api/v1/vm/branch/by_tag/%s", tag_name)
134
+ query = {}
135
+ if params
136
+ h = params.is_a?(Hash) ? params : params.to_h
137
+ h.each { |k, v| query[k.to_s] = v.to_s unless v.nil? }
138
+ end
139
+ request("POST", path, options: options, body: body, query: query)
140
+ end
141
+
142
+ #
143
+ def branch_vm(vm_or_commit_id, body: nil, params: nil, options: nil)
144
+ path = sprintf("/api/v1/vm/%s/branch", vm_or_commit_id)
145
+ query = {}
146
+ if params
147
+ h = params.is_a?(Hash) ? params : params.to_h
148
+ h.each { |k, v| query[k.to_s] = v.to_s unless v.nil? }
149
+ end
150
+ request("POST", path, options: options, body: body, query: query)
151
+ end
152
+
153
+ #
154
+ def get_repo_tag(repo_name, tag_name, options: nil)
155
+ path = sprintf("/api/v1/repositories/%s/tags/%s", repo_name, tag_name)
156
+ request("GET", path, options: options)
157
+ end
158
+
159
+ #
160
+ def delete_repo_tag(repo_name, tag_name, options: nil)
161
+ path = sprintf("/api/v1/repositories/%s/tags/%s", repo_name, tag_name)
162
+ request("DELETE", path, options: options)
163
+ end
164
+
165
+ #
166
+ def update_repo_tag(repo_name, tag_name, body: nil, options: nil)
167
+ path = sprintf("/api/v1/repositories/%s/tags/%s", repo_name, tag_name)
168
+ request("PATCH", path, options: options, body: body)
169
+ end
170
+
171
+ #
172
+ def get_repository(repo_name, options: nil)
173
+ path = sprintf("/api/v1/repositories/%s", repo_name)
174
+ request("GET", path, options: options)
175
+ end
176
+
177
+ #
178
+ def delete_repository(repo_name, options: nil)
179
+ path = sprintf("/api/v1/repositories/%s", repo_name)
180
+ request("DELETE", path, options: options)
181
+ end
182
+
183
+ #
184
+ def list_parent_commits(commit_id, options: nil)
185
+ path = sprintf("/api/v1/vm/commits/%s/parents", commit_id)
186
+ request("GET", path, options: options)
187
+ end
188
+
189
+ #
190
+ def update_vm_state(vm_id, body: nil, params: nil, options: nil)
191
+ path = sprintf("/api/v1/vm/%s/state", vm_id)
192
+ query = {}
193
+ if params
194
+ h = params.is_a?(Hash) ? params : params.to_h
195
+ h.each { |k, v| query[k.to_s] = v.to_s unless v.nil? }
196
+ end
197
+ request("PATCH", path, options: options, body: body, query: query)
198
+ end
199
+
200
+ #
201
+ def fork_repository(body: nil, options: nil)
202
+ path = "/api/v1/repositories/fork"
203
+ request("POST", path, options: options, body: body)
204
+ end
205
+
206
+ #
207
+ def list_repositories(options: nil)
208
+ path = "/api/v1/repositories"
209
+ request("GET", path, options: options)
210
+ end
211
+
212
+ #
213
+ def create_repository(body: nil, options: nil)
214
+ path = "/api/v1/repositories"
215
+ request("POST", path, options: options, body: body)
216
+ end
217
+
218
+ #
219
+ def restore_from_commit(body: nil, options: nil)
220
+ path = "/api/v1/vm/from_commit"
221
+ request("POST", path, options: options, body: body)
222
+ end
223
+
224
+ #
225
+ def get_domain(domain_id, options: nil)
226
+ path = sprintf("/api/v1/domains/%s", domain_id)
227
+ request("GET", path, options: options)
228
+ end
229
+
230
+ #
231
+ def delete_domain(domain_id, options: nil)
232
+ path = sprintf("/api/v1/domains/%s", domain_id)
233
+ request("DELETE", path, options: options)
234
+ end
235
+
236
+ #
237
+ def list_tags(options: nil)
238
+ path = "/api/v1/commit_tags"
239
+ request("GET", path, options: options)
240
+ end
241
+
242
+ #
243
+ def create_tag(body: nil, options: nil)
244
+ path = "/api/v1/commit_tags"
245
+ request("POST", path, options: options, body: body)
246
+ end
247
+
248
+ #
249
+ def list_public_repositories(options: nil)
250
+ path = "/api/v1/public/repositories"
251
+ request("GET", path, options: options)
252
+ end
253
+
254
+ #
255
+ def get_public_repo_tag(org_name, repo_name, tag_name, options: nil)
256
+ path = sprintf("/api/v1/public/repositories/%s/%s/tags/%s", org_name, repo_name, tag_name)
257
+ request("GET", path, options: options)
258
+ end
259
+
260
+ #
261
+ def get_vm_metadata(vm_id, options: nil)
262
+ path = sprintf("/api/v1/vm/%s/metadata", vm_id)
263
+ request("GET", path, options: options)
264
+ end
265
+
266
+ #
267
+ def list_env_vars(options: nil)
268
+ path = "/api/v1/env_vars"
269
+ request("GET", path, options: options)
270
+ end
271
+
272
+ #
273
+ def set_env_vars(body: nil, options: nil)
274
+ path = "/api/v1/env_vars"
275
+ request("PUT", path, options: options, body: body)
276
+ end
277
+
278
+ #
279
+ def delete_vm(vm_id, params: nil, options: nil)
280
+ path = sprintf("/api/v1/vm/%s", vm_id)
281
+ query = {}
282
+ if params
283
+ h = params.is_a?(Hash) ? params : params.to_h
284
+ h.each { |k, v| query[k.to_s] = v.to_s unless v.nil? }
285
+ end
286
+ request("DELETE", path, options: options, query: query)
287
+ end
288
+
289
+ #
290
+ def list_commits(options: nil)
291
+ path = "/api/v1/commits"
292
+ request("GET", path, options: options)
293
+ end
294
+
295
+ #
296
+ def get_tag(tag_name, options: nil)
297
+ path = sprintf("/api/v1/commit_tags/%s", tag_name)
298
+ request("GET", path, options: options)
299
+ end
300
+
301
+ #
302
+ def delete_tag(tag_name, options: nil)
303
+ path = sprintf("/api/v1/commit_tags/%s", tag_name)
304
+ request("DELETE", path, options: options)
305
+ end
306
+
307
+ #
308
+ def update_tag(tag_name, body: nil, options: nil)
309
+ path = sprintf("/api/v1/commit_tags/%s", tag_name)
310
+ request("PATCH", path, options: options, body: body)
311
+ end
312
+
313
+ #
314
+ def branch_by_vm(vm_id, body: nil, params: nil, options: nil)
315
+ path = sprintf("/api/v1/vm/branch/by_vm/%s", vm_id)
316
+ query = {}
317
+ if params
318
+ h = params.is_a?(Hash) ? params : params.to_h
319
+ h.each { |k, v| query[k.to_s] = v.to_s unless v.nil? }
320
+ end
321
+ request("POST", path, options: options, body: body, query: query)
322
+ end
323
+
324
+ #
325
+ def delete_env_var(key, options: nil)
326
+ path = sprintf("/api/v1/env_vars/%s", key)
327
+ request("DELETE", path, options: options)
328
+ end
329
+
330
+ #
331
+ def exec_vm_stream(vm_id, body: nil, options: nil)
332
+ path = sprintf("/api/v1/vm/%s/exec/stream", vm_id)
333
+ request("POST", path, options: options, body: body)
334
+ end
335
+
336
+ #
337
+ def commit_vm(vm_id, body: nil, params: nil, options: nil)
338
+ path = sprintf("/api/v1/vm/%s/commit", vm_id)
339
+ query = {}
340
+ if params
341
+ h = params.is_a?(Hash) ? params : params.to_h
342
+ h.each { |k, v| query[k.to_s] = v.to_s unless v.nil? }
343
+ end
344
+ request("POST", path, options: options, body: body, query: query)
345
+ end
346
+
347
+ #
348
+ def delete_commit(commit_id, options: nil)
349
+ path = sprintf("/api/v1/commits/%s", commit_id)
350
+ request("DELETE", path, options: options)
351
+ end
352
+
353
+ #
354
+ def update_commit(commit_id, body: nil, options: nil)
355
+ path = sprintf("/api/v1/commits/%s", commit_id)
356
+ request("PATCH", path, options: options, body: body)
357
+ end
358
+
359
+ #
360
+ def list_repo_tags(repo_name, options: nil)
361
+ path = sprintf("/api/v1/repositories/%s/tags", repo_name)
362
+ request("GET", path, options: options)
363
+ end
364
+
365
+ #
366
+ def create_repo_tag(repo_name, body: nil, options: nil)
367
+ path = sprintf("/api/v1/repositories/%s/tags", repo_name)
368
+ request("POST", path, options: options, body: body)
369
+ end
370
+
371
+ #
372
+ def ssh_key(vm_id, options: nil)
373
+ path = sprintf("/api/v1/vm/%s/ssh_key", vm_id)
374
+ request("GET", path, options: options)
375
+ end
376
+
377
+ #
378
+ def exec_vm(vm_id, body: nil, options: nil)
379
+ path = sprintf("/api/v1/vm/%s/exec", vm_id)
380
+ request("POST", path, options: options, body: body)
381
+ end
382
+
383
+ #
384
+ def set_repository_visibility(repo_name, body: nil, options: nil)
385
+ path = sprintf("/api/v1/repositories/%s/visibility", repo_name)
386
+ request("PATCH", path, options: options, body: body)
387
+ end
388
+
389
+ #
390
+ def list_vms(options: nil)
391
+ path = "/api/v1/vms"
392
+ request("GET", path, options: options)
393
+ end
394
+
395
+ #
396
+ def list_domains(params: nil, options: nil)
397
+ path = "/api/v1/domains"
398
+ query = {}
399
+ if params
400
+ h = params.is_a?(Hash) ? params : params.to_h
401
+ h.each { |k, v| query[k.to_s] = v.to_s unless v.nil? }
402
+ end
403
+ request("GET", path, options: options, query: query)
404
+ end
405
+
406
+ #
407
+ def create_domain(body: nil, options: nil)
408
+ path = "/api/v1/domains"
409
+ request("POST", path, options: options, body: body)
410
+ end
411
+
412
+ #
413
+ def vm_status(vm_id, options: nil)
414
+ path = sprintf("/api/v1/vm/%s/status", vm_id)
415
+ request("GET", path, options: options)
416
+ end
417
+
418
+ #
419
+ def get_public_repository(org_name, repo_name, options: nil)
420
+ path = sprintf("/api/v1/public/repositories/%s/%s", org_name, repo_name)
421
+ request("GET", path, options: options)
422
+ end
423
+
424
+ #
425
+ def branch_by_commit(commit_id, body: nil, params: nil, options: nil)
426
+ path = sprintf("/api/v1/vm/branch/by_commit/%s", commit_id)
427
+ query = {}
428
+ if params
429
+ h = params.is_a?(Hash) ? params : params.to_h
430
+ h.each { |k, v| query[k.to_s] = v.to_s unless v.nil? }
431
+ end
432
+ request("POST", path, options: options, body: body, query: query)
433
+ end
434
+
435
+
436
+ private
437
+
438
+ def request(method, path, options: nil, body: nil, query: nil)
439
+ uri = build_uri(path, query)
440
+ @logger.debug("request: #{method} #{uri}")
441
+
442
+ last_exc = nil
443
+ retry_after_used = false
444
+
445
+ (@max_retries + 1).times do |attempt|
446
+ if attempt > 0 && !retry_after_used
447
+ delay = retry_delay(attempt - 1)
448
+ @logger.info("retry attempt #{attempt}/#{@max_retries} for #{method} #{path}")
449
+ sleep(delay)
450
+ end
451
+ retry_after_used = false
452
+
453
+ begin
454
+ response = execute_request(method, uri, body: body, options: options)
455
+ status = response.code.to_i
456
+ @logger.debug("response: #{status} for #{method} #{path}")
457
+
458
+ if retryable_status?(status) && attempt < @max_retries
459
+ ra_delay = parse_retry_after(response["Retry-After"])
460
+ if ra_delay
461
+ retry_after_used = true
462
+ @logger.info("retry attempt #{attempt + 1}/#{@max_retries} for #{method} #{path} (retry-after: #{ra_delay}s)")
463
+ sleep(ra_delay)
464
+ end
465
+ last_exc = StandardError.new("HTTP #{status}")
466
+ next
467
+ end
468
+
469
+ check_response(response)
470
+ return response
471
+ rescue APIError
472
+ raise
473
+ rescue Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ETIMEDOUT,
474
+ Net::OpenTimeout, Net::ReadTimeout, SocketError => e
475
+ @logger.error("connection error: #{method} #{path} - #{e.message}")
476
+ raise APIConnectionError.new(message: e.message, cause: e) if attempt == @max_retries
477
+
478
+ last_exc = e
479
+ end
480
+ end
481
+
482
+ raise last_exc if last_exc
483
+ end
484
+
485
+ def build_uri(path, query)
486
+ uri = URI.join(@base_url, path)
487
+ if query && !query.empty?
488
+ uri.query = URI.encode_www_form(query)
489
+ end
490
+ uri
491
+ end
492
+
493
+ def execute_request(method, uri, body: nil, options: nil)
494
+ http = Net::HTTP.new(uri.host, uri.port)
495
+ http.use_ssl = (uri.scheme == "https")
496
+ effective_timeout = (options&.timeout || @timeout)
497
+ http.open_timeout = effective_timeout
498
+ http.read_timeout = effective_timeout
499
+
500
+ req = build_http_request(method, uri, body)
501
+
502
+ # Merge default headers
503
+ @default_headers.each { |k, v| req[k] = v }
504
+
505
+ # Idempotency key for mutating methods
506
+ if %w[POST PUT PATCH DELETE].include?(method)
507
+ req["X-Idempotency-Key"] = SecureRandom.uuid
508
+ end
509
+
510
+ # Per-request header overrides
511
+ if options&.headers
512
+ options.headers.each do |k, v|
513
+ if v.nil?
514
+ req.delete(k)
515
+ else
516
+ req[k] = v
517
+ end
518
+ end
519
+ end
520
+
521
+ http.request(req)
522
+ end
523
+
524
+ def build_http_request(method, uri, body)
525
+ request_path = uri.request_uri
526
+ case method
527
+ when "GET"
528
+ Net::HTTP::Get.new(request_path)
529
+ when "POST"
530
+ req = Net::HTTP::Post.new(request_path)
531
+ req.body = body.to_json if body
532
+ req
533
+ when "PUT"
534
+ req = Net::HTTP::Put.new(request_path)
535
+ req.body = body.to_json if body
536
+ req
537
+ when "PATCH"
538
+ req = Net::HTTP::Patch.new(request_path)
539
+ req.body = body.to_json if body
540
+ req
541
+ when "DELETE"
542
+ Net::HTTP::Delete.new(request_path)
543
+ else
544
+ Net::HTTP::Get.new(request_path)
545
+ end
546
+ end
547
+
548
+ def check_response(response)
549
+ status = response.code.to_i
550
+ return if status >= 200 && status < 300
551
+
552
+ body = nil
553
+ message = nil
554
+ begin
555
+ body = JSON.parse(response.body)
556
+ rescue StandardError
557
+ message = response.body
558
+ end
559
+
560
+ headers = {}
561
+ response.each_header { |k, v| headers[k] = v }
562
+
563
+ raise APIError.generate(status: status, body: body, message: message, headers: headers)
564
+ end
565
+
566
+ def retryable_status?(status)
567
+ status >= 500 || RETRYABLE_STATUS_CODES.include?(status)
568
+ end
569
+
570
+ def retry_delay(attempt)
571
+ base = 0.5 * (2**attempt)
572
+ jitter = rand * base * 0.25
573
+ base + jitter
574
+ end
575
+
576
+ def parse_retry_after(header_value)
577
+ return nil if header_value.nil?
578
+
579
+ # Try numeric
580
+ delay = Float(header_value)
581
+ return [[delay, 0.0].max, 60.0].min
582
+ rescue ArgumentError, TypeError
583
+ # Try HTTP date
584
+ begin
585
+ retry_time = Time.httpdate(header_value)
586
+ delay = retry_time - Time.now
587
+ [[delay, 0.0].max, 60.0].min
588
+ rescue ArgumentError
589
+ nil
590
+ end
591
+ end
592
+ end
593
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Generated by Sterling SDK Generator
4
+ # Orchestrator Control Plane API v0.1.0
5
+
6
+ module VersSdk
7
+ # Base error class for the VersSdk SDK.
8
+ class VersSDKError < StandardError; end
9
+
10
+ # Error returned when the API responds with a non-success status code.
11
+ class APIError < VersSDKError
12
+ attr_reader :status, :headers, :body
13
+
14
+ def initialize(status: nil, body: nil, message: nil, headers: nil)
15
+ @status = status
16
+ @headers = headers
17
+ @body = body
18
+ super(make_message(status, body, message))
19
+ end
20
+
21
+ def self.generate(status:, body: nil, message: nil, headers: nil)
22
+ return APIConnectionError.new(message: message) if status.nil? || headers.nil?
23
+
24
+ error_map = {
25
+ 400 => BadRequestError,
26
+ 401 => AuthenticationError,
27
+ 403 => PermissionDeniedError,
28
+ 404 => NotFoundError,
29
+ 409 => ConflictError,
30
+ 422 => UnprocessableEntityError,
31
+ 429 => RateLimitError
32
+ }
33
+
34
+ error_cls = error_map[status]
35
+ if error_cls
36
+ return error_cls.new(status: status, body: body, message: message, headers: headers)
37
+ end
38
+ if status >= 500
39
+ return InternalServerError.new(status: status, body: body, message: message, headers: headers)
40
+ end
41
+
42
+ new(status: status, body: body, message: message, headers: headers)
43
+ end
44
+
45
+ private
46
+
47
+ def make_message(status, body, message)
48
+ msg = nil
49
+ if body.is_a?(Hash) && body.key?("message")
50
+ msg = body["message"].to_s
51
+ elsif !body.nil?
52
+ msg = body.to_s
53
+ elsif !message.nil?
54
+ msg = message
55
+ end
56
+
57
+ return "#{status} #{msg}" if status && msg
58
+ return "#{status} status code (no body)" if status
59
+ return msg if msg
60
+
61
+ "(no status code or body)"
62
+ end
63
+ end
64
+
65
+ # Error thrown when a connection to the API cannot be established.
66
+ class APIConnectionError < APIError
67
+ def initialize(message: nil, cause: nil)
68
+ @cause = cause
69
+ super(status: nil, body: nil, message: message || "Connection error.")
70
+ end
71
+ end
72
+
73
+ # Error thrown when a request times out.
74
+ class APIConnectionTimeoutError < APIConnectionError
75
+ def initialize(message: nil)
76
+ super(message: message || "Request timed out.")
77
+ end
78
+ end
79
+
80
+ # 400 Bad Request
81
+ class BadRequestError < APIError; end
82
+
83
+ # 401 Unauthorized
84
+ class AuthenticationError < APIError; end
85
+
86
+ # 403 Forbidden
87
+ class PermissionDeniedError < APIError; end
88
+
89
+ # 404 Not Found
90
+ class NotFoundError < APIError; end
91
+
92
+ # 409 Conflict
93
+ class ConflictError < APIError; end
94
+
95
+ # 422 Unprocessable Entity
96
+ class UnprocessableEntityError < APIError; end
97
+
98
+ # 429 Too Many Requests
99
+ class RateLimitError < APIError; end
100
+
101
+ # 5xx Internal Server Error
102
+ class InternalServerError < APIError; end
103
+ end