basecamp-sdk 0.12.0 → 0.14.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 +128 -8
  3. data/lib/basecamp/client.rb +35 -11
  4. data/lib/basecamp/config.rb +69 -0
  5. data/lib/basecamp/error.rb +1 -0
  6. data/lib/basecamp/error_code.rb +1 -0
  7. data/lib/basecamp/exit_code.rb +1 -0
  8. data/lib/basecamp/generated/metadata.json +291 -141
  9. data/lib/basecamp/generated/services/base_service.rb +37 -16
  10. data/lib/basecamp/generated/services/bookmarks_service.rb +5 -4
  11. data/lib/basecamp/generated/services/boosts_service.rb +12 -6
  12. data/lib/basecamp/generated/services/campfires_service.rb +54 -41
  13. data/lib/basecamp/generated/services/cards_service.rb +6 -3
  14. data/lib/basecamp/generated/services/checkins_service.rb +28 -15
  15. data/lib/basecamp/generated/services/client_approvals_service.rb +8 -5
  16. data/lib/basecamp/generated/services/client_correspondences_service.rb +8 -5
  17. data/lib/basecamp/generated/services/client_replies_service.rb +12 -7
  18. data/lib/basecamp/generated/services/cloud_files_service.rb +57 -0
  19. data/lib/basecamp/generated/services/comments_service.rb +6 -3
  20. data/lib/basecamp/generated/services/documents_service.rb +9 -6
  21. data/lib/basecamp/generated/services/drafts_service.rb +5 -4
  22. data/lib/basecamp/generated/services/events_service.rb +6 -3
  23. data/lib/basecamp/generated/services/everything_service.rb +70 -56
  24. data/lib/basecamp/generated/services/folders_service.rb +62 -0
  25. data/lib/basecamp/generated/services/forwards_service.rb +12 -17
  26. data/lib/basecamp/generated/services/gauges_service.rb +12 -7
  27. data/lib/basecamp/generated/services/google_documents_service.rb +61 -0
  28. data/lib/basecamp/generated/services/message_types_service.rb +4 -3
  29. data/lib/basecamp/generated/services/messages_service.rb +6 -4
  30. data/lib/basecamp/generated/services/my_notes_service.rb +1 -1
  31. data/lib/basecamp/generated/services/my_notifications_service.rb +8 -5
  32. data/lib/basecamp/generated/services/people_service.rb +17 -10
  33. data/lib/basecamp/generated/services/projects_service.rb +26 -4
  34. data/lib/basecamp/generated/services/recordings_service.rb +6 -13
  35. data/lib/basecamp/generated/services/reports_service.rb +15 -9
  36. data/lib/basecamp/generated/services/schedules_service.rb +90 -16
  37. data/lib/basecamp/generated/services/search_service.rb +6 -4
  38. data/lib/basecamp/generated/services/templates_service.rb +6 -4
  39. data/lib/basecamp/generated/services/timeline_service.rb +6 -3
  40. data/lib/basecamp/generated/services/timesheets_service.rb +22 -8
  41. data/lib/basecamp/generated/services/todolist_groups_service.rb +7 -4
  42. data/lib/basecamp/generated/services/todolists_service.rb +11 -9
  43. data/lib/basecamp/generated/services/todos_service.rb +6 -14
  44. data/lib/basecamp/generated/services/uploads_service.rb +28 -6
  45. data/lib/basecamp/generated/services/vaults_service.rb +6 -3
  46. data/lib/basecamp/generated/services/webhooks_service.rb +4 -3
  47. data/lib/basecamp/generated/types.rb +603 -139
  48. data/lib/basecamp/http.rb +352 -163
  49. data/lib/basecamp/limit_exceeded_error.rb +22 -0
  50. data/lib/basecamp/list_enumerator.rb +29 -0
  51. data/lib/basecamp/list_meta.rb +44 -0
  52. data/lib/basecamp/services/authorization_service.rb +11 -2
  53. data/lib/basecamp/services/cards_extensions.rb +35 -27
  54. data/lib/basecamp/services/documents_extensions.rb +136 -0
  55. data/lib/basecamp/services/merge_safe.rb +255 -0
  56. data/lib/basecamp/services/schedules_extensions.rb +354 -0
  57. data/lib/basecamp/services/todolists_extensions.rb +274 -0
  58. data/lib/basecamp/services/todos_extensions.rb +22 -6
  59. data/lib/basecamp/validation_error.rb +11 -1
  60. data/lib/basecamp/version.rb +2 -2
  61. data/lib/basecamp.rb +98 -4
  62. data/scripts/generate-metadata.rb +3 -1
  63. data/scripts/generate-services.rb +78 -27
  64. data/scripts/generate-types.rb +4 -2
  65. data/scripts/go_type_spellings.rb +26 -0
  66. metadata +13 -2
@@ -9,11 +9,13 @@ module Basecamp
9
9
 
10
10
  # List projects (active by default; optionally archived/trashed)
11
11
  # @param status [String, nil] active|archived|trashed
12
- # @return [Enumerator<Hash>] paginated results
13
- def list(status: nil)
12
+ # @param page [Integer, nil] Page number for paginating through results. Defaults to 1. A positive value selects exactly that page, not a starting offset; see SPEC section 8.
13
+ # @param max_items [Integer, nil] cap on items yielded across pages; nil or non-positive means no cap
14
+ # @return [ListEnumerator<Hash>] lazily paginated results (#meta carries pagination metadata)
15
+ def list(status: nil, page: nil, max_items: nil)
14
16
  wrap_paginated(service: "projects", operation: "list", is_mutation: false) do
15
- params = compact_query_params(status: status)
16
- paginate("/projects.json", params: params, operation: "ListProjects")
17
+ params = compact_query_params(status: status, page: page)
18
+ paginate("/projects.json", params: params, operation: "ListProjects", max_items: max_items)
17
19
  end
18
20
  end
19
21
 
@@ -58,6 +60,26 @@ module Basecamp
58
60
  nil
59
61
  end
60
62
  end
63
+
64
+ # Restore a project to active status from trash as well as from the archive (returns 204 No Content).
65
+ # @param project_id [Integer] project id ID
66
+ # @return [void]
67
+ def unarchive(project_id:)
68
+ with_operation(service: "projects", operation: "unarchive", is_mutation: true, project_id: project_id) do
69
+ http_put("/projects/#{project_id}/status/active.json")
70
+ nil
71
+ end
72
+ end
73
+
74
+ # Archive a project, removing it from the active project list (returns 204 No Content).
75
+ # @param project_id [Integer] project id ID
76
+ # @return [void]
77
+ def archive(project_id:)
78
+ with_operation(service: "projects", operation: "archive", is_mutation: true, project_id: project_id) do
79
+ http_put("/projects/#{project_id}/status/archived.json")
80
+ nil
81
+ end
82
+ end
61
83
  end
62
84
  end
63
85
  end
@@ -13,20 +13,13 @@ module Basecamp
13
13
  # @param status [String, nil] active|archived|trashed
14
14
  # @param sort [String, nil] created_at|updated_at
15
15
  # @param direction [String, nil] asc|desc
16
- # @return [Enumerator<Hash>] paginated results
17
- def list(type:, bucket: nil, status: nil, sort: nil, direction: nil)
16
+ # @param page [Integer, nil] Page number for paginating through results. Defaults to 1. A positive value selects exactly that page, not a starting offset; see SPEC section 8.
17
+ # @param max_items [Integer, nil] cap on items yielded across pages; nil or non-positive means no cap
18
+ # @return [ListEnumerator<Hash>] lazily paginated results (#meta carries pagination metadata)
19
+ def list(type:, bucket: nil, status: nil, sort: nil, direction: nil, page: nil, max_items: nil)
18
20
  wrap_paginated(service: "recordings", operation: "list", is_mutation: false) do
19
- params = compact_query_params(type: type, bucket: bucket, status: status, sort: sort, direction: direction)
20
- paginate("/projects/recordings.json", params: params, operation: "ListRecordings")
21
- end
22
- end
23
-
24
- # Get a single recording by id
25
- # @param recording_id [Integer] recording id ID
26
- # @return [Hash] response data
27
- def get(recording_id:)
28
- with_operation(service: "recordings", operation: "get", is_mutation: false, resource_id: recording_id) do
29
- http_get("/recordings/#{recording_id}", operation: "GetRecording").json
21
+ params = compact_query_params(type: type, bucket: bucket, status: status, sort: sort, direction: direction, page: page)
22
+ paginate("/projects/recordings.json", params: params, operation: "ListRecordings", max_items: max_items)
30
23
  end
31
24
  end
32
25
 
@@ -8,18 +8,21 @@ module Basecamp
8
8
  class ReportsService < BaseService
9
9
 
10
10
  # Get account-wide activity feed (progress report)
11
- # @return [Enumerator<Hash>] paginated results
12
- def progress()
11
+ # @param page [Integer, nil] Page number for paginating through results. Defaults to 1. A positive value selects exactly that page, not a starting offset; see SPEC section 8.
12
+ # @param max_items [Integer, nil] cap on items yielded across pages; nil or non-positive means no cap
13
+ # @return [ListEnumerator<Hash>] lazily paginated results (#meta carries pagination metadata)
14
+ def progress(page: nil, max_items: nil)
13
15
  wrap_paginated(service: "reports", operation: "progress", is_mutation: false) do
14
- paginate("/reports/progress.json", operation: "GetProgressReport")
16
+ params = compact_query_params(page: page)
17
+ paginate("/reports/progress.json", params: params, operation: "GetProgressReport", max_items: max_items)
15
18
  end
16
19
  end
17
20
 
18
21
  # Get upcoming schedule entries and assignable items within a date window.
19
- # @param window_starts_on [String, nil] window starts on
20
- # @param window_ends_on [String, nil] window ends on
22
+ # @param window_starts_on [String] Inclusive first day of the window, `YYYY-MM-DD`. Required — BC3 answers 400 without it.
23
+ # @param window_ends_on [String] Inclusive last day of the window, `YYYY-MM-DD`. Required — BC3 answers 400 without it.
21
24
  # @return [Hash] response data
22
- def upcoming(window_starts_on: nil, window_ends_on: nil)
25
+ def upcoming(window_starts_on:, window_ends_on:)
23
26
  with_operation(service: "reports", operation: "upcoming", is_mutation: false) do
24
27
  http_get("/reports/schedules/upcoming.json", params: compact_query_params(window_starts_on: window_starts_on, window_ends_on: window_ends_on), operation: "GetUpcomingSchedule").json
25
28
  end
@@ -45,10 +48,13 @@ module Basecamp
45
48
 
46
49
  # Get a person's activity timeline
47
50
  # @param person_id [Integer] person id ID
48
- # @return [Hash] response data
49
- def person_progress(person_id:)
51
+ # @param page [Integer, nil] Page number for paginating through results. Defaults to 1. A positive value selects exactly that page, not a starting offset; see SPEC section 8.
52
+ # @param max_items [Integer, nil] cap on items yielded across pages; nil or non-positive means no cap
53
+ # @return [Hash] wrapper fields merged with a ListEnumerator of the paginated items
54
+ def person_progress(person_id:, page: nil, max_items: nil)
50
55
  wrap_paginated_wrapped(key: "events", service: "reports", operation: "person_progress", is_mutation: false, resource_id: person_id) do
51
- paginate_wrapped("/reports/users/progress/#{person_id}.json", key: "events", operation: "GetPersonProgress")
56
+ params = compact_query_params(page: page)
57
+ paginate_wrapped("/reports/users/progress/#{person_id}.json", key: "events", params: params, operation: "GetPersonProgress", max_items: max_items)
52
58
  end
53
59
  end
54
60
  end
@@ -16,14 +16,16 @@ module Basecamp
16
16
  end
17
17
  end
18
18
 
19
- # Update an existing schedule entry
19
+ # Replace a schedule entry with a new complete representation.
20
20
  # @param entry_id [Integer] entry id ID
21
21
  # @param summary [String, nil] summary
22
- # @param starts_at [String, nil] starts at (RFC3339 (e.g., 2024-12-15T09:00:00Z))
23
- # @param ends_at [String, nil] ends at (RFC3339 (e.g., 2024-12-15T09:00:00Z))
22
+ # @param starts_at [String] The entry's start, as a bare date ("2026-06-01") for an all-day entry or a
23
+ # full timestamp otherwise. Same rule as CreateScheduleEntry: send it
24
+ # verbatim, never parsed and re-rendered.
25
+ # @param ends_at [String] The entry's end. See starts_at for the date-vs-timestamp rule.
24
26
  # @param description [String, nil] description
25
27
  # @param participant_ids [Array, nil] Replaces the entry's participants.
26
- #
28
+ #
27
29
  # Omitting this member preserves the current participants; sending an empty
28
30
  # array clears them. That guarantee is BC3-side and recent: until
29
31
  # basecamp/bc3#12425, `Schedules::EntriesController#update` called
@@ -31,12 +33,36 @@ module Basecamp
31
33
  # including the shape in BC3's own "Update a schedule entry" doc example —
32
34
  # silently removed every participant and notified each one. The controller
33
35
  # now guards on the request actually addressing participants.
34
- # @param all_day [Boolean, nil] all day
36
+ # @param all_day [Boolean, nil] Whether the entry occupies whole days rather than a time range.
37
+ #
38
+ # Not carved out, and the carve-out list is what makes that dangerous to
39
+ # forget: `schedule_entries.all_day` is NOT NULL with a `false` default, so
40
+ # omitting this member on a replace resets it — silently converting an
41
+ # all-day entry into a midnight-to-midnight timed one. The SDK's merge-safe
42
+ # update and edit resend it from the read-back for exactly this reason.
43
+ #
44
+ # Sending an explicit null is worse than omitting it: the column rejects
45
+ # NULL, so BC3 raises rather than falling back to the default. The same is
46
+ # true of highlighted.
35
47
  # @param notify [Boolean, nil] notify
48
+ # @param url [String, nil] The entry's join link — a video-call URL or similar, up to 2500
49
+ # characters, validated as a URL when present.
50
+ #
51
+ # Omitting this member preserves the current join link; sending an empty
52
+ # string clears it. Read it back as `join_url`, never as `url`: the entry's
53
+ # `url` is its own Basecamp API URL, written by a partial that renders
54
+ # before this one, so BC3 emits the join link under a non-colliding key.
55
+ # Echoing the response's `url` into this member would write the API URL into
56
+ # the join link.
57
+ # @param highlighted [Boolean, nil] Whether the entry is highlighted on the schedule.
58
+ #
59
+ # Omitting this member preserves the current highlight; sending false
60
+ # removes it. Preserved on omission because until basecamp/bc3#12502 the
61
+ # field was writable but never returned, so no caller could resend it.
36
62
  # @return [Hash] response data
