basecamp-sdk 0.2.1 → 0.2.3

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: 5251d36b00bfd269e7fce1bc324c2c9b491b9d0f0d30296807c0b1b724272adc
4
- data.tar.gz: c590cec999d1b5c687456d8cdf1259712df2fcb26260048410ac5edd41dd772b
3
+ metadata.gz: 2d9ac0d518ad24aed0762084bbe6d5e086652d2b10cd6d7f28ba0afa839fa2cd
4
+ data.tar.gz: 7dbc3a032f0e0fbe6f2993db0c588d44b4a2aaebde9764004820845eac3bacc5
5
5
  SHA512:
6
- metadata.gz: 874a76ab2b404c5ad8bada234f9002ba4e50d3ad5e574352d0f6cc53991efa656f0adb0ddd1701df52e66966ed201740b1156c9aaa7a3193422c9ab82fa877ae
7
- data.tar.gz: 05570761aa1e7a59823ed614505391415f883b41fcf7ba4b4197f51ad2ad07cf7ce9d7ecd7722ecfe7e60d759fb963638f7bfe621b20406233dcf1b0a1909400
6
+ metadata.gz: 2914132d8a9a6a9dba9b14bd10e36fbab735ec9a564342cb9f44aed6c4c766687f4462e9264284bba46a31d3ab614ddf9fdc9350c7726e17517ba358769b6a05
7
+ data.tar.gz: be5c86123641a00cb716b2c1d87daaff532fe5da7c6aa30665401a9d0eefdad300895f1ce2cce937c984f1b59e046916b3b9197baf55da2c4f85ec90d1dd47c0
@@ -56,6 +56,9 @@ module Basecamp
56
56
  # @return [Integer, nil] seconds to wait before retrying (for rate limits)
57
57
  attr_reader :retry_after
58
58
 
59
+ # @return [String, nil] X-Request-Id from the response
60
+ attr_reader :request_id
61
+
59
62
  # @return [Exception, nil] original error that caused this error
60
63
  attr_reader :cause
61
64
 
@@ -65,14 +68,16 @@ module Basecamp
65
68
  # @param http_status [Integer, nil] HTTP status code
66
69
  # @param retryable [Boolean] whether operation can be retried
67
70
  # @param retry_after [Integer, nil] seconds to wait before retry
71
+ # @param request_id [String, nil] X-Request-Id from response
68
72
  # @param cause [Exception, nil] underlying cause
69
- def initialize(code:, message:, hint: nil, http_status: nil, retryable: false, retry_after: nil, cause: nil)
73
+ def initialize(code:, message:, hint: nil, http_status: nil, retryable: false, retry_after: nil, request_id: nil, cause: nil)
70
74
  super(message)
71
75
  @code = code
72
76
  @hint = hint
73
77
  @http_status = http_status
74
78
  @retryable = retryable
75
79
  @retry_after = retry_after
80
+ @request_id = request_id
76
81
  @cause = cause
77
82
  end
78
83
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://basecamp.com/schemas/sdk-metadata.json",
3
3
  "version": "1.0.0",
