basecamp-sdk 0.9.0 → 0.11.0

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.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +106 -10
  3. data/lib/basecamp/client.rb +13 -8
  4. data/lib/basecamp/config.rb +4 -3
  5. data/lib/basecamp/generated/metadata.json +280 -12
  6. data/lib/basecamp/generated/services/account_service.rb +1 -1
  7. data/lib/basecamp/generated/services/automation_service.rb +2 -2
  8. data/lib/basecamp/generated/services/boosts_service.rb +3 -3
  9. data/lib/basecamp/generated/services/campfires_service.rb +7 -7
  10. data/lib/basecamp/generated/services/card_columns_service.rb +1 -1
  11. data/lib/basecamp/generated/services/card_steps_service.rb +1 -1
  12. data/lib/basecamp/generated/services/card_tables_service.rb +1 -1
  13. data/lib/basecamp/generated/services/cards_service.rb +4 -4
  14. data/lib/basecamp/generated/services/checkins_service.rb +8 -8
  15. data/lib/basecamp/generated/services/client_approvals_service.rb +2 -2
  16. data/lib/basecamp/generated/services/client_correspondences_service.rb +2 -2
  17. data/lib/basecamp/generated/services/client_replies_service.rb +2 -2
  18. data/lib/basecamp/generated/services/comments_service.rb +2 -2
  19. data/lib/basecamp/generated/services/documents_service.rb +2 -2
  20. data/lib/basecamp/generated/services/events_service.rb +1 -1
  21. data/lib/basecamp/generated/services/everything_service.rb +169 -0
  22. data/lib/basecamp/generated/services/forwards_service.rb +5 -5
  23. data/lib/basecamp/generated/services/gauges_service.rb +3 -3
  24. data/lib/basecamp/generated/services/hill_charts_service.rb +1 -1
  25. data/lib/basecamp/generated/services/message_boards_service.rb +1 -1
  26. data/lib/basecamp/generated/services/message_types_service.rb +20 -15
  27. data/lib/basecamp/generated/services/messages_service.rb +2 -2
  28. data/lib/basecamp/generated/services/my_assignments_service.rb +5 -5
  29. data/lib/basecamp/generated/services/my_notifications_service.rb +16 -2
  30. data/lib/basecamp/generated/services/people_service.rb +9 -9
  31. data/lib/basecamp/generated/services/projects_service.rb +2 -2
  32. data/lib/basecamp/generated/services/recordings_service.rb +3 -3
  33. data/lib/basecamp/generated/services/reports_service.rb +5 -5
  34. data/lib/basecamp/generated/services/schedules_service.rb +13 -5
  35. data/lib/basecamp/generated/services/search_service.rb +2 -2
  36. data/lib/basecamp/generated/services/subscriptions_service.rb +1 -1
  37. data/lib/basecamp/generated/services/templates_service.rb +3 -3
  38. data/lib/basecamp/generated/services/timeline_service.rb +1 -1
  39. data/lib/basecamp/generated/services/timesheets_service.rb +5 -5
  40. data/lib/basecamp/generated/services/todolist_groups_service.rb +1 -1
  41. data/lib/basecamp/generated/services/todolists_service.rb +2 -2
  42. data/lib/basecamp/generated/services/todos_service.rb +2 -2
  43. data/lib/basecamp/generated/services/todosets_service.rb +1 -1
  44. data/lib/basecamp/generated/services/tools_service.rb +4 -3
  45. data/lib/basecamp/generated/services/uploads_service.rb +3 -3
  46. data/lib/basecamp/generated/services/vaults_service.rb +2 -2
  47. data/lib/basecamp/generated/services/webhooks_service.rb +2 -2
  48. data/lib/basecamp/generated/types.rb +374 -18
  49. data/lib/basecamp/http.rb +47 -18
  50. data/lib/basecamp/oauth/device_authorization.rb +37 -0
  51. data/lib/basecamp/oauth/device_flow.rb +873 -0
  52. data/lib/basecamp/oauth/device_flow_error.rb +46 -0
  53. data/lib/basecamp/oauth/discovery.rb +13 -11
  54. data/lib/basecamp/oauth/exchange.rb +44 -5
  55. data/lib/basecamp/oauth/fetcher.rb +626 -56
  56. data/lib/basecamp/oauth/oauth_error.rb +3 -1
  57. data/lib/basecamp/oauth/refresh_request.rb +8 -2
  58. data/lib/basecamp/oauth/resource.rb +13 -10
  59. data/lib/basecamp/oauth/token.rb +10 -3
  60. data/lib/basecamp/oauth.rb +47 -2
  61. data/lib/basecamp/services/cards_extensions.rb +67 -0
  62. data/lib/basecamp/version.rb +2 -2
  63. data/lib/basecamp.rb +5 -0
  64. data/scripts/generate-services.rb +24 -6
  65. data/scripts/generate-types.rb +29 -2
  66. metadata +7 -2
@@ -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-07-25T04:02:54Z
4
+ # Generated: 2026-07-30T23:14:00Z
5
5
 
6
6
  require "json"
7
7
  require "time"
@@ -288,6 +288,60 @@ module Basecamp
288
288
  end
289
289
  end
290
290
 
291
+ # BucketCardsGroup
292
+ class BucketCardsGroup
293
+ include TypeHelpers
294
+ attr_accessor :bucket, :cards
295
+
296
+ # @return [Array<Symbol>]
297
+ def self.required_fields
298
+ %i[bucket cards].freeze
299
+ end
300
+
301
+ def initialize(data = {})
302
+ @bucket = parse_type(data["bucket"], "RecordingBucket")
303
+ @cards = parse_array(data["cards"], "Card")
304
+ end
305
+
306
+ def to_h
307
+ {
308
+ "bucket" => @bucket,
309
+ "cards" => @cards,
310
+ }.compact
311
+ end
312
+
313
+ def to_json(*args)
314
+ to_h.to_json(*args)
315
+ end
316
+ end
317
+
318
+ # BucketTodosGroup
319
+ class BucketTodosGroup
320
+ include TypeHelpers
321
+ attr_accessor :bucket, :todos
322
+
323
+ # @return [Array<Symbol>]
324
+ def self.required_fields
325
+ %i[bucket todos].freeze
326
+ end
327
+
328
+ def initialize(data = {})
329
+ @bucket = parse_type(data["bucket"], "RecordingBucket")
330
+ @todos = parse_array(data["todos"], "Todo")
331
+ end
332
+
333
+ def to_h
334
+ {
335
+ "bucket" => @bucket,
336
+ "todos" => @todos,
337
+ }.compact
338
+ end
339
+
340
+ def to_json(*args)
341
+ to_h.to_json(*args)
342
+ end
343
+ end
344
+
291
345
  # Campfire
292
346
  class Campfire
293
347
  include TypeHelpers
@@ -1269,6 +1323,34 @@ module Basecamp
1269
1323
  end
1270
1324
  end
1271
1325
 
1326
+ # DoorService
1327
+ class DoorService
1328
+ include TypeHelpers
1329
+ attr_accessor :code, :example_url, :name, :supporting_text, :valid_patterns
1330
+
1331
+ def initialize(data = {})
1332
+ @code = data["code"]
1333
+ @example_url = data["example_url"]
1334
+ @name = data["name"]
1335
+ @supporting_text = data["supporting_text"]
1336
+ @valid_patterns = data["valid_patterns"]
1337
+ end
1338
+
1339
+ def to_h
1340
+ {
1341
+ "code" => @code,
1342
+ "example_url" => @example_url,
1343
+ "name" => @name,
1344
+ "supporting_text" => @supporting_text,
1345
+ "valid_patterns" => @valid_patterns,
1346
+ }.compact
1347
+ end
1348
+
1349
+ def to_json(*args)
1350
+ to_h.to_json(*args)
1351
+ end
1352
+ end
1353
+
1272
1354
  # Event
1273
1355
  class Event
1274
1356
  include TypeHelpers
@@ -1332,6 +1414,88 @@ module Basecamp
1332
1414
  end
1333
1415
  end
1334
1416
 
1417
+ # EverythingFile
1418
+ class EverythingFile
1419
+ include TypeHelpers
1420
+ attr_accessor :app_download_url, :app_url, :attachable_sgid, :bookmark_url, :boosts_count, :boosts_url, :bucket, :byte_size, :comments_count, :comments_url, :content, :content_attachments, :content_type, :created_at, :creator, :description, :description_attachments, :download_url, :filename, :height, :id, :inherits_status, :parent, :position, :status, :subscription_url, :title, :type, :updated_at, :url, :visible_to_clients, :width
1421
+
1422
+ def initialize(data = {})
1423
+ @app_download_url = data["app_download_url"]
1424
+ @app_url = data["app_url"]
1425
+ @attachable_sgid = data["attachable_sgid"]
1426
+ @bookmark_url = data["bookmark_url"]
1427
+ @boosts_count = parse_integer(data["boosts_count"])
1428
+ @boosts_url = data["boosts_url"]
1429
+ @bucket = parse_type(data["bucket"], "RecordingBucket")
1430
+ @byte_size = parse_integer(data["byte_size"])
1431
+ @comments_count = parse_integer(data["comments_count"])
1432
+ @comments_url = data["comments_url"]
1433
+ @content = data["content"]
1434
+ @content_attachments = parse_array(data["content_attachments"], "RichTextAttachment")
1435
+ @content_type = data["content_type"]
1436
+ @created_at = parse_datetime(data["created_at"])
1437
+ @creator = parse_type(data["creator"], "Person")
1438
+ @description = data["description"]
1439
+ @description_attachments = parse_array(data["description_attachments"], "RichTextAttachment")
1440
+ @download_url = data["download_url"]
1441
+ @filename = data["filename"]
1442
+ @height = parse_integer(data["height"])
1443
+ @id = parse_integer(data["id"])
1444
+ @inherits_status = parse_boolean(data["inherits_status"])
1445
+ @parent = parse_type(data["parent"], "RecordingParent")
1446
+ @position = parse_integer(data["position"])
1447
+ @status = data["status"]
1448
+ @subscription_url = data["subscription_url"]
1449
+ @title = data["title"]
1450
+ @type = data["type"]
1451
+ @updated_at = parse_datetime(data["updated_at"])
1452
+ @url = data["url"]
1453
+ @visible_to_clients = parse_boolean(data["visible_to_clients"])
1454
+ @width = parse_integer(data["width"])
1455
+ end
1456
+
1457
+ def to_h
1458
+ {
1459
+ "app_download_url" => @app_download_url,
1460
+ "app_url" => @app_url,
1461
+ "attachable_sgid" => @attachable_sgid,
1462
+ "bookmark_url" => @bookmark_url,
1463
+ "boosts_count" => @boosts_count,
1464
+ "boosts_url" => @boosts_url,
1465
+ "bucket" => @bucket,
1466
+ "byte_size" => @byte_size,
1467
+ "comments_count" => @comments_count,
1468
+ "comments_url" => @comments_url,
1469
+ "content" => @content,
1470
+ "content_attachments" => @content_attachments,
1471
+ "content_type" => @content_type,
1472
+ "created_at" => @created_at,
1473
+ "creator" => @creator,
1474
+ "description" => @description,
1475
+ "description_attachments" => @description_attachments,
1476
+ "download_url" => @download_url,
1477
+ "filename" => @filename,
1478
+ "height" => @height,
1479
+ "id" => @id,
1480
+ "inherits_status" => @inherits_status,
1481
+ "parent" => @parent,
1482
+ "position" => @position,
1483
+ "status" => @status,
1484
+ "subscription_url" => @subscription_url,
1485
+ "title" => @title,
1486
+ "type" => @type,
1487
+ "updated_at" => @updated_at,
1488
+ "url" => @url,
1489
+ "visible_to_clients" => @visible_to_clients,
1490
+ "width" => @width,
1491
+ }.compact
1492
+ end
1493
+
1494
+ def to_json(*args)
1495
+ to_h.to_json(*args)
1496
+ end
1497
+ end
1498
+
1335
1499
  # Forward
1336
1500
  class Forward
1337
1501
  include TypeHelpers
@@ -2900,11 +3064,11 @@ module Basecamp
2900
3064
  # Recording
2901
3065
  class Recording
2902
3066
  include TypeHelpers
2903
- attr_accessor :app_url, :bucket, :created_at, :creator, :id, :inherits_status, :parent, :status, :title, :type, :updated_at, :url, :visible_to_clients, :bookmark_url, :comments_count, :comments_url, :content, :content_attachments, :description_attachments, :subscription_url
3067
+ attr_accessor :app_url, :bucket, :created_at, :creator, :id, :inherits_status, :status, :title, :type, :updated_at, :url, :visible_to_clients, :bookmark_url, :boosts_count, :boosts_url, :bubble_up_url, :category, :comments_count, :comments_url, :content, :content_attachments, :description, :description_attachments, :from, :group_on, :parent, :position, :replies_count, :replies_url, :service, :subject, :subscription_url
2904
3068
 
2905
3069
  # @return [Array<Symbol>]
2906
3070
  def self.required_fields
2907
- %i[app_url bucket created_at creator id inherits_status parent status title type updated_at url visible_to_clients].freeze
3071
+ %i[app_url bucket created_at creator id inherits_status status title type updated_at url visible_to_clients].freeze
2908
3072
  end
2909
3073
 
2910
3074
  def initialize(data = {})
@@ -2914,7 +3078,6 @@ module Basecamp
2914
3078
  @creator = parse_type(data["creator"], "Person")
2915
3079
  @id = parse_integer(data["id"])
2916
3080
  @inherits_status = parse_boolean(data["inherits_status"])
2917
- @parent = parse_type(data["parent"], "RecordingParent")
2918
3081
  @status = data["status"]
2919
3082
  @title = data["title"]
2920
3083
  @type = data["type"]
@@ -2922,11 +3085,24 @@ module Basecamp
2922
3085
  @url = data["url"]
2923
3086
  @visible_to_clients = parse_boolean(data["visible_to_clients"])
2924
3087
  @bookmark_url = data["bookmark_url"]
3088
+ @boosts_count = parse_integer(data["boosts_count"])
3089
+ @boosts_url = data["boosts_url"]
3090
+ @bubble_up_url = data["bubble_up_url"]
3091
+ @category = parse_type(data["category"], "RecordingCategory")
2925
3092
  @comments_count = parse_integer(data["comments_count"])
2926
3093
  @comments_url = data["comments_url"]
2927
3094
  @content = data["content"]
2928
3095
  @content_attachments = parse_array(data["content_attachments"], "RichTextAttachment")
3096
+ @description = data["description"]
2929
3097
  @description_attachments = parse_array(data["description_attachments"], "RichTextAttachment")
3098
+ @from = data["from"]
3099
+ @group_on = data["group_on"]
3100
+ @parent = parse_type(data["parent"], "RecordingParent")
3101
+ @position = parse_integer(data["position"])
3102
+ @replies_count = parse_integer(data["replies_count"])
3103
+ @replies_url = data["replies_url"]
3104
+ @service = parse_type(data["service"], "DoorService")
3105
+ @subject = data["subject"]
2930
3106
  @subscription_url = data["subscription_url"]
2931
3107
  end
2932
3108
 
@@ -2938,7 +3114,6 @@ module Basecamp
2938
3114
  "creator" => @creator,
2939
3115
  "id" => @id,
2940
3116
  "inherits_status" => @inherits_status,
2941
- "parent" => @parent,
2942
3117
  "status" => @status,
2943
3118
  "title" => @title,
2944
3119
  "type" => @type,
@@ -2946,11 +3121,24 @@ module Basecamp
2946
3121
  "url" => @url,
2947
3122
  "visible_to_clients" => @visible_to_clients,
2948
3123
  "bookmark_url" => @bookmark_url,
3124
+ "boosts_count" => @boosts_count,
3125
+ "boosts_url" => @boosts_url,
3126
+ "bubble_up_url" => @bubble_up_url,
3127
+ "category" => @category,
2949
3128
  "comments_count" => @comments_count,
2950
3129
  "comments_url" => @comments_url,
2951
3130
  "content" => @content,
2952
3131
  "content_attachments" => @content_attachments,
3132
+ "description" => @description,
2953
3133
  "description_attachments" => @description_attachments,
3134
+ "from" => @from,
3135
+ "group_on" => @group_on,
3136
+ "parent" => @parent,
3137
+ "position" => @position,
3138
+ "replies_count" => @replies_count,
3139
+ "replies_url" => @replies_url,
3140
+ "service" => @service,
3141
+ "subject" => @subject,
2954
3142
  "subscription_url" => @subscription_url,
2955
3143
  }.compact
2956
3144
  end
@@ -2989,10 +3177,39 @@ module Basecamp
2989
3177
  end
2990
3178
  end
2991
3179
 
3180
+ # RecordingCategory
3181
+ class RecordingCategory
3182
+ include TypeHelpers
3183
+ attr_accessor :id, :name, :icon
3184
+
3185
+ # @return [Array<Symbol>]
3186
+ def self.required_fields
3187
+ %i[id name].freeze
3188
+ end
3189
+
3190
+ def initialize(data = {})
3191
+ @id = parse_integer(data["id"])
3192
+ @name = data["name"]
3193
+ @icon = data["icon"]
3194
+ end
3195
+
3196
+ def to_h
3197
+ {
3198
+ "id" => @id,
3199
+ "name" => @name,
3200
+ "icon" => @icon,
3201
+ }.compact
3202
+ end
3203
+
3204
+ def to_json(*args)
3205
+ to_h.to_json(*args)
3206
+ end
3207
+ end
3208
+
2992
3209
  # RecordingParent
2993
3210
  class RecordingParent
2994
3211
  include TypeHelpers
2995
- attr_accessor :app_url, :id, :title, :type, :url
3212
+ attr_accessor :app_url, :id, :title, :type, :url, :bucket
2996
3213
 
2997
3214
  # @return [Array<Symbol>]
2998
3215
  def self.required_fields
@@ -3005,6 +3222,7 @@ module Basecamp
3005
3222
  @title = data["title"]
3006
3223
  @type = data["type"]
3007
3224
  @url = data["url"]
3225
+ @bucket = parse_type(data["bucket"], "RecordingBucket")
3008
3226
  end
3009
3227
 
3010
3228
  def to_h
@@ -3014,6 +3232,7 @@ module Basecamp
3014
3232
  "title" => @title,
3015
3233
  "type" => @type,
3016
3234
  "url" => @url,
3235
+ "bucket" => @bucket,
3017
3236
  }.compact
3018
3237
  end
3019
3238
 
@@ -3261,29 +3480,32 @@ module Basecamp
3261
3480
  # SearchResult
3262
3481
  class SearchResult
3263
3482
  include TypeHelpers
3264
- attr_accessor :app_url, :id, :title, :type, :url, :bookmark_url, :bucket, :content, :content_attachments, :created_at, :creator, :description, :description_attachments, :inherits_status, :parent, :status, :subject, :updated_at, :visible_to_clients
3483
+ attr_accessor :app_url, :content, :description, :id, :title, :type, :url, :bookmark_url, :bubble_up_url, :bucket, :content_attachments, :created_at, :creator, :description_attachments, :inherits_status, :parent, :plain_text_content, :plain_text_description, :status, :subject, :updated_at, :visible_to_clients
3265
3484
 
3266
3485
  # @return [Array<Symbol>]
3267
3486
  def self.required_fields
3268
- %i[app_url id title type url].freeze
3487
+ %i[app_url content description id title type url].freeze
3269
3488
  end
3270
3489
 
3271
3490
  def initialize(data = {})
3272
3491
  @app_url = data["app_url"]
3492
+ @content = data["content"]
3493
+ @description = data["description"]
3273
3494
  @id = parse_integer(data["id"])
3274
3495
  @title = data["title"]
3275
3496
  @type = data["type"]
3276
3497
  @url = data["url"]
3277
3498
  @bookmark_url = data["bookmark_url"]
3499
+ @bubble_up_url = data["bubble_up_url"]
3278
3500
  @bucket = parse_type(data["bucket"], "RecordingBucket")
3279
- @content = data["content"]
3280
3501
  @content_attachments = parse_array(data["content_attachments"], "RichTextAttachment")
3281
3502
  @created_at = parse_datetime(data["created_at"])
3282
3503
  @creator = parse_type(data["creator"], "Person")
3283
- @description = data["description"]
3284
3504
  @description_attachments = parse_array(data["description_attachments"], "RichTextAttachment")
3285
3505
  @inherits_status = parse_boolean(data["inherits_status"])
3286
3506
  @parent = parse_type(data["parent"], "RecordingParent")
3507
+ @plain_text_content = data["plain_text_content"]
3508
+ @plain_text_description = data["plain_text_description"]
3287
3509
  @status = data["status"]
3288
3510
  @subject = data["subject"]
3289
3511
  @updated_at = parse_datetime(data["updated_at"])
@@ -3293,25 +3515,28 @@ module Basecamp
3293
3515
  def to_h
3294
3516
  {
3295
3517
  "app_url" => @app_url,
3518
+ "content" => @content,
3519
+ "description" => @description,
3296
3520
  "id" => @id,
3297
3521
  "title" => @title,
3298
3522
  "type" => @type,
3299
3523
  "url" => @url,
3300
3524
  "bookmark_url" => @bookmark_url,
3525
+ "bubble_up_url" => @bubble_up_url,
3301
3526
  "bucket" => @bucket,
3302
- "content" => @content,
3303
3527
  "content_attachments" => @content_attachments,
3304
3528
  "created_at" => @created_at,
3305
3529
  "creator" => @creator,
3306
- "description" => @description,
3307
3530
  "description_attachments" => @description_attachments,
3308
3531
  "inherits_status" => @inherits_status,
3309
3532
  "parent" => @parent,
3533
+ "plain_text_content" => @plain_text_content,
3534
+ "plain_text_description" => @plain_text_description,
3310
3535
  "status" => @status,
3311
3536
  "subject" => @subject,
3312
3537
  "updated_at" => @updated_at,
3313
3538
  "visible_to_clients" => @visible_to_clients,
3314
- }.compact
3539
+ }.reject { |k, v| v.nil? && !["content", "description"].include?(k) }
3315
3540
  end
3316
3541
 
3317
3542
  def to_json(*args)
@@ -3418,17 +3643,112 @@ module Basecamp
3418
3643
  end
3419
3644
  end
3420
3645
 
3646
+ # TimelineAttachment
3647
+ class TimelineAttachment
3648
+ include TypeHelpers
3649
+ attr_accessor :app_download_url, :app_url, :attachable_sgid, :bookmark_url, :boosts_count, :boosts_url, :bucket, :byte_size, :caption, :comments_count, :comments_url, :content_type, :created_at, :creator, :description, :description_attachments, :download_url, :filename, :height, :id, :inherits_status, :key, :parent, :position, :preview_url, :previewable, :sgid, :status, :status_url, :subscription_url, :thumbnail_url, :title, :type, :updated_at, :url, :visible_to_clients, :width
3650
+
3651
+ def initialize(data = {})
3652
+ @app_download_url = data["app_download_url"]
3653
+ @app_url = data["app_url"]
3654
+ @attachable_sgid = data["attachable_sgid"]
3655
+ @bookmark_url = data["bookmark_url"]
3656
+ @boosts_count = parse_integer(data["boosts_count"])
3657
+ @boosts_url = data["boosts_url"]
3658
+ @bucket = parse_type(data["bucket"], "TodoBucket")
3659
+ @byte_size = parse_integer(data["byte_size"])
3660
+ @caption = data["caption"]
3661
+ @comments_count = parse_integer(data["comments_count"])
3662
+ @comments_url = data["comments_url"]
3663
+ @content_type = data["content_type"]
3664
+ @created_at = parse_datetime(data["created_at"])
3665
+ @creator = parse_type(data["creator"], "Person")
3666
+ @description = data["description"]
3667
+ @description_attachments = parse_array(data["description_attachments"], "RichTextAttachment")
3668
+ @download_url = data["download_url"]
3669
+ @filename = data["filename"]
3670
+ @height = parse_integer(data["height"])
3671
+ @id = parse_integer(data["id"])
3672
+ @inherits_status = parse_boolean(data["inherits_status"])
3673
+ @key = data["key"]
3674
+ @parent = parse_type(data["parent"], "RecordingParent")
3675
+ @position = parse_integer(data["position"])
3676
+ @preview_url = data["preview_url"]
3677
+ @previewable = parse_boolean(data["previewable"])
3678
+ @sgid = data["sgid"]
3679
+ @status = data["status"]
3680
+ @status_url = data["status_url"]
3681
+ @subscription_url = data["subscription_url"]
3682
+ @thumbnail_url = data["thumbnail_url"]
3683
+ @title = data["title"]
3684
+ @type = data["type"]
3685
+ @updated_at = parse_datetime(data["updated_at"])
3686
+ @url = data["url"]
3687
+ @visible_to_clients = parse_boolean(data["visible_to_clients"])
3688
+ @width = parse_integer(data["width"])
3689
+ end
3690
+
3691
+ def to_h
3692
+ {
3693
+ "app_download_url" => @app_download_url,
3694
+ "app_url" => @app_url,
3695
+ "attachable_sgid" => @attachable_sgid,
3696
+ "bookmark_url" => @bookmark_url,
3697
+ "boosts_count" => @boosts_count,
3698
+ "boosts_url" => @boosts_url,
3699
+ "bucket" => @bucket,
3700
+ "byte_size" => @byte_size,
3701
+ "caption" => @caption,
3702
+ "comments_count" => @comments_count,
3703
+ "comments_url" => @comments_url,
3704
+ "content_type" => @content_type,
3705
+ "created_at" => @created_at,
3706
+ "creator" => @creator,
3707
+ "description" => @description,
3708
+ "description_attachments" => @description_attachments,
3709
+ "download_url" => @download_url,
3710
+ "filename" => @filename,
3711
+ "height" => @height,
3712
+ "id" => @id,
3713
+ "inherits_status" => @inherits_status,
3714
+ "key" => @key,
3715
+ "parent" => @parent,
3716
+ "position" => @position,
3717
+ "preview_url" => @preview_url,
3718
+ "previewable" => @previewable,
3719
+ "sgid" => @sgid,
3720
+ "status" => @status,
3721
+ "status_url" => @status_url,
3722
+ "subscription_url" => @subscription_url,
3723
+ "thumbnail_url" => @thumbnail_url,
3724
+ "title" => @title,
3725
+ "type" => @type,
3726
+ "updated_at" => @updated_at,
3727
+ "url" => @url,
3728
+ "visible_to_clients" => @visible_to_clients,
3729
+ "width" => @width,
3730
+ }.compact
3731
+ end
3732
+
3733
+ def to_json(*args)
3734
+ to_h.to_json(*args)
3735
+ end
3736
+ end
3737
+
3421
3738
  # TimelineEvent
3422
3739
  class TimelineEvent
3423
3740
  include TypeHelpers
3424
- attr_accessor :action, :app_url, :bucket, :created_at, :creator, :id, :kind, :parent_recording_id, :summary_excerpt, :target, :title, :url
3741
+ attr_accessor :action, :app_url, :attachments, :avatars_sample, :bucket, :created_at, :creator, :data, :id, :kind, :parent_recording_id, :summary_excerpt, :target, :title, :url
3425
3742
 
3426
3743
  def initialize(data = {})
3427
3744
  @action = data["action"]
3428
3745
  @app_url = data["app_url"]
3746
+ @attachments = parse_array(data["attachments"], "TimelineAttachment")
3747
+ @avatars_sample = data["avatars_sample"]
3429
3748
  @bucket = parse_type(data["bucket"], "TodoBucket")
3430
3749
  @created_at = parse_datetime(data["created_at"])
3431
3750
  @creator = parse_type(data["creator"], "Person")
3751
+ @data = parse_type(data["data"], "TimelineEventData")
3432
3752
  @id = parse_integer(data["id"])
3433
3753
  @kind = data["kind"]
3434
3754
  @parent_recording_id = parse_integer(data["parent_recording_id"])
@@ -3442,9 +3762,12 @@ module Basecamp
3442
3762
  {
3443
3763
  "action" => @action,
3444
3764
  "app_url" => @app_url,
3765
+ "attachments" => @attachments,
3766
+ "avatars_sample" => @avatars_sample,
3445
3767
  "bucket" => @bucket,
3446
3768
  "created_at" => @created_at,
3447
3769
  "creator" => @creator,
3770
+ "data" => @data,
3448
3771
  "id" => @id,
3449
3772
  "kind" => @kind,
3450
3773
  "parent_recording_id" => @parent_recording_id,
@@ -3460,6 +3783,35 @@ module Basecamp
3460
3783
  end
3461
3784
  end
3462
3785
 
3786
+ # TimelineEventData
3787
+ class TimelineEventData
3788
+ include TypeHelpers
3789
+ attr_accessor :all_day, :ends_at, :starts_at
3790
+
3791
+ # @return [Array<Symbol>]
3792
+ def self.required_fields
3793
+ %i[all_day ends_at starts_at].freeze
3794
+ end
3795
+
3796
+ def initialize(data = {})
3797
+ @all_day = parse_boolean(data["all_day"])
3798
+ @ends_at = data["ends_at"]
3799
+ @starts_at = data["starts_at"]
3800
+ end
3801
+
3802
+ def to_h
3803
+ {
3804
+ "all_day" => @all_day,
3805
+ "ends_at" => @ends_at,
3806
+ "starts_at" => @starts_at,
3807
+ }.reject { |k, v| v.nil? && !["ends_at", "starts_at"].include?(k) }
3808
+ end
3809
+
3810
+ def to_json(*args)
3811
+ to_h.to_json(*args)
3812
+ end
3813
+ end
3814
+
3463
3815
  # TimesheetEntry
3464
3816
  class TimesheetEntry
3465
3817
  include TypeHelpers
@@ -3667,15 +4019,16 @@ module Basecamp
3667
4019
  # Todolist
3668
4020
  class Todolist
3669
4021
  include TypeHelpers
3670
- attr_accessor :app_url, :bucket, :created_at, :creator, :description_attachments, :id, :inherits_status, :name, :parent, :status, :title, :type, :updated_at, :url, :visible_to_clients, :app_todos_url, :bookmark_url, :boosts_count, :boosts_url, :comments_count, :comments_url, :completed, :completed_ratio, :description, :groups_url, :position, :subscription_url, :todos_url
4022
+ attr_accessor :app_url, :bubble_up_url, :bucket, :created_at, :creator, :description_attachments, :id, :inherits_status, :name, :parent, :status, :title, :type, :updated_at, :url, :visible_to_clients, :app_todos_url, :bookmark_url, :boosts_count, :boosts_url, :comments_count, :comments_url, :completed, :completed_ratio, :description, :groups_url, :position, :subscription_url, :todos_url
3671
4023
 
3672
4024
  # @return [Array<Symbol>]
3673
4025
  def self.required_fields
3674
- %i[app_url bucket created_at creator description_attachments id inherits_status name parent status title type updated_at url visible_to_clients].freeze
4026
+ %i[app_url bubble_up_url bucket created_at creator description_attachments id inherits_status name parent status title type updated_at url visible_to_clients].freeze
3675
4027
  end
3676
4028
 
3677
4029
  def initialize(data = {})
3678
4030
  @app_url = data["app_url"]
4031
+ @bubble_up_url = data["bubble_up_url"]
3679
4032
  @bucket = parse_type(data["bucket"], "TodoBucket")
3680
4033
  @created_at = parse_datetime(data["created_at"])
3681
4034
  @creator = parse_type(data["creator"], "Person")
@@ -3708,6 +4061,7 @@ module Basecamp
3708
4061
  def to_h
3709
4062
  {
3710
4063
  "app_url" => @app_url,
4064
+ "bubble_up_url" => @bubble_up_url,
3711
4065
  "bucket" => @bucket,
3712
4066
  "created_at" => @created_at,
3713
4067
  "creator" => @creator,
@@ -3746,15 +4100,16 @@ module Basecamp
3746
4100
  # TodolistGroup
3747
4101
  class TodolistGroup
3748
4102
  include TypeHelpers
3749
- attr_accessor :app_url, :bucket, :created_at, :creator, :id, :inherits_status, :name, :parent, :status, :title, :type, :updated_at, :url, :visible_to_clients, :app_todos_url, :bookmark_url, :comments_count, :comments_url, :completed, :completed_ratio, :position, :subscription_url, :todos_url
4103
+ attr_accessor :app_url, :bubble_up_url, :bucket, :created_at, :creator, :id, :inherits_status, :name, :parent, :status, :title, :type, :updated_at, :url, :visible_to_clients, :app_todos_url, :bookmark_url, :comments_count, :comments_url, :completed, :completed_ratio, :position, :subscription_url, :todos_url
3750
4104
 
3751
4105
  # @return [Array<Symbol>]
3752
4106
  def self.required_fields
3753
- %i[app_url bucket created_at creator id inherits_status name parent status title type updated_at url visible_to_clients].freeze
4107
+ %i[app_url bubble_up_url bucket created_at creator id inherits_status name parent status title type updated_at url visible_to_clients].freeze
3754
4108
  end
3755
4109
 
3756
4110
  def initialize(data = {})
3757
4111
  @app_url = data["app_url"]
4112
+ @bubble_up_url = data["bubble_up_url"]
3758
4113
  @bucket = parse_type(data["bucket"], "TodoBucket")
3759
4114
  @created_at = parse_datetime(data["created_at"])
3760
4115
  @creator = parse_type(data["creator"], "Person")
@@ -3782,6 +4137,7 @@ module Basecamp
3782
4137
  def to_h
3783
4138
  {
3784
4139
  "app_url" => @app_url,
4140
+ "bubble_up_url" => @bubble_up_url,
3785
4141
  "bucket" => @bucket,
3786
4142
  "created_at" => @created_at,
3787
4143
  "creator" => @creator,