37
- def update_entry(entry_id:, summary: nil, starts_at: nil, ends_at: nil, description: nil, participant_ids: nil, all_day: nil, notify: nil)
38
- with_operation(service: "schedules", operation: "update_entry", is_mutation: true, resource_id: entry_id) do
39
- http_put("/schedule_entries/#{entry_id}", 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
63
+ def replace_entry(entry_id:, starts_at:, ends_at:, summary: nil, description: nil, participant_ids: nil, all_day: nil, notify: nil, url: nil, highlighted: nil)
64
+ with_operation(service: "schedules", operation: "replace_entry", is_mutation: true, resource_id: entry_id) do
65
+ http_put("/schedule_entries/#{entry_id}", 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, url: url, highlighted: highlighted)).json
40
66
  end
41
67
  end
42
68
 
@@ -72,29 +98,77 @@ module Basecamp
72
98
  # List entries on a schedule
73
99
  # @param schedule_id [Integer] schedule id ID
74
100
  # @param status [String, nil] active|archived|trashed
75
- # @return [Enumerator<Hash>] paginated results
76
- def list_entries(schedule_id:, status: nil)
101
+ # @param page [Integer, nil] Page number for paginating through results. Defaults to 1. A positive value selects exactly that page, not a starting offset; see SPEC section 8.
102
+ # @param max_items [Integer, nil] cap on items yielded across pages; nil or non-positive means no cap
103
+ # @return [ListEnumerator<Hash>] lazily paginated results (#meta carries pagination metadata)
104
+ def list_entries(schedule_id:, status: nil, page: nil, max_items: nil)
77
105
  wrap_paginated(service: "schedules", operation: "list_entries", is_mutation: false, resource_id: schedule_id) do
78
- params = compact_query_params(status: status)
79
- paginate("/schedules/#{schedule_id}/entries.json", params: params, operation: "ListScheduleEntries")
106
+ params = compact_query_params(status: status, page: page)
107
+ paginate("/schedules/#{schedule_id}/entries.json", params: params, operation: "ListScheduleEntries", max_items: max_items)
80
108
  end
81
109
  end
82
110
 
83
111
  # Create a new schedule entry
84
112
  # @param schedule_id [Integer] schedule id ID
85
113
  # @param summary [String] summary
86
- # @param starts_at [String] starts at (RFC3339 (e.g., 2024-12-15T09:00:00Z))
87
- # @param ends_at [String] ends at (RFC3339 (e.g., 2024-12-15T09:00:00Z))
114
+ # @param starts_at [String] The entry's start, as a bare date ("2026-06-01") for an all-day entry or a
115
+ # full timestamp ("2026-06-01T09:00:00Z") otherwise — the same two forms the
116
+ # response renders, and the same two ReplaceScheduleEntry accepts.
117
+ #
118
+ # Create and replace share one permit list:
119
+ # `Schedules::Entries::BaseController#base_schedule_entry_params` is what
120
+ # both `new_schedule_entry_params` and `update_schedule_entry_params` call,
121
+ # and Schedule::Entry does no format-specific parsing of either bound, so
122
+ # whatever one operation takes the other takes too.
123
+ #
124
+ # Treat the value as opaque and send it verbatim. Parsing it into a
125
+ # date-time type and re-rendering rewrites an all-day entry's bounds into
126
+ # midnight timestamps, which is why every SDK models it as a string.
127
+ # @param ends_at [String] The entry's end. See starts_at for the date-vs-timestamp rule.
88
128
  # @param description [String, nil] description
89
129
  # @param participant_ids [Array, nil] participant ids
90
130
  # @param all_day [Boolean, nil] all day
91
131
  # @param notify [Boolean, nil] notify
132
+ # @param url [String, nil] The entry's join link — a video-call URL or similar, up to 2500
133
+ # characters, validated as a URL when present. A scheme-less value is
134
+ # normalized to `https://`.
135
+ #
136
+ # Spell it `url` on the way in and read it back as `join_url`: the response
137
+ # key `url` is the entry's own Basecamp API URL, written by a partial that
138
+ # renders before this field, so BC3 emits the join link under a
139
+ # non-colliding name. Sending `join_url` instead is silently dropped by
140
+ # strong parameters — the create succeeds with no join link.
141
+ #
142
+ # Accepted on create since long before it was documented:
143
+ # `Schedules::Entries::BaseController#base_schedule_entry_params` permits it
144
+ # and `new_schedule_entry_params` passes it through unchanged for API
145
+ # requests. Modeling it only on ReplaceScheduleEntry forced callers into a
146
+ # three-request read-modify-write for a field the create already took — and
147
+ # create is the notifying write, so participants learned about a video call
148
+ # before its link existed.
149
+ # @param highlighted [Boolean, nil] Whether the entry is highlighted on the schedule. Defaults to false.
150
+ #
151
+ # Do not send an explicit null: `schedule_entries.highlighted` is NOT NULL,
152
+ # so BC3 raises rather than falling back to the default. Omit it instead —
153
+ # every SDK's request compactor already drops unset members.
154
+ # @param status [String, nil] Publication state at creation — `active|drafted`, defaulting to `active`
155
+ # for an API create.
156
+ #
157
+ # A top-level parameter, not part of the entry's attributes: `status` is a
158
+ # Recording column, so `wrap_parameters` leaves it outside the
159
+ # `schedule_entry` envelope and `Recording::StatusParam#status_param` reads
160
+ # it directly. On create it accepts `drafted`, `active`, `archived` or
161
+ # `trashed` and raises `ActionController::BadRequest` — a 400, not a 422 —
162
+ # for anything else; the two documented values are the two worth sending.
163
+ #
164
+ # Unlike messages and documents, schedule-entry drafts are not listed by
165
+ # GetMyDrafts.
92
166
  # @param subscriptions [Array, nil] subscriptions
93
167
  # @param visible_to_clients [Boolean, nil] visible to clients
94
168
  # @return [Hash] response data
95
- def create_entry(schedule_id:, summary:, starts_at:, ends_at:, description: nil, participant_ids: nil, all_day: nil, notify: nil, subscriptions: nil, visible_to_clients: nil)
169
+ def create_entry(schedule_id:, summary:, starts_at:, ends_at:, description: nil, participant_ids: nil, all_day: nil, notify: nil, url: nil, highlighted: nil, status: nil, subscriptions: nil, visible_to_clients: nil)
96
170
  with_operation(service: "schedules", operation: "create_entry", is_mutation: true, resource_id: schedule_id) do
97
- 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, visible_to_clients: visible_to_clients)).json
171
+ 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, url: url, highlighted: highlighted, status: status, subscriptions: subscriptions, visible_to_clients: visible_to_clients)).json
98
172
  end
99
173
  end
100
174
  end
@@ -21,11 +21,13 @@ module Basecamp
21
21
  # @param type [String, nil] Deprecated: prefer type_names[].
22
22
  # @param bucket_id [Integer, nil] Deprecated: prefer bucket_ids[].
23
23
  # @param creator_id [Integer, nil] Deprecated: prefer creator_ids[].
24
- # @return [Enumerator<Hash>] paginated results
25
- def search(q:, type_names: nil, bucket_ids: nil, creator_ids: nil, file_type: nil, exclude_chat: nil, since: nil, sort: nil, type: nil, bucket_id: nil, creator_id: nil)
24
+ # @param page [Integer, nil] Page number for paginating through results. Defaults to 1. A positive value selects exactly that page, not a starting offset; see SPEC section 8.
25
+ # @param max_items [Integer, nil] cap on items yielded across pages; nil or non-positive means no cap
26
+ # @return [ListEnumerator<Hash>] lazily paginated results (#meta carries pagination metadata)
27
+ def search(q:, type_names: nil, bucket_ids: nil, creator_ids: nil, file_type: nil, exclude_chat: nil, since: nil, sort: nil, type: nil, bucket_id: nil, creator_id: nil, page: nil, max_items: nil)
26
28
  wrap_paginated(service: "search", operation: "search", is_mutation: false) do
27
- params = compact_query_params(q: q, type_names: type_names, bucket_ids: bucket_ids, creator_ids: creator_ids, file_type: file_type, exclude_chat: exclude_chat, since: since, sort: sort, type: type, bucket_id: bucket_id, creator_id: creator_id)
28
- paginate("/search.json", params: params, operation: "Search")
29
+ params = compact_query_params(q: q, type_names: type_names, bucket_ids: bucket_ids, creator_ids: creator_ids, file_type: file_type, exclude_chat: exclude_chat, since: since, sort: sort, type: type, bucket_id: bucket_id, creator_id: creator_id, page: page)
30
+ paginate("/search.json", params: params, operation: "Search", max_items: max_items)
29
31
  end
30
32
  end
31
33
 
@@ -9,11 +9,13 @@ module Basecamp
9
9
 
10
10
  # List all templates visible to the current user
11
11
  # @param status [String, nil] active|archived|trashed
12
- # @return [Enumerator<Hash>] paginated results
13
- def list(status: nil)
12
+ # @param page [Integer, nil] Page number for paginating through results. Defaults to 1. A positive value selects exactly that page, not a starting offset; see SPEC section 8.
13
+ # @param max_items [Integer, nil] cap on items yielded across pages; nil or non-positive means no cap
14
+ # @return [ListEnumerator<Hash>] lazily paginated results (#meta carries pagination metadata)
15
+ def list(status: nil, page: nil, max_items: nil)
14
16
  wrap_paginated(service: "templates", operation: "list", is_mutation: false) do
15
- params = compact_query_params(status: status)
16
- paginate("/templates.json", params: params, operation: "ListTemplates")
17
+ params = compact_query_params(status: status, page: page)
18
+ paginate("/templates.json", params: params, operation: "ListTemplates", max_items: max_items)
17
19
  end
18
20
  end
19
21
 
@@ -9,10 +9,13 @@ module Basecamp
9
9
 
10
10
  # Get project timeline
11
11
  # @param project_id [Integer] project id ID
12
- # @return [Enumerator<Hash>] paginated results
13
- def get_project_timeline(project_id:)
12
+ # @param page [Integer, nil] Page number for paginating through results. Defaults to 1. A positive value selects exactly that page, not a starting offset; see SPEC section 8.
13
+ # @param max_items [Integer, nil] cap on items yielded across pages; nil or non-positive means no cap
14
+ # @return [ListEnumerator<Hash>] lazily paginated results (#meta carries pagination metadata)
15
+ def get_project_timeline(project_id:, page: nil, max_items: nil)
14
16
  wrap_paginated(service: "timeline", operation: "get_project_timeline", is_mutation: false, project_id: project_id) do
15
- paginate("/projects/#{project_id}/timeline.json", operation: "GetProjectTimeline")
17
+ params = compact_query_params(page: page)
18
+ paginate("/projects/#{project_id}/timeline.json", params: params, operation: "GetProjectTimeline", max_items: max_items)
16
19
  end
17
20
  end
18
21
  end
@@ -12,11 +12,13 @@ module Basecamp
12
12
  # @param from [String, nil] from
13
13
  # @param to [String, nil] to
14
14
  # @param person_id [Integer, nil] person id
15
- # @return [Enumerator<Hash>] paginated results
16
- def for_project(project_id:, from: nil, to: nil, person_id: nil)
15
+ # @param page [Integer, nil] Page number for paginating through results. Defaults to 1. A positive value selects exactly that page, not a starting offset; see SPEC section 8.
16
+ # @param max_items [Integer, nil] cap on items yielded across pages; nil or non-positive means no cap
17
+ # @return [ListEnumerator<Hash>] lazily paginated results (#meta carries pagination metadata)
18
+ def for_project(project_id:, from: nil, to: nil, person_id: nil, page: nil, max_items: nil)
17
19
  wrap_paginated(service: "timesheets", operation: "for_project", is_mutation: false, project_id: project_id) do
18
- params = compact_query_params(from: from, to: to, person_id: person_id)
19
- paginate("/projects/#{project_id}/timesheet.json", params: params, operation: "GetProjectTimesheet")
20
+ params = compact_query_params(from: from, to: to, person_id: person_id, page: page)
21
+ paginate("/projects/#{project_id}/timesheet.json", params: params, operation: "GetProjectTimesheet", max_items: max_items)
20
22
  end
21
23
  end
22
24
 
@@ -25,11 +27,13 @@ module Basecamp
25
27
  # @param from [String, nil] from
26
28
  # @param to [String, nil] to
27
29
  # @param person_id [Integer, nil] person id
28
- # @return [Enumerator<Hash>] paginated results
29
- def for_recording(recording_id:, from: nil, to: nil, person_id: nil)
30
+ # @param page [Integer, nil] Page number for paginating through results. Defaults to 1. A positive value selects exactly that page, not a starting offset; see SPEC section 8.
31
+ # @param max_items [Integer, nil] cap on items yielded across pages; nil or non-positive means no cap
32
+ # @return [ListEnumerator<Hash>] lazily paginated results (#meta carries pagination metadata)
33
+ def for_recording(recording_id:, from: nil, to: nil, person_id: nil, page: nil, max_items: nil)
30
34
  wrap_paginated(service: "timesheets", operation: "for_recording", is_mutation: false, resource_id: recording_id) do
31
- params = compact_query_params(from: from, to: to, person_id: person_id)
32
- paginate("/recordings/#{recording_id}/timesheet.json", params: params, operation: "GetRecordingTimesheet")
35
+ params = compact_query_params(from: from, to: to, person_id: person_id, page: page)
36
+ paginate("/recordings/#{recording_id}/timesheet.json", params: params, operation: "GetRecordingTimesheet", max_items: max_items)
33
37
  end
34
38
  end
35
39
 
@@ -78,6 +82,16 @@ module Basecamp
78
82
  http_put("/timesheet_entries/#{entry_id}", body: compact_params(date: date, hours: hours, description: description, person_id: person_id)).json
79
83
  end
80
84
  end
85
+
86
+ # Permanently delete a timesheet entry; answers 403 when the caller may not archive or trash it.
87
+ # @param entry_id [Integer] entry id ID
88
+ # @return [void]
89
+ def destroy(entry_id:)
90
+ with_operation(service: "timesheets", operation: "destroy", is_mutation: true, resource_id: entry_id) do
91
+ http_delete("/timesheet_entries/#{entry_id}")
92
+ nil
93
+ end
94
+ end
81
95
  end
82
96
  end
83
97
  end
@@ -13,17 +13,20 @@ module Basecamp
13
13
  # @return [void]
14
14
  def reposition(group_id:, position:)
15
15
  with_operation(service: "todolistgroups", operation: "reposition", is_mutation: true, resource_id: group_id) do
16
- http_put("/todolists/#{group_id}/position.json", body: compact_params(position: position))
16
+ http_put("/todolists/groups/#{group_id}/position.json", body: compact_params(position: position))
17
17
  nil
18
18
  end
19
19
  end
20
20
 
21
21
  # List groups in a todolist
22
22
  # @param todolist_id [Integer] todolist id ID
23
- # @return [Enumerator<Hash>] paginated results
24
- def list(todolist_id:)
23
+ # @param page [Integer, nil] Page number for paginating through results. Defaults to 1. A positive value selects exactly that page, not a starting offset; see SPEC section 8.
24
+ # @param max_items [Integer, nil] cap on items yielded across pages; nil or non-positive means no cap
25
+ # @return [ListEnumerator<Hash>] lazily paginated results (#meta carries pagination metadata)
26
+ def list(todolist_id:, page: nil, max_items: nil)
25
27
  wrap_paginated(service: "todolistgroups", operation: "list", is_mutation: false, resource_id: todolist_id) do
26
- paginate("/todolists/#{todolist_id}/groups.json", operation: "ListTodolistGroups")
28
+ params = compact_query_params(page: page)
29
+ paginate("/todolists/#{todolist_id}/groups.json", params: params, operation: "ListTodolistGroups", max_items: max_items)
27
30
  end
28
31
  end
29
32
 
@@ -16,13 +16,13 @@ module Basecamp
16
16
  end
17
17
  end
18
18
 
19
- # Update an existing todolist or todolist group
19
+ # Replace a todolist (or todolist group) with a new complete representation.
20
20
  # @param id [Integer] id ID
21
- # @param name [String, nil] Name (required for both Todolist and TodolistGroup)
22
- # @param description [String, nil] Description (Todolist only, ignored for groups)
21
+ # @param name [String] Name (required for a to-do list and for a group alike) - presence-validated server-side, so omitting it is a 422, not a preserve
22
+ # @param description [String, nil] Description (rich text HTML) - writable for a todolist group as well as a todolist, and omitting it clears it either way
23
23
  # @return [Hash] response data
24
- def update(id:, name: nil, description: nil)
25
- with_operation(service: "todolists", operation: "update", is_mutation: true, resource_id: id) do
24
+ def replace(id:, name:, description: nil)
25
+ with_operation(service: "todolists", operation: "replace", is_mutation: true, resource_id: id) do
26
26
  http_put("/todolists/#{id}", body: compact_params(name: name, description: description)).json
27
27
  end
28
28
  end
@@ -41,11 +41,13 @@ module Basecamp
41
41
  # List todolists in a todoset
42
42
  # @param todoset_id [Integer] todoset id ID
43
43
  # @param status [String, nil] active|archived|trashed
44
- # @return [Enumerator<Hash>] paginated results
45
- def list(todoset_id:, status: nil)
44
+ # @param page [Integer, nil] Page number for paginating through results. Defaults to 1. A positive value selects exactly that page, not a starting offset; see SPEC section 8.
45
+ # @param max_items [Integer, nil] cap on items yielded across pages; nil or non-positive means no cap
46
+ # @return [ListEnumerator<Hash>] lazily paginated results (#meta carries pagination metadata)
47
+ def list(todoset_id:, status: nil, page: nil, max_items: nil)
46
48
  wrap_paginated(service: "todolists", operation: "list", is_mutation: false, resource_id: todoset_id) do
47
- params = compact_query_params(status: status)
48
- paginate("/todosets/#{todoset_id}/todolists.json", params: params, operation: "ListTodolists")
49
+ params = compact_query_params(status: status, page: page)
50
+ paginate("/todosets/#{todoset_id}/todolists.json", params: params, operation: "ListTodolists", max_items: max_items)
49
51
  end
50
52
  end
51
53
 
@@ -28,11 +28,13 @@ module Basecamp
28
28
  # @param todolist_id [Integer] todolist id ID
29
29
  # @param status [String, nil] active|archived|trashed
30
30
  # @param completed [Boolean, nil] completed
31
- # @return [Enumerator<Hash>] paginated results
32
- def list(todolist_id:, status: nil, completed: nil)
31
+ # @param page [Integer, nil] Page number for paginating through results. Defaults to 1. A positive value selects exactly that page, not a starting offset; see SPEC section 8.
32
+ # @param max_items [Integer, nil] cap on items yielded across pages; nil or non-positive means no cap
33
+ # @return [ListEnumerator<Hash>] lazily paginated results (#meta carries pagination metadata)
34
+ def list(todolist_id:, status: nil, completed: nil, page: nil, max_items: nil)
33
35
  wrap_paginated(service: "todos", operation: "list", is_mutation: false, resource_id: todolist_id) do
34
- params = compact_query_params(status: status, completed: completed)
35
- paginate("/todolists/#{todolist_id}/todos.json", params: params, operation: "ListTodos")
36
+ params = compact_query_params(status: status, completed: completed, page: page)
37
+ paginate("/todolists/#{todolist_id}/todos.json", params: params, operation: "ListTodos", max_items: max_items)
36
38
  end
37
39
  end
38
40
 
@@ -77,16 +79,6 @@ module Basecamp
77
79
  end
78
80
  end
79
81
 
80
- # Trash a todo (returns 204 No Content)
81
- # @param todo_id [Integer] todo id ID
82
- # @return [void]
83
- def trash(todo_id:)
84
- with_operation(service: "todos", operation: "trash", is_mutation: true, resource_id: todo_id) do
85
- http_delete("/todos/#{todo_id}")
86
- nil
87
- end
88
- end
89
-
90
82
  # Mark a todo as complete
91
83
  # @param todo_id [Integer] todo id ID
92
84
  # @return [void]
@@ -29,19 +29,41 @@ module Basecamp
29
29
 
30
30
  # List versions of an upload
31
31
  # @param upload_id [Integer] upload id ID
32
- # @return [Enumerator<Hash>] paginated results
33
- def list_versions(upload_id:)
32
+ # @param max_items [Integer, nil] cap on items yielded across pages; nil or non-positive means no cap
33
+ # @return [ListEnumerator<Hash>] lazily paginated results (#meta carries pagination metadata)
34
+ def list_versions(upload_id:, max_items: nil)
34
35
  wrap_paginated(service: "uploads", operation: "list_versions", is_mutation: false, resource_id: upload_id) do
35
- paginate("/uploads/#{upload_id}/versions.json", operation: "ListUploadVersions")
36
+ paginate("/uploads/#{upload_id}/versions.json", operation: "ListUploadVersions", max_items: max_items)
37
+ end
38
+ end
39
+
40
+ # Replace an upload's file with a new version
41
+ # @param upload_id [Integer] upload id ID
42
+ # @param attachable_sgid [String] attachable sgid
43
+ # @param base_name [String, nil] Omit to keep the uploaded file's own name. Sending "" also keeps it.
44
+ # @param description [String, nil] Presence-aware: omit to carry the previous version's description forward,
45
+ # send "" to clear it, send a value to set it.
46
+ # @param notify [String, nil] Who to notify: "default", "everyone", or "custom" (the people in subscriptions).
47
+ #
48
+ # Omit both this and subscriptions to notify nobody. A subscriptions array sent
49
+ # without notify is read as "custom".
50
+ # @param subscriptions [Array, nil] People to notify about the replacement and subscribe to the upload.
51
+ # @return [Hash] response data
52
+ def create_version(upload_id:, attachable_sgid:, base_name: nil, description: nil, notify: nil, subscriptions: nil)
53
+ with_operation(service: "uploads", operation: "create_version", is_mutation: true, resource_id: upload_id) do
54
+ http_post("/uploads/#{upload_id}/versions.json", body: compact_params(attachable_sgid: attachable_sgid, base_name: base_name, description: description, notify: notify, subscriptions: subscriptions)).json
36
55
  end
37
56
  end
38
57
 
39
58
  # List uploads in a vault
40
59
  # @param vault_id [Integer] vault id ID
41
- # @return [Enumerator<Hash>] paginated results
42
- def list(vault_id:)
60
+ # @param page [Integer, nil] Page number for paginating through results. Defaults to 1. A positive value selects exactly that page, not a starting offset; see SPEC section 8.
61
+ # @param max_items [Integer, nil] cap on items yielded across pages; nil or non-positive means no cap
62
+ # @return [ListEnumerator<Hash>] lazily paginated results (#meta carries pagination metadata)
63
+ def list(vault_id:, page: nil, max_items: nil)
43
64
  wrap_paginated(service: "uploads", operation: "list", is_mutation: false, resource_id: vault_id) do
44
- paginate("/vaults/#{vault_id}/uploads.json", operation: "ListUploads")
65
+ params = compact_query_params(page: page)
66
+ paginate("/vaults/#{vault_id}/uploads.json", params: params, operation: "ListUploads", max_items: max_items)
45
67
  end
46
68
  end
47
69
 
@@ -28,10 +28,13 @@ module Basecamp
28
28
 
29
29
  # List vaults (subfolders) in a vault
30
30
  # @param vault_id [Integer] vault id ID
31
- # @return [Enumerator<Hash>] paginated results
32
- def list(vault_id:)
31
+ # @param page [Integer, nil] Page number for paginating through results. Defaults to 1. A positive value selects exactly that page, not a starting offset; see SPEC section 8.
32
+ # @param max_items [Integer, nil] cap on items yielded across pages; nil or non-positive means no cap
33
+ # @return [ListEnumerator<Hash>] lazily paginated results (#meta carries pagination metadata)
34
+ def list(vault_id:, page: nil, max_items: nil)
33
35
  wrap_paginated(service: "vaults", operation: "list", is_mutation: false, resource_id: vault_id) do
34
- paginate("/vaults/#{vault_id}/vaults.json", operation: "ListVaults")
36
+ params = compact_query_params(page: page)
37
+ paginate("/vaults/#{vault_id}/vaults.json", params: params, operation: "ListVaults", max_items: max_items)
35
38
  end
36
39
  end
37
40
 
@@ -9,10 +9,11 @@ module Basecamp
9
9
 
10
10
  # List all webhooks for a project
11
11
  # @param bucket_id [Integer] bucket id ID
12
- # @return [Enumerator<Hash>] paginated results
13
- def list(bucket_id:)
12
+ # @param max_items [Integer, nil] cap on items yielded across pages; nil or non-positive means no cap
13
+ # @return [ListEnumerator<Hash>] lazily paginated results (#meta carries pagination metadata)
14
+ def list(bucket_id:, max_items: nil)
14
15
  wrap_paginated(service: "webhooks", operation: "list", is_mutation: false, project_id: bucket_id) do
15
- paginate("/buckets/#{bucket_id}/webhooks.json", operation: "ListWebhooks")
16
+ paginate("/buckets/#{bucket_id}/webhooks.json", operation: "ListWebhooks", max_items: max_items)
16
17
  end
17
18
  end
18
19