4
- "generated": "2026-02-27T01:07:43Z",
4
+ "generated": "2026-03-09T19:39:33Z",
5
5
  "operations": {
6
6
  "CreateAttachment": {
7
7
  "retry": {
@@ -515,6 +515,33 @@
515
515
  "natural": true
516
516
  }
517
517
  },
518
+ "ListCampfireUploads": {
519
+ "retry": {
520
+ "maxAttempts": 3,
521
+ "baseDelayMs": 1000,
522
+ "backoff": "exponential",
523
+ "retryOn": [
524
+ 429,
525
+ 503
526
+ ]
527
+ },
528
+ "pagination": {
529
+ "style": "link",
530
+ "totalCountHeader": "X-Total-Count",
531
+ "maxPageSize": 50
532
+ }
533
+ },
534
+ "CreateCampfireUpload": {
535
+ "retry": {
536
+ "maxAttempts": 3,
537
+ "baseDelayMs": 2000,
538
+ "backoff": "exponential",
539
+ "retryOn": [
540
+ 429,
541
+ 503
542
+ ]
543
+ }
544
+ },
518
545
  "ListPingablePeople": {
519
546
  "retry": {
520
547
  "maxAttempts": 3,
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "uri"
4
+
3
5
  module Basecamp
4
6
  module Services
5
7
  # Service for Campfires operations
@@ -117,6 +119,27 @@ module Basecamp
117
119
  nil
118
120
  end
119
121
  end
122
+
123
+ # List uploaded files in a campfire
124
+ # @param campfire_id [Integer] campfire id ID
125
+ # @return [Enumerator<Hash>] paginated results
126
+ def list_uploads(campfire_id:)
127
+ wrap_paginated(service: "campfires", operation: "list_uploads", is_mutation: false, resource_id: campfire_id) do
128
+ paginate("/chats/#{campfire_id}/uploads.json")
129
+ end
130
+ end
131
+
132
+ # Upload a file to a campfire
133
+ # @param campfire_id [Integer] campfire id ID
134
+ # @param data [String] Binary file data to upload
135
+ # @param content_type [String] MIME type of the file (e.g., "application/pdf", "image/png")
136
+ # @param name [String] Filename for the uploaded file (e.g. "report.pdf").
137
+ # @return [Hash] response data
138
+ def create_upload(campfire_id:, data:, content_type:, name:)
139
+ with_operation(service: "campfires", operation: "create_upload", is_mutation: true, resource_id: campfire_id) do
140
+ http_post_raw("/chats/#{campfire_id}/uploads.json?name=#{URI.encode_www_form_component(name.to_s)}", body: data, content_type: content_type).json
141
+ end
142
+ end
120
143
  end
121
144
  end
122
145
  end
@@ -41,10 +41,11 @@ module Basecamp
41
41
  # @param title [String] title
42
42
  # @param content [String, nil] content
43
43
  # @param status [String, nil] active|drafted
44
+ # @param subscriptions [Array, nil] subscriptions
44
45
  # @return [Hash] response data
45
- def create(vault_id:, title:, content: nil, status: nil)
46
+ def create(vault_id:, title:, content: nil, status: nil, subscriptions: nil)
46
47
  with_operation(service: "documents", operation: "create", is_mutation: true, resource_id: vault_id) do
47
- http_post("/vaults/#{vault_id}/documents.json", body: compact_params(title: title, content: content, status: status)).json
48
+ http_post("/vaults/#{vault_id}/documents.json", body: compact_params(title: title, content: content, status: status, subscriptions: subscriptions)).json
48
49
  end
49
50
  end
50
51
  end
@@ -22,10 +22,11 @@ module Basecamp
22
22
  # @param content [String, nil] content
23
23
  # @param status [String, nil] active|drafted
24
24
  # @param category_id [Integer, nil] category id
25
+ # @param subscriptions [Array, nil] subscriptions
25
26
  # @return [Hash] response data
26
- def create(board_id:, subject:, content: nil, status: nil, category_id: nil)
27
+ def create(board_id:, subject:, content: nil, status: nil, category_id: nil, subscriptions: nil)
27
28
  with_operation(service: "messages", operation: "create", is_mutation: true, resource_id: board_id) do
28
- http_post("/message_boards/#{board_id}/messages.json", body: compact_params(subject: subject, content: content, status: status, category_id: category_id)).json
29
+ http_post("/message_boards/#{board_id}/messages.json", body: compact_params(subject: subject, content: content, status: status, category_id: category_id, subscriptions: subscriptions)).json
29
30
  end
30
31
  end
31
32
 
@@ -81,10 +81,11 @@ module Basecamp
81
81
  # @param participant_ids [Array, nil] participant ids
82
82
  # @param all_day [Boolean, nil] all day
83
83
  # @param notify [Boolean, nil] notify
84
+ # @param subscriptions [Array, nil] subscriptions
84
85
  # @return [Hash] response data
85
- def create_entry(schedule_id:, summary:, starts_at:, ends_at:, description: nil, participant_ids: nil, all_day: nil, notify: nil)
86
+ def create_entry(schedule_id:, summary:, starts_at:, ends_at:, description: nil, participant_ids: nil, all_day: nil, notify: nil, subscriptions: nil)
86
87
  with_operation(service: "schedules", operation: "create_entry", is_mutation: true, resource_id: schedule_id) do
87
- http_post("/schedules/#{schedule_id}/entries.json", body: compact_params(summary: summary, starts_at: starts_at, ends_at: ends_at, description: description, participant_ids: participant_ids, all_day: all_day, notify: notify)).json
88
+ http_post("/schedules/#{schedule_id}/entries.json", body: compact_params(summary: summary, starts_at: starts_at, ends_at: ends_at, description: description, participant_ids: participant_ids, all_day: all_day, notify: notify, subscriptions: subscriptions)).json
88
89
  end
89
90
  end
90
91
  end
@@ -50,10 +50,11 @@ module Basecamp
50
50
  # @param attachable_sgid [String] attachable sgid
51
51
  # @param description [String, nil] description
52
52
  # @param base_name [String, nil] base name
53
+ # @param subscriptions [Array, nil] subscriptions
53
54
  # @return [Hash] response data
54
- def create(vault_id:, attachable_sgid:, description: nil, base_name: nil)
55
+ def create(vault_id:, attachable_sgid:, description: nil, base_name: nil, subscriptions: nil)
55
56
  with_operation(service: "uploads", operation: "create", is_mutation: true, resource_id: vault_id) do
56
- http_post("/vaults/#{vault_id}/uploads.json", body: compact_params(attachable_sgid: attachable_sgid, description: description, base_name: base_name)).json
57
+ http_post("/vaults/#{vault_id}/uploads.json", body: compact_params(attachable_sgid: attachable_sgid, description: description, base_name: base_name, subscriptions: subscriptions)).json
57
58
  end
58
59
  end
59
60
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Auto-generated from OpenAPI spec. Do not edit manually.
4
- # Generated: 2026-02-27T01:07:43Z
4
+ # Generated: 2026-03-09T19:39:33Z
5
5
 
6
6
  require "json"
7
7
  require "time"
@@ -136,7 +136,7 @@ module Basecamp
136
136
  # Campfire
137
137
  class Campfire
138
138
  include TypeHelpers
139
- attr_accessor :app_url, :bucket, :created_at, :creator, :id, :inherits_status, :status, :title, :type, :updated_at, :url, :visible_to_clients, :bookmark_url, :lines_url, :position, :subscription_url, :topic
139
+ attr_accessor :app_url, :bucket, :created_at, :creator, :id, :inherits_status, :status, :title, :type, :updated_at, :url, :visible_to_clients, :bookmark_url, :files_url, :lines_url, :position, :subscription_url, :topic
140
140
 
141
141
  # @return [Array<Symbol>]
142
142
  def self.required_fields
@@ -157,6 +157,7 @@ module Basecamp
157
157
  @url = data["url"]
158
158
  @visible_to_clients = parse_boolean(data["visible_to_clients"])
159
159
  @bookmark_url = data["bookmark_url"]
160
+ @files_url = data["files_url"]
160
161
  @lines_url = data["lines_url"]
161
162
  @position = parse_integer(data["position"])
162
163
  @subscription_url = data["subscription_url"]
@@ -178,6 +179,7 @@ module Basecamp
178
179
  "url" => @url,
179
180
  "visible_to_clients" => @visible_to_clients,
180
181
  "bookmark_url" => @bookmark_url,
182
+ "files_url" => @files_url,
181
183
  "lines_url" => @lines_url,
182
184
  "position" => @position,
183
185
  "subscription_url" => @subscription_url,
@@ -193,17 +195,16 @@ module Basecamp
193
195
  # CampfireLine
194
196
  class CampfireLine
195
197
  include TypeHelpers
196
- attr_accessor :app_url, :bucket, :content, :created_at, :creator, :id, :inherits_status, :parent, :status, :title, :type, :updated_at, :url, :visible_to_clients, :bookmark_url, :boosts_count, :boosts_url
198
+ attr_accessor :app_url, :bucket, :created_at, :creator, :id, :inherits_status, :parent, :status, :title, :type, :updated_at, :url, :visible_to_clients, :attachments, :bookmark_url, :boosts_count, :boosts_url, :content
197
199
 
198
200
  # @return [Array<Symbol>]
199
201
  def self.required_fields
200
- %i[app_url bucket content created_at creator id inherits_status parent status title type updated_at url visible_to_clients].freeze
202
+ %i[app_url bucket created_at creator id inherits_status parent status title type updated_at url visible_to_clients].freeze
201
203
  end
202
204
 
203
205
  def initialize(data = {})
204
206
  @app_url = data["app_url"]
205
207
  @bucket = parse_type(data["bucket"], "TodoBucket")
206
- @content = data["content"]
207
208
  @created_at = parse_datetime(data["created_at"])
208
209
  @creator = parse_type(data["creator"], "Person")
209
210
  @id = parse_integer(data["id"])
@@ -215,16 +216,17 @@ module Basecamp
215
216
  @updated_at = parse_datetime(data["updated_at"])
216
217
  @url = data["url"]
217
218
  @visible_to_clients = parse_boolean(data["visible_to_clients"])
219
+ @attachments = parse_array(data["attachments"], "CampfireLineAttachment")
218
220
  @bookmark_url = data["bookmark_url"]
219
221
  @boosts_count = parse_integer(data["boosts_count"])
220
222
  @boosts_url = data["boosts_url"]
223
+ @content = data["content"]
221
224
  end
222
225
 
223
226
  def to_h
224
227
  {
225
228
  "app_url" => @app_url,
226
229
  "bucket" => @bucket,
227
- "content" => @content,
228
230
  "created_at" => @created_at,
229
231
  "creator" => @creator,
230
232
  "id" => @id,
@@ -236,9 +238,41 @@ module Basecamp
236
238
  "updated_at" => @updated_at,
237
239
  "url" => @url,
238
240
  "visible_to_clients" => @visible_to_clients,
241
+ "attachments" => @attachments,
239
242
  "bookmark_url" => @bookmark_url,
240
243
  "boosts_count" => @boosts_count,
241
244
  "boosts_url" => @boosts_url,
245
+ "content" => @content,
246
+ }.compact
247
+ end
248
+
249
+ def to_json(*args)
250
+ to_h.to_json(*args)
251
+ end
252
+ end
253
+
254
+ # CampfireLineAttachment
255
+ class CampfireLineAttachment
256
+ include TypeHelpers
257
+ attr_accessor :byte_size, :content_type, :download_url, :filename, :title, :url
258
+
259
+ def initialize(data = {})
260
+ @byte_size = parse_integer(data["byte_size"])
261
+ @content_type = data["content_type"]
262
+ @download_url = data["download_url"]
263
+ @filename = data["filename"]
264
+ @title = data["title"]
265
+ @url = data["url"]
266
+ end
267
+
268
+ def to_h
269
+ {
270
+ "byte_size" => @byte_size,
271
+ "content_type" => @content_type,
272
+ "download_url" => @download_url,
273
+ "filename" => @filename,
274
+ "title" => @title,
275
+ "url" => @url,
242
276
  }.compact
243
277
  end
244
278
 
data/lib/basecamp/http.rb CHANGED
@@ -244,7 +244,7 @@ module Basecamp
244
244
  status: response.status,
245
245
  headers: response.headers
246
246
  )
247
- rescue Faraday::ClientError => e
247
+ rescue Faraday::ServerError, Faraday::ClientError => e
248
248
  duration = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time
249
249
  error = handle_error(e)
250
250
  result = RequestResult.new(
@@ -299,7 +299,7 @@ module Basecamp
299
299
  status: response.status,
300
300
  headers: response.headers
301
301
  )
302
- rescue Faraday::ClientError => e
302
+ rescue Faraday::ServerError, Faraday::ClientError => e
303
303
  duration = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time
304
304
  error = handle_error(e)
305
305
  result = RequestResult.new(
@@ -325,12 +325,12 @@ module Basecamp
325
325
  headers = error.response&.dig(:headers) || {}
326
326
 
327
327
  retry_after = parse_retry_after(headers["Retry-After"] || headers["retry-after"])
328
+ request_id = headers["X-Request-Id"] || headers["x-request-id"]
328
329
 
329
- case status
330
+ err = case status
330
331
  when 401
331
332
  # Try token refresh; flag for caller to retry
332
333
  @token_refreshed = @token_provider&.refreshable? && @token_provider.refresh
333
-
334
334
  Basecamp::AuthError.new("Authentication failed")
335
335
  when 403
336
336
  Basecamp::ForbiddenError.new("Access denied")
@@ -342,13 +342,16 @@ module Basecamp
342
342
  message = Security.truncate(Basecamp.parse_error_message(body) || "Validation failed")
343
343
  Basecamp::ValidationError.new(message, http_status: status)
344
344
  when 500
345
- Basecamp::APIError.new("Server error (500)", http_status: 500)
345
+ Basecamp::APIError.new("Server error (500)", http_status: 500, retryable: true)
346
346
  when 502, 503, 504
347
347
  Basecamp::APIError.new("Gateway error (#{status})", http_status: status, retryable: true)
348
348
  else
349
349
  message = Security.truncate(Basecamp.parse_error_message(body) || "Request failed (HTTP #{status})")
350
350
  Basecamp::APIError.from_status(status || 0, message)
351
351
  end
352
+
353
+ err.instance_variable_set(:@request_id, request_id) if request_id
354
+ err
352
355
  end
353
356
 
354
357
  def build_url(path)
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Basecamp
4
- VERSION = "0.2.1"
4
+ VERSION = "0.2.3"
5
5
  API_VERSION = "2026-01-26"
6
6
  end
@@ -45,6 +45,7 @@ class ServiceGenerator
45
45
  GetCampfire ListCampfires
46
46
  ListChatbots CreateChatbot GetChatbot UpdateChatbot DeleteChatbot
47
47
  ListCampfireLines CreateCampfireLine GetCampfireLine DeleteCampfireLine
48
+ ListCampfireUploads CreateCampfireUpload
48
49
  ]
49
50
  },
50
51
  'Card Tables' => {
@@ -188,6 +189,8 @@ class ServiceGenerator
188
189
  'CreateCampfireLine' => 'create_line',
189
190
  'GetCampfireLine' => 'get_line',
190
191
  'DeleteCampfireLine' => 'delete_line',
192
+ 'ListCampfireUploads' => 'list_uploads',
193
+ 'CreateCampfireUpload' => 'create_upload',
191
194
  # Forwards - use specific names to avoid conflicts between forwards, replies, and inbox
192
195
  'GetForward' => 'get',
193
196
  'ListForwards' => 'list',
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: basecamp-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Basecamp
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-02-28 00:00:00.000000000 Z
11
+ date: 2026-03-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday