diff --git a/behavior-model.json b/behavior-model.json index 5fe249fe0..0b537c17e 100644 --- a/behavior-model.json +++ b/behavior-model.json @@ -781,6 +781,126 @@ ] } }, + "GetEverythingBoosts": { + "readonly": true, + "pagination": { + "style": "link", + "maxPageSize": 50 + }, + "retry": { + "max": 3, + "base_delay_ms": 1000, + "backoff": "exponential", + "retry_on": [ + 429, + 503 + ] + } + }, + "GetEverythingCheckins": { + "readonly": true, + "pagination": { + "style": "link", + "maxPageSize": 50 + }, + "retry": { + "max": 3, + "base_delay_ms": 1000, + "backoff": "exponential", + "retry_on": [ + 429, + 503 + ] + } + }, + "GetEverythingComments": { + "readonly": true, + "pagination": { + "style": "link", + "maxPageSize": 50 + }, + "retry": { + "max": 3, + "base_delay_ms": 1000, + "backoff": "exponential", + "retry_on": [ + 429, + 503 + ] + } + }, + "GetEverythingFiles": { + "readonly": true, + "pagination": { + "style": "link", + "maxPageSize": 50 + }, + "retry": { + "max": 3, + "base_delay_ms": 1000, + "backoff": "exponential", + "retry_on": [ + 429, + 503 + ] + } + }, + "GetEverythingForwards": { + "readonly": true, + "pagination": { + "style": "link", + "maxPageSize": 50 + }, + "retry": { + "max": 3, + "base_delay_ms": 1000, + "backoff": "exponential", + "retry_on": [ + 429, + 503 + ] + } + }, + "GetEverythingMessages": { + "readonly": true, + "pagination": { + "style": "link", + "maxPageSize": 50 + }, + "retry": { + "max": 3, + "base_delay_ms": 1000, + "backoff": "exponential", + "retry_on": [ + 429, + 503 + ] + } + }, + "GetEverythingOverdueCards": { + "readonly": true, + "retry": { + "max": 3, + "base_delay_ms": 1000, + "backoff": "exponential", + "retry_on": [ + 429, + 503 + ] + } + }, + "GetEverythingOverdueTodos": { + "readonly": true, + "retry": { + "max": 3, + "base_delay_ms": 1000, + "backoff": "exponential", + "retry_on": [ + 429, + 503 + ] + } + }, "GetForward": { "readonly": true, "retry": { diff --git a/conformance/runner/go/main.go b/conformance/runner/go/main.go index 9d7399ec7..100bfcc0f 100644 --- a/conformance/runner/go/main.go +++ b/conformance/runner/go/main.go @@ -682,6 +682,38 @@ func executeOperation(ctx context.Context, account *basecamp.AccountClient, tc T } return operationResult{err: nil} + case "GetEverythingMessages": + _, err := account.Everything().Messages(ctx, 0) + return operationResult{err: err} + + case "GetEverythingComments": + _, err := account.Everything().Comments(ctx, 0) + return operationResult{err: err} + + case "GetEverythingCheckins": + _, err := account.Everything().Checkins(ctx, 0) + return operationResult{err: err} + + case "GetEverythingForwards": + _, err := account.Everything().Forwards(ctx, 0) + return operationResult{err: err} + + case "GetEverythingBoosts": + _, err := account.Everything().Boosts(ctx, 0) + return operationResult{err: err} + + case "GetEverythingFiles": + _, err := account.Everything().Files(ctx, 0, nil) + return operationResult{err: err} + + case "GetEverythingOverdueTodos": + _, err := account.Everything().OverdueTodos(ctx) + return operationResult{err: err} + + case "GetEverythingOverdueCards": + _, err := account.Everything().OverdueCards(ctx) + return operationResult{err: err} + default: return operationResult{ err: fmt.Errorf("unknown operation: %s", tc.Operation), diff --git a/conformance/runner/python/runner.py b/conformance/runner/python/runner.py index fd101b162..a9f028f35 100644 --- a/conformance/runner/python/runner.py +++ b/conformance/runner/python/runner.py @@ -150,6 +150,22 @@ def __call__(self, operation: str, *, path_params: dict, query_params: dict, bod return self._account.tools.enable(tool_id=path_params["toolId"]) case "UploadsDownload": return self._account.uploads.download(upload_id=path_params["uploadId"]) + case "GetEverythingMessages": + return self._account.everything.get_everything_messages() + case "GetEverythingComments": + return self._account.everything.get_everything_comments() + case "GetEverythingCheckins": + return self._account.everything.get_everything_checkins() + case "GetEverythingForwards": + return self._account.everything.get_everything_forwards() + case "GetEverythingBoosts": + return self._account.everything.get_everything_boosts() + case "GetEverythingFiles": + return self._account.everything.get_everything_files() + case "GetEverythingOverdueTodos": + return self._account.everything.get_everything_overdue_todos() + case "GetEverythingOverdueCards": + return self._account.everything.get_everything_overdue_cards() case _: raise ValueError(f"Unknown operation: {operation}") diff --git a/conformance/runner/ruby/runner.rb b/conformance/runner/ruby/runner.rb index 1010b6b96..669a6b29f 100644 --- a/conformance/runner/ruby/runner.rb +++ b/conformance/runner/ruby/runner.rb @@ -165,6 +165,22 @@ def call(operation, path_params: {}, query_params: {}, body: nil, path: "") todo_id: path_params["todoId"], **todo_write_kwargs(body) ) + when "GetEverythingMessages" + @account.everything.get_everything_messages.to_a + when "GetEverythingComments" + @account.everything.get_everything_comments.to_a + when "GetEverythingCheckins" + @account.everything.get_everything_checkins.to_a + when "GetEverythingForwards" + @account.everything.get_everything_forwards.to_a + when "GetEverythingBoosts" + @account.everything.get_everything_boosts.to_a + when "GetEverythingFiles" + @account.everything.get_everything_files.to_a + when "GetEverythingOverdueTodos" + @account.everything.get_everything_overdue_todos + when "GetEverythingOverdueCards" + @account.everything.get_everything_overdue_cards else raise "Unknown operation: #{operation}" end diff --git a/conformance/runner/typescript/live-dispatch.ts b/conformance/runner/typescript/live-dispatch.ts index 46c993066..93f422dc1 100644 --- a/conformance/runner/typescript/live-dispatch.ts +++ b/conformance/runner/typescript/live-dispatch.ts @@ -81,6 +81,66 @@ export const LIVE_OPERATIONS: Record = { }, }, + ListRecordings: { + fixtures: [], + call: async (ctx) => { + // Backs the type=Door external-links canary; validates the door shape + // (external url + service + description) against the Recording schema. + const result = await ctx.client.recordings.list("Door"); + return { resolvedIds: {}, result }; + }, + }, + + GetProgressReport: { + fixtures: [], + call: async (ctx) => { + const result = await ctx.client.reports.progress(); + return { resolvedIds: {}, result }; + }, + }, + + GetBubbleUps: { + fixtures: [], + call: async (ctx) => { + const result = await ctx.client.myNotifications.bubbleUps(); + return { resolvedIds: {}, result }; + }, + }, + + // Everything aggregates — flat family (one canary per group). + GetEverythingMessages: { + fixtures: [], + call: async (ctx) => ({ resolvedIds: {}, result: await ctx.client.everything.everythingMessages() }), + }, + GetEverythingComments: { + fixtures: [], + call: async (ctx) => ({ resolvedIds: {}, result: await ctx.client.everything.everythingComments() }), + }, + GetEverythingCheckins: { + fixtures: [], + call: async (ctx) => ({ resolvedIds: {}, result: await ctx.client.everything.everythingCheckins() }), + }, + GetEverythingForwards: { + fixtures: [], + call: async (ctx) => ({ resolvedIds: {}, result: await ctx.client.everything.everythingForwards() }), + }, + GetEverythingBoosts: { + fixtures: [], + call: async (ctx) => ({ resolvedIds: {}, result: await ctx.client.everything.everythingBoosts() }), + }, + GetEverythingFiles: { + fixtures: [], + call: async (ctx) => ({ resolvedIds: {}, result: await ctx.client.everything.everythingFiles() }), + }, + GetEverythingOverdueTodos: { + fixtures: [], + call: async (ctx) => ({ resolvedIds: {}, result: await ctx.client.everything.everythingOverdueTodos() }), + }, + GetEverythingOverdueCards: { + fixtures: [], + call: async (ctx) => ({ resolvedIds: {}, result: await ctx.client.everything.everythingOverdueCards() }), + }, + GetMyProfile: { fixtures: [], call: async (ctx) => { diff --git a/conformance/runner/typescript/runner.test.ts b/conformance/runner/typescript/runner.test.ts index a743f4e01..2b54c1766 100644 --- a/conformance/runner/typescript/runner.test.ts +++ b/conformance/runner/typescript/runner.test.ts @@ -260,6 +260,38 @@ async function executeOperation( await client.reports.personProgress(Number(params.personId)); break; + case "GetEverythingMessages": + await client.everything.everythingMessages(); + break; + + case "GetEverythingComments": + await client.everything.everythingComments(); + break; + + case "GetEverythingCheckins": + await client.everything.everythingCheckins(); + break; + + case "GetEverythingForwards": + await client.everything.everythingForwards(); + break; + + case "GetEverythingBoosts": + await client.everything.everythingBoosts(); + break; + + case "GetEverythingFiles": + await client.everything.everythingFiles(); + break; + + case "GetEverythingOverdueTodos": + await client.everything.everythingOverdueTodos(); + break; + + case "GetEverythingOverdueCards": + await client.everything.everythingOverdueCards(); + break; + case "GetTool": await client.tools.get(Number(params.toolId)); break; diff --git a/conformance/tests/live-my-surface.json b/conformance/tests/live-my-surface.json index 88696f0ab..0918d0df2 100644 --- a/conformance/tests/live-my-surface.json +++ b/conformance/tests/live-my-surface.json @@ -6,10 +6,17 @@ "operation": "GetProgressReport", "method": "GET", "path": "/reports/progress.json", - "tags": ["live", "read-only"], + "tags": [ + "live", + "read-only" + ], "liveAssertions": [ - { "type": "liveCallSucceeds" }, - { "type": "liveSchemaValidate" } + { + "type": "liveCallSucceeds" + }, + { + "type": "liveSchemaValidate" + } ] }, { @@ -19,10 +26,17 @@ "operation": "GetBubbleUps", "method": "GET", "path": "/my/readings/bubble_ups.json", - "tags": ["live", "read-only"], + "tags": [ + "live", + "read-only" + ], "liveAssertions": [ - { "type": "liveCallSucceeds" }, - { "type": "liveSchemaValidate" } + { + "type": "liveCallSucceeds" + }, + { + "type": "liveSchemaValidate" + } ] }, { @@ -32,10 +46,17 @@ "operation": "ListProjects", "method": "GET", "path": "/projects.json", - "tags": ["live", "read-only"], + "tags": [ + "live", + "read-only" + ], "liveAssertions": [ - { "type": "liveCallSucceeds" }, - { "type": "liveSchemaValidate" } + { + "type": "liveCallSucceeds" + }, + { + "type": "liveSchemaValidate" + } ] }, { @@ -45,29 +66,55 @@ "operation": "GetProject", "method": "GET", "path": "/projects/${PROJECT_ID}.json", - "fixtureIds": { "PROJECT_ID": "PROJECT_ID" }, - "pathParams": { "projectId": "${PROJECT_ID}" }, - "tags": ["live", "read-only"], + "fixtureIds": { + "PROJECT_ID": "PROJECT_ID" + }, + "pathParams": { + "projectId": "${PROJECT_ID}" + }, + "tags": [ + "live", + "read-only" + ], "liveAssertions": [ - { "type": "liveCallSucceeds" }, - { "type": "liveSchemaValidate" }, + { + "type": "liveCallSucceeds" + }, + { + "type": "liveSchemaValidate" + }, { "type": "liveResponseFieldsRequired", - "fields": ["id", "status", "created_at", "updated_at", "name", "url", "app_url"] + "fields": [ + "id", + "status", + "created_at", + "updated_at", + "name", + "url", + "app_url" + ] } ] }, { "mode": "live", "name": "GetMyAssignments decodes priorities + non_priorities", - "description": "Verifies MyAssignment.bucket JSON-key compat — the field is preserved despite route renames.", + "description": "Verifies MyAssignment.bucket JSON-key compat \u2014 the field is preserved despite route renames.", "operation": "GetMyAssignments", "method": "GET", "path": "/my/assignments.json", - "tags": ["live", "read-only"], + "tags": [ + "live", + "read-only" + ], "liveAssertions": [ - { "type": "liveCallSucceeds" }, - { "type": "liveSchemaValidate" } + { + "type": "liveCallSucceeds" + }, + { + "type": "liveSchemaValidate" + } ] }, { @@ -76,10 +123,17 @@ "operation": "GetMyCompletedAssignments", "method": "GET", "path": "/my/assignments/completed.json", - "tags": ["live", "read-only"], + "tags": [ + "live", + "read-only" + ], "liveAssertions": [ - { "type": "liveCallSucceeds" }, - { "type": "liveSchemaValidate" } + { + "type": "liveCallSucceeds" + }, + { + "type": "liveSchemaValidate" + } ] }, { @@ -88,23 +142,38 @@ "operation": "GetMyDueAssignments", "method": "GET", "path": "/my/assignments/due.json", - "tags": ["live", "read-only"], + "tags": [ + "live", + "read-only" + ], "liveAssertions": [ - { "type": "liveCallSucceeds" }, - { "type": "liveSchemaValidate" } + { + "type": "liveCallSucceeds" + }, + { + "type": "liveSchemaValidate" + } ] }, { "mode": "live", "name": "GetMyNotifications decodes unreads/reads/memories/bubble_ups", - "description": "Asserts the call succeeds and the raw body validates against the GetMyNotifications response schema on production BC5 (bubble_ups / scheduled_bubble_ups / memories are all schema-checked as arrays). It does not assert memories is empty — BC5 ships memories as an always-empty placeholder by documented contract (doc/api/sections/my_notifications.md, bc3 #11628), noted here as context; see spec/api-gaps/memories-emptied-regression.md.", + "description": "Asserts the call succeeds and the raw body validates against the GetMyNotifications response schema on production BC5 (bubble_ups / scheduled_bubble_ups / memories are all schema-checked as arrays). It does not assert memories is empty \u2014 BC5 ships memories as an always-empty placeholder by documented contract (doc/api/sections/my_notifications.md, bc3 #11628), noted here as context; see spec/api-gaps/memories-emptied-regression.md.", "operation": "GetMyNotifications", "method": "GET", "path": "/my/readings.json", - "tags": ["live", "read-only", "bc5-additive"], + "tags": [ + "live", + "read-only", + "bc5-additive" + ], "liveAssertions": [ - { "type": "liveCallSucceeds" }, - { "type": "liveSchemaValidate" } + { + "type": "liveCallSucceeds" + }, + { + "type": "liveSchemaValidate" + } ] }, { @@ -114,29 +183,52 @@ "operation": "GetMyProfile", "method": "GET", "path": "/my/profile.json", - "tags": ["live", "read-only", "bc5-additive"], + "tags": [ + "live", + "read-only", + "bc5-additive" + ], "liveAssertions": [ - { "type": "liveCallSucceeds" }, - { "type": "liveSchemaValidate" }, + { + "type": "liveCallSucceeds" + }, + { + "type": "liveSchemaValidate" + }, { "type": "liveResponseFieldsRequired", - "fields": ["id", "name"] + "fields": [ + "id", + "name" + ] } ] }, { "mode": "live", "name": "GetTodoset decodes the BC5 count + url additions", - "description": "Verifies todos_count, completed_loose_todos_count, todos_url, app_todos_url decode (when present — eligibility-gated like other BC5 additions).", + "description": "Verifies todos_count, completed_loose_todos_count, todos_url, app_todos_url decode (when present \u2014 eligibility-gated like other BC5 additions).", "operation": "GetTodoset", "method": "GET", "path": "/todosets/${TODOSET_ID}.json", - "fixtureIds": { "TODOSET_ID": "TODOSET_ID" }, - "pathParams": { "todosetId": "${TODOSET_ID}" }, - "tags": ["live", "read-only", "bc5-additive"], + "fixtureIds": { + "TODOSET_ID": "TODOSET_ID" + }, + "pathParams": { + "todosetId": "${TODOSET_ID}" + }, + "tags": [ + "live", + "read-only", + "bc5-additive" + ], "liveAssertions": [ - { "type": "liveCallSucceeds" }, - { "type": "liveSchemaValidate" } + { + "type": "liveCallSucceeds" + }, + { + "type": "liveSchemaValidate" + } ] }, { @@ -146,12 +238,23 @@ "operation": "ListTodolists", "method": "GET", "path": "/todosets/${TODOSET_ID}/todolists.json", - "fixtureIds": { "TODOSET_ID": "TODOSET_ID" }, - "pathParams": { "todosetId": "${TODOSET_ID}" }, - "tags": ["live", "read-only"], + "fixtureIds": { + "TODOSET_ID": "TODOSET_ID" + }, + "pathParams": { + "todosetId": "${TODOSET_ID}" + }, + "tags": [ + "live", + "read-only" + ], "liveAssertions": [ - { "type": "liveCallSucceeds" }, - { "type": "liveSchemaValidate" } + { + "type": "liveCallSucceeds" + }, + { + "type": "liveSchemaValidate" + } ] }, { @@ -161,26 +264,244 @@ "operation": "ListTodos", "method": "GET", "path": "/todolists/${TODOLIST_ID}/todos.json", - "fixtureIds": { "TODOLIST_ID": "TODOLIST_ID" }, - "pathParams": { "todolistId": "${TODOLIST_ID}" }, - "tags": ["live", "read-only", "bc5-additive"], + "fixtureIds": { + "TODOLIST_ID": "TODOLIST_ID" + }, + "pathParams": { + "todolistId": "${TODOLIST_ID}" + }, + "tags": [ + "live", + "read-only", + "bc5-additive" + ], "liveAssertions": [ - { "type": "liveCallSucceeds" }, - { "type": "liveSchemaValidate" } + { + "type": "liveCallSucceeds" + }, + { + "type": "liveSchemaValidate" + } ] }, { "mode": "live", "name": "Search accepts the BC5 filter params (type_names[]/since) and decodes results", - "description": "ACCEPTANCE/DECODING coverage for the absorbed search filter surface (spec/api-gaps/search-filter-additions.md, bc3 #12361). Drives search with the new type_names[] array + since params through the SDK's bracketed wire encoding (the dispatch call in live-dispatch.ts is authoritative). New params are silently ignored on BC4, accepted on BC5. HONESTY NOTE: the available live assertions prove only 2xx + SearchResult schema validity — NOT that BC5 actually filters by the params. No result-membership assertion exists in the vocabulary (conformance/schema.json), so this canary is decoding/acceptance coverage, not proof the filter is respected.", + "description": "ACCEPTANCE/DECODING coverage for the absorbed search filter surface (spec/api-gaps/search-filter-additions.md, bc3 #12361). Drives search with the new type_names[] array + since params through the SDK's bracketed wire encoding (the dispatch call in live-dispatch.ts is authoritative). New params are silently ignored on BC4, accepted on BC5. HONESTY NOTE: the available live assertions prove only 2xx + SearchResult schema validity \u2014 NOT that BC5 actually filters by the params. No result-membership assertion exists in the vocabulary (conformance/schema.json), so this canary is decoding/acceptance coverage, not proof the filter is respected.", "operation": "Search", "method": "GET", "path": "/search.json", - "queryParams": { "q": "the", "type_names[]": ["Message"], "since": "last_30_days" }, - "tags": ["live", "read-only", "bc5-additive"], + "queryParams": { + "q": "the", + "type_names[]": [ + "Message" + ], + "since": "last_30_days" + }, + "tags": [ + "live", + "read-only", + "bc5-additive" + ], + "liveAssertions": [ + { + "type": "liveCallSucceeds" + }, + { + "type": "liveSchemaValidate" + } + ] + }, + { + "mode": "live", + "name": "ListRecordings type=Door decodes the external-link (door) shape", + "description": "DECODING coverage for the absorbed external-links/doors list surface (spec/api-gaps/external-links-doors.md, bc3 #12375). Drives the type=Door recordings query and validates each recording against the Recording schema, including the door-specific url/service/description/position fields. Live-dormant: validates statically until credentials are provisioned. Create/image/composite remain residual gaps (see the entry).", + "operation": "ListRecordings", + "method": "GET", + "path": "/projects/recordings.json", + "queryParams": { + "type": "Door" + }, + "tags": [ + "live", + "read-only", + "bc5-additive" + ], + "liveAssertions": [ + { + "type": "liveCallSucceeds" + }, + { + "type": "liveSchemaValidate" + } + ] + }, + { + "mode": "live", + "name": "GetEverythingMessages validates against the everything-aggregates schema", + "description": "DECODING coverage for the everything-aggregates flat family (spec/api-gaps/everything-aggregates.md, bc3 #11627). Exercises every message across all projects and validates each item against its schema. Live-dormant: validates statically until credentials are provisioned.", + "operation": "GetEverythingMessages", + "method": "GET", + "path": "/messages.json", + "tags": [ + "live", + "read-only", + "bc5-additive" + ], + "liveAssertions": [ + { + "type": "liveCallSucceeds" + }, + { + "type": "liveSchemaValidate" + } + ] + }, + { + "mode": "live", + "name": "GetEverythingComments validates against the everything-aggregates schema", + "description": "DECODING coverage for the everything-aggregates flat family (spec/api-gaps/everything-aggregates.md, bc3 #11627). Exercises every comment across all projects and validates each item against its schema. Live-dormant: validates statically until credentials are provisioned.", + "operation": "GetEverythingComments", + "method": "GET", + "path": "/comments.json", + "tags": [ + "live", + "read-only", + "bc5-additive" + ], + "liveAssertions": [ + { + "type": "liveCallSucceeds" + }, + { + "type": "liveSchemaValidate" + } + ] + }, + { + "mode": "live", + "name": "GetEverythingCheckins validates against the everything-aggregates schema", + "description": "DECODING coverage for the everything-aggregates flat family (spec/api-gaps/everything-aggregates.md, bc3 #11627). Exercises every check-in answer across all projects and validates each item against its schema. Live-dormant: validates statically until credentials are provisioned.", + "operation": "GetEverythingCheckins", + "method": "GET", + "path": "/checkins.json", + "tags": [ + "live", + "read-only", + "bc5-additive" + ], + "liveAssertions": [ + { + "type": "liveCallSucceeds" + }, + { + "type": "liveSchemaValidate" + } + ] + }, + { + "mode": "live", + "name": "GetEverythingForwards validates against the everything-aggregates schema", + "description": "DECODING coverage for the everything-aggregates flat family (spec/api-gaps/everything-aggregates.md, bc3 #11627). Exercises every inbox forward across all projects and validates each item against its schema. Live-dormant: validates statically until credentials are provisioned.", + "operation": "GetEverythingForwards", + "method": "GET", + "path": "/forwards.json", + "tags": [ + "live", + "read-only", + "bc5-additive" + ], "liveAssertions": [ - { "type": "liveCallSucceeds" }, - { "type": "liveSchemaValidate" } + { + "type": "liveCallSucceeds" + }, + { + "type": "liveSchemaValidate" + } + ] + }, + { + "mode": "live", + "name": "GetEverythingBoosts validates against the everything-aggregates schema", + "description": "DECODING coverage for the everything-aggregates flat family (spec/api-gaps/everything-aggregates.md, bc3 #11627). Exercises every boost across all projects and validates each item against its schema. Live-dormant: validates statically until credentials are provisioned.", + "operation": "GetEverythingBoosts", + "method": "GET", + "path": "/boosts.json", + "tags": [ + "live", + "read-only", + "bc5-additive" + ], + "liveAssertions": [ + { + "type": "liveCallSucceeds" + }, + { + "type": "liveSchemaValidate" + } + ] + }, + { + "mode": "live", + "name": "GetEverythingFiles validates against the everything-aggregates schema", + "description": "DECODING coverage for the everything-aggregates flat family (spec/api-gaps/everything-aggregates.md, bc3 #11627). Exercises the heterogeneous files feed (uploads/documents/attachments) and validates each item against its schema. Live-dormant: validates statically until credentials are provisioned.", + "operation": "GetEverythingFiles", + "method": "GET", + "path": "/files.json", + "tags": [ + "live", + "read-only", + "bc5-additive" + ], + "liveAssertions": [ + { + "type": "liveCallSucceeds" + }, + { + "type": "liveSchemaValidate" + } + ] + }, + { + "mode": "live", + "name": "GetEverythingOverdueTodos validates against the everything-aggregates schema", + "description": "DECODING coverage for the everything-aggregates flat family (spec/api-gaps/everything-aggregates.md, bc3 #11627). Exercises the unpaginated oldest-first overdue to-dos and validates each item against its schema. Live-dormant: validates statically until credentials are provisioned.", + "operation": "GetEverythingOverdueTodos", + "method": "GET", + "path": "/todos/overdue.json", + "tags": [ + "live", + "read-only", + "bc5-additive" + ], + "liveAssertions": [ + { + "type": "liveCallSucceeds" + }, + { + "type": "liveSchemaValidate" + } + ] + }, + { + "mode": "live", + "name": "GetEverythingOverdueCards validates against the everything-aggregates schema", + "description": "DECODING coverage for the everything-aggregates flat family (spec/api-gaps/everything-aggregates.md, bc3 #11627). Exercises the unpaginated oldest-first overdue cards and validates each item against its schema. Live-dormant: validates statically until credentials are provisioned.", + "operation": "GetEverythingOverdueCards", + "method": "GET", + "path": "/cards/overdue.json", + "tags": [ + "live", + "read-only", + "bc5-additive" + ], + "liveAssertions": [ + { + "type": "liveCallSucceeds" + }, + { + "type": "liveSchemaValidate" + } ] } ] diff --git a/conformance/tests/paths.json b/conformance/tests/paths.json index 6af0703cd..b03c033dc 100644 --- a/conformance/tests/paths.json +++ b/conformance/tests/paths.json @@ -5,15 +5,28 @@ "operation": "GetProjectTimeline", "method": "GET", "path": "/projects/{projectId}/timeline.json", - "pathParams": {"projectId": 12345}, + "pathParams": { + "projectId": 12345 + }, "mockResponses": [ - {"status": 200, "body": []} + { + "status": 200, + "body": [] + } ], "assertions": [ - {"type": "requestPath", "expected": "/999/projects/12345/timeline.json"}, - {"type": "noError"} + { + "type": "requestPath", + "expected": "/999/projects/12345/timeline.json" + }, + { + "type": "noError" + } ], - "tags": ["path", "timeline"] + "tags": [ + "path", + "timeline" + ] }, { "name": "Progress report uses /reports/ path", @@ -22,13 +35,24 @@ "method": "GET", "path": "/reports/progress.json", "mockResponses": [ - {"status": 200, "body": []} + { + "status": 200, + "body": [] + } ], "assertions": [ - {"type": "requestPath", "expected": "/999/reports/progress.json"}, - {"type": "noError"} + { + "type": "requestPath", + "expected": "/999/reports/progress.json" + }, + { + "type": "noError" + } ], - "tags": ["path", "reports"] + "tags": [ + "path", + "reports" + ] }, { "name": "Person progress uses /reports/users/ path with .json", @@ -36,15 +60,34 @@ "operation": "GetPersonProgress", "method": "GET", "path": "/reports/users/progress/{personId}.json", - "pathParams": {"personId": 45678}, + "pathParams": { + "personId": 45678 + }, "mockResponses": [ - {"status": 200, "body": {"person": {"id": 45678, "name": "Test"}, "events": []}} + { + "status": 200, + "body": { + "person": { + "id": 45678, + "name": "Test" + }, + "events": [] + } + } ], "assertions": [ - {"type": "requestPath", "expected": "/999/reports/users/progress/45678.json"}, - {"type": "noError"} + { + "type": "requestPath", + "expected": "/999/reports/users/progress/45678.json" + }, + { + "type": "noError" + } ], - "tags": ["path", "reports"] + "tags": [ + "path", + "reports" + ] }, { "name": "Project timesheet uses project-scoped /projects/{projectId}/timesheet.json path", @@ -52,15 +95,28 @@ "operation": "GetProjectTimesheet", "method": "GET", "path": "/projects/{projectId}/timesheet.json", - "pathParams": {"projectId": 12345}, + "pathParams": { + "projectId": 12345 + }, "mockResponses": [ - {"status": 200, "body": []} + { + "status": 200, + "body": [] + } ], "assertions": [ - {"type": "requestPath", "expected": "/999/projects/12345/timesheet.json"}, - {"type": "noError"} + { + "type": "requestPath", + "expected": "/999/projects/12345/timesheet.json" + }, + { + "type": "noError" + } ], - "tags": ["path", "timesheets"] + "tags": [ + "path", + "timesheets" + ] }, { "name": "Timesheet entry get uses /timesheet_entries/{entryId} path", @@ -68,15 +124,57 @@ "operation": "GetTimesheetEntry", "method": "GET", "path": "/timesheet_entries/{entryId}", - "pathParams": {"entryId": 999}, + "pathParams": { + "entryId": 999 + }, "mockResponses": [ - {"status": 200, "body": {"id": 999, "status": "active", "visible_to_clients": false, "created_at": "2024-01-01T00:00:00Z", "updated_at": "2024-01-01T00:00:00Z", "title": "Timesheet entry", "inherits_status": true, "type": "Timesheet::Entry", "url": "https://3.basecampapi.com/999/buckets/456/timesheet_entries/999.json", "app_url": "https://3.basecamp.com/999/buckets/456/timesheet_entries/999", "parent": {"id": 2, "title": "Timesheet", "type": "Timesheet", "url": "https://3.basecampapi.com/999/buckets/456/timesheets/2.json", "app_url": "https://3.basecamp.com/999/buckets/456/timesheets/2"}, "bucket": {"id": 456, "name": "Test", "type": "Project"}, "creator": {"id": 1, "name": "Test User"}, "date": "2024-01-01", "hours": "4.0"}} + { + "status": 200, + "body": { + "id": 999, + "status": "active", + "visible_to_clients": false, + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "title": "Timesheet entry", + "inherits_status": true, + "type": "Timesheet::Entry", + "url": "https://3.basecampapi.com/999/buckets/456/timesheet_entries/999.json", + "app_url": "https://3.basecamp.com/999/buckets/456/timesheet_entries/999", + "parent": { + "id": 2, + "title": "Timesheet", + "type": "Timesheet", + "url": "https://3.basecampapi.com/999/buckets/456/timesheets/2.json", + "app_url": "https://3.basecamp.com/999/buckets/456/timesheets/2" + }, + "bucket": { + "id": 456, + "name": "Test", + "type": "Project" + }, + "creator": { + "id": 1, + "name": "Test User" + }, + "date": "2024-01-01", + "hours": "4.0" + } + } ], "assertions": [ - {"type": "requestPath", "expected": "/999/timesheet_entries/999"}, - {"type": "noError"} + { + "type": "requestPath", + "expected": "/999/timesheet_entries/999" + }, + { + "type": "noError" + } ], - "tags": ["path", "timesheets"] + "tags": [ + "path", + "timesheets" + ] }, { "name": "Timesheet entry update uses /timesheet_entries/{entryId} path", @@ -84,16 +182,60 @@ "operation": "UpdateTimesheetEntry", "method": "PUT", "path": "/timesheet_entries/{entryId}", - "pathParams": {"entryId": 999}, - "requestBody": {"hours": "4.0"}, + "pathParams": { + "entryId": 999 + }, + "requestBody": { + "hours": "4.0" + }, "mockResponses": [ - {"status": 200, "body": {"id": 999, "status": "active", "visible_to_clients": false, "created_at": "2024-01-01T00:00:00Z", "updated_at": "2024-01-01T00:00:00Z", "title": "Timesheet entry", "inherits_status": true, "type": "Timesheet::Entry", "url": "https://3.basecampapi.com/999/buckets/456/timesheet_entries/999.json", "app_url": "https://3.basecamp.com/999/buckets/456/timesheet_entries/999", "parent": {"id": 2, "title": "Timesheet", "type": "Timesheet", "url": "https://3.basecampapi.com/999/buckets/456/timesheets/2.json", "app_url": "https://3.basecamp.com/999/buckets/456/timesheets/2"}, "bucket": {"id": 456, "name": "Test", "type": "Project"}, "creator": {"id": 1, "name": "Test User"}, "date": "2024-01-01", "hours": "4.0"}} + { + "status": 200, + "body": { + "id": 999, + "status": "active", + "visible_to_clients": false, + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "title": "Timesheet entry", + "inherits_status": true, + "type": "Timesheet::Entry", + "url": "https://3.basecampapi.com/999/buckets/456/timesheet_entries/999.json", + "app_url": "https://3.basecamp.com/999/buckets/456/timesheet_entries/999", + "parent": { + "id": 2, + "title": "Timesheet", + "type": "Timesheet", + "url": "https://3.basecampapi.com/999/buckets/456/timesheets/2.json", + "app_url": "https://3.basecamp.com/999/buckets/456/timesheets/2" + }, + "bucket": { + "id": 456, + "name": "Test", + "type": "Project" + }, + "creator": { + "id": 1, + "name": "Test User" + }, + "date": "2024-01-01", + "hours": "4.0" + } + } ], "assertions": [ - {"type": "requestPath", "expected": "/999/timesheet_entries/999"}, - {"type": "noError"} + { + "type": "requestPath", + "expected": "/999/timesheet_entries/999" + }, + { + "type": "noError" + } ], - "tags": ["path", "timesheets"] + "tags": [ + "path", + "timesheets" + ] }, { "name": "List webhooks uses bucket-scoped /buckets/{bucketId}/webhooks.json path", @@ -101,15 +243,28 @@ "operation": "ListWebhooks", "method": "GET", "path": "/buckets/{bucketId}/webhooks.json", - "pathParams": {"bucketId": 456}, + "pathParams": { + "bucketId": 456 + }, "mockResponses": [ - {"status": 200, "body": []} + { + "status": 200, + "body": [] + } ], "assertions": [ - {"type": "requestPath", "expected": "/999/buckets/456/webhooks.json"}, - {"type": "noError"} + { + "type": "requestPath", + "expected": "/999/buckets/456/webhooks.json" + }, + { + "type": "noError" + } ], - "tags": ["path", "webhooks"] + "tags": [ + "path", + "webhooks" + ] }, { "name": "Create webhook uses bucket-scoped /buckets/{bucketId}/webhooks.json path", @@ -117,16 +272,45 @@ "operation": "CreateWebhook", "method": "POST", "path": "/buckets/{bucketId}/webhooks.json", - "pathParams": {"bucketId": 456}, - "requestBody": {"payload_url": "https://example.com/hook", "types": ["Todo"]}, + "pathParams": { + "bucketId": 456 + }, + "requestBody": { + "payload_url": "https://example.com/hook", + "types": [ + "Todo" + ] + }, "mockResponses": [ - {"status": 201, "body": {"id": 1, "payload_url": "https://example.com/hook", "created_at": "2024-01-01T00:00:00Z", "updated_at": "2024-01-01T00:00:00Z", "url": "https://3.basecampapi.com/999/buckets/456/webhooks/1.json", "app_url": "https://3.basecamp.com/999/buckets/456/webhooks/1", "active": true, "types": ["Todo"]}} + { + "status": 201, + "body": { + "id": 1, + "payload_url": "https://example.com/hook", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "url": "https://3.basecampapi.com/999/buckets/456/webhooks/1.json", + "app_url": "https://3.basecamp.com/999/buckets/456/webhooks/1", + "active": true, + "types": [ + "Todo" + ] + } + } ], "assertions": [ - {"type": "requestPath", "expected": "/999/buckets/456/webhooks.json"}, - {"type": "noError"} + { + "type": "requestPath", + "expected": "/999/buckets/456/webhooks.json" + }, + { + "type": "noError" + } ], - "tags": ["path", "webhooks"] + "tags": [ + "path", + "webhooks" + ] }, { "name": "GetTool uses flat /dock/tools/{toolId} path (no bucketId)", @@ -134,15 +318,44 @@ "operation": "GetTool", "method": "GET", "path": "/dock/tools/{toolId}", - "pathParams": {"toolId": 789}, + "pathParams": { + "toolId": 789 + }, "mockResponses": [ - {"status": 200, "body": {"id": 789, "name": "todoset", "title": "To-dos", "status": "active", "enabled": true, "position": 1, "created_at": "2024-01-01T00:00:00Z", "updated_at": "2024-01-01T00:00:00Z", "url": "https://3.basecampapi.com/999/buckets/456/todosets/789.json", "app_url": "https://3.basecamp.com/999/buckets/456/todosets/789", "bucket": {"id": 456, "name": "Test", "type": "Project"}}} + { + "status": 200, + "body": { + "id": 789, + "name": "todoset", + "title": "To-dos", + "status": "active", + "enabled": true, + "position": 1, + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "url": "https://3.basecampapi.com/999/buckets/456/todosets/789.json", + "app_url": "https://3.basecamp.com/999/buckets/456/todosets/789", + "bucket": { + "id": 456, + "name": "Test", + "type": "Project" + } + } + } ], "assertions": [ - {"type": "requestPath", "expected": "/999/dock/tools/789"}, - {"type": "noError"} + { + "type": "requestPath", + "expected": "/999/dock/tools/789" + }, + { + "type": "noError" + } ], - "tags": ["path", "tools"] + "tags": [ + "path", + "tools" + ] }, { "name": "CreateTool uses bucket-scoped /buckets/{bucketId}/dock/tools.json path", @@ -150,16 +363,48 @@ "operation": "CreateTool", "method": "POST", "path": "/buckets/{bucketId}/dock/tools.json", - "pathParams": {"bucketId": 456}, - "requestBody": {"tool_type": "Message::Board", "title": "Message Board (Copy)"}, + "pathParams": { + "bucketId": 456 + }, + "requestBody": { + "tool_type": "Message::Board", + "title": "Message Board (Copy)" + }, "mockResponses": [ - {"status": 201, "body": {"id": 800, "name": "message_board", "title": "Message Board (Copy)", "status": "active", "enabled": true, "position": 5, "created_at": "2024-01-01T00:00:00Z", "updated_at": "2024-01-01T00:00:00Z", "url": "https://3.basecampapi.com/999/buckets/456/message_boards/800.json", "app_url": "https://3.basecamp.com/999/buckets/456/message_boards/800", "bucket": {"id": 456, "name": "Test", "type": "Project"}}} + { + "status": 201, + "body": { + "id": 800, + "name": "message_board", + "title": "Message Board (Copy)", + "status": "active", + "enabled": true, + "position": 5, + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "url": "https://3.basecampapi.com/999/buckets/456/message_boards/800.json", + "app_url": "https://3.basecamp.com/999/buckets/456/message_boards/800", + "bucket": { + "id": 456, + "name": "Test", + "type": "Project" + } + } + } ], "assertions": [ - {"type": "requestPath", "expected": "/999/buckets/456/dock/tools.json"}, - {"type": "noError"} + { + "type": "requestPath", + "expected": "/999/buckets/456/dock/tools.json" + }, + { + "type": "noError" + } ], - "tags": ["path", "tools"] + "tags": [ + "path", + "tools" + ] }, { "name": "CreateTool accepts omitted title on bucket-scoped path", @@ -167,16 +412,47 @@ "operation": "CreateTool", "method": "POST", "path": "/buckets/{bucketId}/dock/tools.json", - "pathParams": {"bucketId": 456}, - "requestBody": {"tool_type": "Message::Board"}, + "pathParams": { + "bucketId": 456 + }, + "requestBody": { + "tool_type": "Message::Board" + }, "mockResponses": [ - {"status": 201, "body": {"id": 801, "name": "message_board", "title": "Message Board", "status": "active", "enabled": true, "position": 6, "created_at": "2024-01-01T00:00:00Z", "updated_at": "2024-01-01T00:00:00Z", "url": "https://3.basecampapi.com/999/buckets/456/message_boards/801.json", "app_url": "https://3.basecamp.com/999/buckets/456/message_boards/801", "bucket": {"id": 456, "name": "Test", "type": "Project"}}} + { + "status": 201, + "body": { + "id": 801, + "name": "message_board", + "title": "Message Board", + "status": "active", + "enabled": true, + "position": 6, + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "url": "https://3.basecampapi.com/999/buckets/456/message_boards/801.json", + "app_url": "https://3.basecamp.com/999/buckets/456/message_boards/801", + "bucket": { + "id": 456, + "name": "Test", + "type": "Project" + } + } + } ], "assertions": [ - {"type": "requestPath", "expected": "/999/buckets/456/dock/tools.json"}, - {"type": "noError"} + { + "type": "requestPath", + "expected": "/999/buckets/456/dock/tools.json" + }, + { + "type": "noError" + } ], - "tags": ["path", "tools"] + "tags": [ + "path", + "tools" + ] }, { "name": "EnableTool uses flat /recordings/{toolId}/position.json path (no bucketId)", @@ -184,15 +460,28 @@ "operation": "EnableTool", "method": "POST", "path": "/recordings/{toolId}/position.json", - "pathParams": {"toolId": 789}, + "pathParams": { + "toolId": 789 + }, "mockResponses": [ - {"status": 201, "body": {}} + { + "status": 201, + "body": {} + } ], "assertions": [ - {"type": "requestPath", "expected": "/999/recordings/789/position.json"}, - {"type": "noError"} + { + "type": "requestPath", + "expected": "/999/recordings/789/position.json" + }, + { + "type": "noError" + } ], - "tags": ["path", "tools"] + "tags": [ + "path", + "tools" + ] }, { "name": "Mixed-case host and explicit default port stay on the mocked origin", @@ -200,32 +489,305 @@ "operation": "GetTool", "method": "GET", "path": "/dock/tools/{toolId}", - "pathParams": {"toolId": 789}, - "configOverrides": {"baseUrl": "https://3.BasecampAPI.com:443"}, + "pathParams": { + "toolId": 789 + }, + "configOverrides": { + "baseUrl": "https://3.BasecampAPI.com:443" + }, "mockResponses": [ - {"status": 200, "body": {"id": 789, "name": "todoset", "title": "To-dos", "status": "active", "enabled": true, "position": 1, "created_at": "2024-01-01T00:00:00Z", "updated_at": "2024-01-01T00:00:00Z", "url": "https://3.basecampapi.com/999/buckets/456/todosets/789.json", "app_url": "https://3.basecamp.com/999/buckets/456/todosets/789", "bucket": {"id": 456, "name": "Test", "type": "Project"}}} + { + "status": 200, + "body": { + "id": 789, + "name": "todoset", + "title": "To-dos", + "status": "active", + "enabled": true, + "position": 1, + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "url": "https://3.basecampapi.com/999/buckets/456/todosets/789.json", + "app_url": "https://3.basecamp.com/999/buckets/456/todosets/789", + "bucket": { + "id": 456, + "name": "Test", + "type": "Project" + } + } + } ], "assertions": [ - {"type": "requestCount", "expected": 1}, - {"type": "noError"} + { + "type": "requestCount", + "expected": 1 + }, + { + "type": "noError" + } ], - "tags": ["path", "origin", "normalization"] + "tags": [ + "path", + "origin", + "normalization" + ] }, { "name": "Bracketed IPv6 loopback origin stays on the mocked origin", - "description": "Regression for runner origin interception with IPv6: a configOverrides.baseUrl of http://[::1]:3000 (the SPEC §16 IPv6 example; loopback is exempt from HTTPS enforcement) must keep its brackets through origin normalization so the request is served by the mock queue. Runners that dial baseUrl directly rather than intercepting by origin may skip.", + "description": "Regression for runner origin interception with IPv6: a configOverrides.baseUrl of http://[::1]:3000 (the SPEC \u00a716 IPv6 example; loopback is exempt from HTTPS enforcement) must keep its brackets through origin normalization so the request is served by the mock queue. Runners that dial baseUrl directly rather than intercepting by origin may skip.", "operation": "GetTool", "method": "GET", "path": "/dock/tools/{toolId}", - "pathParams": {"toolId": 789}, - "configOverrides": {"baseUrl": "http://[::1]:3000"}, + "pathParams": { + "toolId": 789 + }, + "configOverrides": { + "baseUrl": "http://[::1]:3000" + }, "mockResponses": [ - {"status": 200, "body": {"id": 789, "name": "todoset", "title": "To-dos", "status": "active", "enabled": true, "position": 1, "created_at": "2024-01-01T00:00:00Z", "updated_at": "2024-01-01T00:00:00Z", "url": "https://3.basecampapi.com/999/buckets/456/todosets/789.json", "app_url": "https://3.basecamp.com/999/buckets/456/todosets/789", "bucket": {"id": 456, "name": "Test", "type": "Project"}}} + { + "status": 200, + "body": { + "id": 789, + "name": "todoset", + "title": "To-dos", + "status": "active", + "enabled": true, + "position": 1, + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "url": "https://3.basecampapi.com/999/buckets/456/todosets/789.json", + "app_url": "https://3.basecamp.com/999/buckets/456/todosets/789", + "bucket": { + "id": 456, + "name": "Test", + "type": "Project" + } + } + } ], "assertions": [ - {"type": "requestCount", "expected": 1}, - {"type": "noError"} + { + "type": "requestCount", + "expected": 1 + }, + { + "type": "noError" + } ], - "tags": ["path", "origin", "normalization", "ipv6"] + "tags": [ + "path", + "origin", + "normalization", + "ipv6" + ] + }, + { + "name": "GetEverythingMessages constructs /messages.json", + "description": "Verifies GetEverythingMessages constructs the flat top-level URL path /messages.json (the /everything/ segment is the Rails controller namespace, not part of the URL).", + "operation": "GetEverythingMessages", + "method": "GET", + "path": "/messages.json", + "mockResponses": [ + { + "status": 200, + "body": [] + } + ], + "assertions": [ + { + "type": "requestPath", + "expected": "/999/messages.json" + }, + { + "type": "noError" + } + ], + "tags": [ + "path", + "everything" + ] + }, + { + "name": "GetEverythingComments constructs /comments.json", + "description": "Verifies GetEverythingComments constructs the flat top-level URL path /comments.json (the /everything/ segment is the Rails controller namespace, not part of the URL).", + "operation": "GetEverythingComments", + "method": "GET", + "path": "/comments.json", + "mockResponses": [ + { + "status": 200, + "body": [] + } + ], + "assertions": [ + { + "type": "requestPath", + "expected": "/999/comments.json" + }, + { + "type": "noError" + } + ], + "tags": [ + "path", + "everything" + ] + }, + { + "name": "GetEverythingCheckins constructs /checkins.json", + "description": "Verifies GetEverythingCheckins constructs the flat top-level URL path /checkins.json (the /everything/ segment is the Rails controller namespace, not part of the URL).", + "operation": "GetEverythingCheckins", + "method": "GET", + "path": "/checkins.json", + "mockResponses": [ + { + "status": 200, + "body": [] + } + ], + "assertions": [ + { + "type": "requestPath", + "expected": "/999/checkins.json" + }, + { + "type": "noError" + } + ], + "tags": [ + "path", + "everything" + ] + }, + { + "name": "GetEverythingForwards constructs /forwards.json", + "description": "Verifies GetEverythingForwards constructs the flat top-level URL path /forwards.json (the /everything/ segment is the Rails controller namespace, not part of the URL).", + "operation": "GetEverythingForwards", + "method": "GET", + "path": "/forwards.json", + "mockResponses": [ + { + "status": 200, + "body": [] + } + ], + "assertions": [ + { + "type": "requestPath", + "expected": "/999/forwards.json" + }, + { + "type": "noError" + } + ], + "tags": [ + "path", + "everything" + ] + }, + { + "name": "GetEverythingBoosts constructs /boosts.json", + "description": "Verifies GetEverythingBoosts constructs the flat top-level URL path /boosts.json (the /everything/ segment is the Rails controller namespace, not part of the URL).", + "operation": "GetEverythingBoosts", + "method": "GET", + "path": "/boosts.json", + "mockResponses": [ + { + "status": 200, + "body": [] + } + ], + "assertions": [ + { + "type": "requestPath", + "expected": "/999/boosts.json" + }, + { + "type": "noError" + } + ], + "tags": [ + "path", + "everything" + ] + }, + { + "name": "GetEverythingFiles constructs /files.json", + "description": "Verifies GetEverythingFiles constructs the flat top-level URL path /files.json (the /everything/ segment is the Rails controller namespace, not part of the URL).", + "operation": "GetEverythingFiles", + "method": "GET", + "path": "/files.json", + "mockResponses": [ + { + "status": 200, + "body": [] + } + ], + "assertions": [ + { + "type": "requestPath", + "expected": "/999/files.json" + }, + { + "type": "noError" + } + ], + "tags": [ + "path", + "everything" + ] + }, + { + "name": "GetEverythingOverdueTodos constructs /todos/overdue.json", + "description": "Verifies GetEverythingOverdueTodos constructs the flat top-level URL path /todos/overdue.json (the /everything/ segment is the Rails controller namespace, not part of the URL).", + "operation": "GetEverythingOverdueTodos", + "method": "GET", + "path": "/todos/overdue.json", + "mockResponses": [ + { + "status": 200, + "body": [] + } + ], + "assertions": [ + { + "type": "requestPath", + "expected": "/999/todos/overdue.json" + }, + { + "type": "noError" + } + ], + "tags": [ + "path", + "everything" + ] + }, + { + "name": "GetEverythingOverdueCards constructs /cards/overdue.json", + "description": "Verifies GetEverythingOverdueCards constructs the flat top-level URL path /cards/overdue.json (the /everything/ segment is the Rails controller namespace, not part of the URL).", + "operation": "GetEverythingOverdueCards", + "method": "GET", + "path": "/cards/overdue.json", + "mockResponses": [ + { + "status": 200, + "body": [] + } + ], + "assertions": [ + { + "type": "requestPath", + "expected": "/999/cards/overdue.json" + }, + { + "type": "noError" + } + ], + "tags": [ + "path", + "everything" + ] } ] diff --git a/go/pkg/basecamp/client.go b/go/pkg/basecamp/client.go index c358c487d..b45409964 100644 --- a/go/pkg/basecamp/client.go +++ b/go/pkg/basecamp/client.go @@ -108,6 +108,7 @@ type AccountClient struct { gauges *GaugesService myAssignments *MyAssignmentsService myNotifications *MyNotificationsService + everything *EverythingService } // Response wraps an API response. @@ -1336,6 +1337,16 @@ func (ac *AccountClient) Timeline() *TimelineService { return ac.timeline } +// Everything returns the EverythingService for account-wide aggregate listings. +func (ac *AccountClient) Everything() *EverythingService { + ac.mu.Lock() + defer ac.mu.Unlock() + if ac.everything == nil { + ac.everything = NewEverythingService(ac) + } + return ac.everything +} + // Reports returns the ReportsService for reports operations. func (ac *AccountClient) Reports() *ReportsService { ac.mu.Lock() diff --git a/go/pkg/basecamp/everything.go b/go/pkg/basecamp/everything.go new file mode 100644 index 000000000..89068644e --- /dev/null +++ b/go/pkg/basecamp/everything.go @@ -0,0 +1,465 @@ +package basecamp + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "time" + + "github.com/basecamp/basecamp-sdk/go/pkg/generated" +) + +// EverythingService exposes the account-wide "everything" aggregate listings: +// recency-ordered, paginated roots (messages, comments, checkins, forwards, +// files, boosts) and the unpaginated oldest-first overdue todo/card lists. Each +// item embeds its bucket for project context. See +// spec/api-gaps/everything-aggregates.md. +type EverythingService struct { + client *AccountClient +} + +// NewEverythingService creates a new EverythingService. +func NewEverythingService(client *AccountClient) *EverythingService { + return &EverythingService{client: client} +} + +// RecordingsPage is a page-followed list of recordings with pagination metadata. +type RecordingsPage struct { + Recordings []Recording + Meta ListMeta +} + +// EverythingBoostsPage is a page-followed list of boosts with pagination metadata. +type EverythingBoostsPage struct { + Boosts []Boost + Meta ListMeta +} + +// EverythingFilesPage is a page-followed list of files with pagination metadata. +type EverythingFilesPage struct { + Files []EverythingFile + Meta ListMeta +} + +// EverythingFilesOptions specifies optional filters for the files feed. +type EverythingFilesOptions struct { + // Kind filters by file kind: "all" (default), "images", "pdfs", + // "documents", or "videos". + Kind string + // PeopleIDs restricts the list to files created by the given people. + PeopleIDs []int64 +} + +// EverythingFile is a single item in the /files.json feed: an optional-field +// superset over three wire variants — a full Upload recording, a Basecamp +// Document recording, and a rich-text attachment wrapped in a recording envelope +// (distinguished by AttachableSGID and blob metadata). Only the fields of the +// variant an instance represents are populated. +type EverythingFile struct { + ID int64 `json:"id,omitempty"` + Status string `json:"status,omitempty"` + // VisibleToClients/CreatedAt/UpdatedAt/InheritsStatus are pointers so an + // absent field (the variant this instance is not) stays nil and round-trips + // as omitted rather than a fabricated zero timestamp or a dropped false. + VisibleToClients *bool `json:"visible_to_clients,omitempty"` + CreatedAt *time.Time `json:"created_at,omitempty"` + UpdatedAt *time.Time `json:"updated_at,omitempty"` + Title string `json:"title,omitempty"` + InheritsStatus *bool `json:"inherits_status,omitempty"` + // Type is "Upload", "Document", or "Attachment". + Type string `json:"type,omitempty"` + URL string `json:"url,omitempty"` + AppURL string `json:"app_url,omitempty"` + BookmarkURL string `json:"bookmark_url,omitempty"` + SubscriptionURL string `json:"subscription_url,omitempty"` + CommentsCount int32 `json:"comments_count,omitempty"` + CommentsURL string `json:"comments_url,omitempty"` + BoostsCount int32 `json:"boosts_count,omitempty"` + BoostsURL string `json:"boosts_url,omitempty"` + Position int32 `json:"position,omitempty"` + Parent *Parent `json:"parent,omitempty"` + Bucket *Bucket `json:"bucket,omitempty"` + Creator *Person `json:"creator,omitempty"` + // AttachableSGID is present on the rich-text attachment variant only. + AttachableSGID string `json:"attachable_sgid,omitempty"` + // Blob/file metadata (uploads and attachments). + ContentType string `json:"content_type,omitempty"` + ByteSize int64 `json:"byte_size,omitempty"` + Filename string `json:"filename,omitempty"` + DownloadURL string `json:"download_url,omitempty"` + AppDownloadURL string `json:"app_download_url,omitempty"` + // Width and Height are null for non-image blobs and may be float-spelled + // (1024.0) on the wire; narrowed to *int32 here (nil when absent/null). + Width *int32 `json:"width,omitempty"` + Height *int32 `json:"height,omitempty"` + Description string `json:"description,omitempty"` + // DescriptionAttachments carries the rich-text companion array for the + // upload/document Description (absent on the attachment variant). + DescriptionAttachments *[]RichTextAttachment `json:"description_attachments,omitempty"` +} + +// UnmarshalJSON routes decoding through the generated EverythingFile so the +// public struct handles the float-encoded integers (1024.0) and null dimensions +// the BC3 API emits for width/height. Mirrors TimelineAttachment.UnmarshalJSON. +func (f *EverythingFile) UnmarshalJSON(data []byte) error { + var gf generated.EverythingFile + if err := json.Unmarshal(data, &gf); err != nil { + return err + } + *f = everythingFileFromGenerated(gf) + return nil +} + +// everythingFileFromGenerated converts a generated EverythingFile (the +// optional-field superset) to the clean public type. Width and Height are +// optional/nullable *types.FlexInt in the generated type; a nil pointer leaves +// the public *int32 nil, and a present value is narrowed to int32. +func everythingFileFromGenerated(gf generated.EverythingFile) EverythingFile { + f := EverythingFile{ + Status: gf.Status, + VisibleToClients: gf.VisibleToClients, + CreatedAt: gf.CreatedAt, + UpdatedAt: gf.UpdatedAt, + Title: gf.Title, + InheritsStatus: gf.InheritsStatus, + Type: gf.Type, + URL: gf.Url, + AppURL: gf.AppUrl, + BookmarkURL: gf.BookmarkUrl, + SubscriptionURL: gf.SubscriptionUrl, + CommentsCount: gf.CommentsCount, + CommentsURL: gf.CommentsUrl, + BoostsCount: gf.BoostsCount, + BoostsURL: gf.BoostsUrl, + Position: gf.Position, + AttachableSGID: gf.AttachableSgid, + ContentType: gf.ContentType, + ByteSize: gf.ByteSize, + Filename: gf.Filename, + DownloadURL: gf.DownloadUrl, + AppDownloadURL: gf.AppDownloadUrl, + Description: gf.Description, + } + if gf.Id != nil { + f.ID = *gf.Id + } + if gf.Width != nil { + w := int32(*gf.Width) + f.Width = &w + } + if gf.Height != nil { + h := int32(*gf.Height) + f.Height = &h + } + if gf.Parent.Id != 0 || gf.Parent.Title != "" { + f.Parent = &Parent{ID: gf.Parent.Id, Title: gf.Parent.Title, Type: gf.Parent.Type, URL: gf.Parent.Url, AppURL: gf.Parent.AppUrl} + } + if gf.Bucket.Id != 0 || gf.Bucket.Name != "" { + f.Bucket = &Bucket{ID: gf.Bucket.Id, Name: gf.Bucket.Name, Type: gf.Bucket.Type} + } + if gf.Creator.Id != 0 || gf.Creator.Name != "" { + creator := personFromGenerated(gf.Creator) + f.Creator = &creator + } + f.DescriptionAttachments = richTextAttachmentsPtrFromGenerated(gf.DescriptionAttachments) + return f +} + +// Messages returns every message across all accessible projects, newest-first. +// Pass a positive page to return only that page; page 0 follows the Link header +// across all pages. +func (s *EverythingService) Messages(ctx context.Context, page int32) (result *RecordingsPage, err error) { + op := OperationInfo{Service: "Everything", Operation: "Messages", ResourceType: "recording", IsMutation: false} + ctx, done, err := s.begin(ctx, op) + if err != nil { + return nil, err + } + defer done(&err) + var params *generated.GetEverythingMessagesParams + if page > 0 { + params = &generated.GetEverythingMessagesParams{Page: page} + } + resp, err := s.client.parent.gen.GetEverythingMessagesWithResponse(ctx, s.client.accountID, params) + if err != nil { + return nil, err + } + return s.finishRecordingsPage(ctx, resp.HTTPResponse, resp.Body, resp.JSON200, page) +} + +// Comments returns every comment across all accessible projects, newest-first. +func (s *EverythingService) Comments(ctx context.Context, page int32) (result *RecordingsPage, err error) { + op := OperationInfo{Service: "Everything", Operation: "Comments", ResourceType: "recording", IsMutation: false} + ctx, done, err := s.begin(ctx, op) + if err != nil { + return nil, err + } + defer done(&err) + var params *generated.GetEverythingCommentsParams + if page > 0 { + params = &generated.GetEverythingCommentsParams{Page: page} + } + resp, err := s.client.parent.gen.GetEverythingCommentsWithResponse(ctx, s.client.accountID, params) + if err != nil { + return nil, err + } + return s.finishRecordingsPage(ctx, resp.HTTPResponse, resp.Body, resp.JSON200, page) +} + +// Checkins returns every automatic check-in answer across all accessible +// projects, newest-first. +func (s *EverythingService) Checkins(ctx context.Context, page int32) (result *RecordingsPage, err error) { + op := OperationInfo{Service: "Everything", Operation: "Checkins", ResourceType: "recording", IsMutation: false} + ctx, done, err := s.begin(ctx, op) + if err != nil { + return nil, err + } + defer done(&err) + var params *generated.GetEverythingCheckinsParams + if page > 0 { + params = &generated.GetEverythingCheckinsParams{Page: page} + } + resp, err := s.client.parent.gen.GetEverythingCheckinsWithResponse(ctx, s.client.accountID, params) + if err != nil { + return nil, err + } + return s.finishRecordingsPage(ctx, resp.HTTPResponse, resp.Body, resp.JSON200, page) +} + +// Forwards returns every inbox forward across all accessible projects, +// newest-first. +func (s *EverythingService) Forwards(ctx context.Context, page int32) (result *RecordingsPage, err error) { + op := OperationInfo{Service: "Everything", Operation: "Forwards", ResourceType: "recording", IsMutation: false} + ctx, done, err := s.begin(ctx, op) + if err != nil { + return nil, err + } + defer done(&err) + var params *generated.GetEverythingForwardsParams + if page > 0 { + params = &generated.GetEverythingForwardsParams{Page: page} + } + resp, err := s.client.parent.gen.GetEverythingForwardsWithResponse(ctx, s.client.accountID, params) + if err != nil { + return nil, err + } + return s.finishRecordingsPage(ctx, resp.HTTPResponse, resp.Body, resp.JSON200, page) +} + +// finishRecordingsPage decodes the first page of a []Recording aggregate root +// and follows the Link header (unless a positive page was requested). +func (s *EverythingService) finishRecordingsPage(ctx context.Context, httpResp *http.Response, body []byte, json200 *[]generated.Recording, page int32) (*RecordingsPage, error) { + if err := checkResponse(httpResp, body); err != nil { + return nil, err + } + var recordings []Recording + if json200 != nil { + for _, gr := range *json200 { + recordings = append(recordings, recordingFromGenerated(gr)) + } + } + totalCount := parseTotalCount(httpResp) + if page > 0 { + return &RecordingsPage{Recordings: recordings, Meta: ListMeta{TotalCount: totalCount}}, nil + } + rawMore, truncated, err := s.client.parent.followPagination(ctx, httpResp, len(recordings), 0) + if err != nil { + return nil, err + } + for _, raw := range rawMore { + var gr generated.Recording + if err := json.Unmarshal(raw, &gr); err != nil { + return nil, fmt.Errorf("failed to parse recording: %w", err) + } + recordings = append(recordings, recordingFromGenerated(gr)) + } + return &RecordingsPage{Recordings: recordings, Meta: ListMeta{TotalCount: totalCount, Truncated: truncated}}, nil +} + +// Boosts returns every boost across all accessible projects, newest-first. Each +// boost carries its booster and the recording it boosts. +func (s *EverythingService) Boosts(ctx context.Context, page int32) (result *EverythingBoostsPage, err error) { + op := OperationInfo{Service: "Everything", Operation: "Boosts", ResourceType: "boost", IsMutation: false} + ctx, done, err := s.begin(ctx, op) + if err != nil { + return nil, err + } + defer done(&err) + + var params *generated.GetEverythingBoostsParams + if page > 0 { + params = &generated.GetEverythingBoostsParams{Page: page} + } + resp, err := s.client.parent.gen.GetEverythingBoostsWithResponse(ctx, s.client.accountID, params) + if err != nil { + return nil, err + } + if err = checkResponse(resp.HTTPResponse, resp.Body); err != nil { + return nil, err + } + + var boosts []Boost + if resp.JSON200 != nil { + for _, gb := range *resp.JSON200 { + boosts = append(boosts, boostFromGenerated(gb)) + } + } + totalCount := parseTotalCount(resp.HTTPResponse) + if page > 0 { + return &EverythingBoostsPage{Boosts: boosts, Meta: ListMeta{TotalCount: totalCount}}, nil + } + rawMore, truncated, err := s.client.parent.followPagination(ctx, resp.HTTPResponse, len(boosts), 0) + if err != nil { + return nil, err + } + for _, raw := range rawMore { + var gb generated.Boost + if err := json.Unmarshal(raw, &gb); err != nil { + return nil, fmt.Errorf("failed to parse boost: %w", err) + } + boosts = append(boosts, boostFromGenerated(gb)) + } + return &EverythingBoostsPage{Boosts: boosts, Meta: ListMeta{TotalCount: totalCount, Truncated: truncated}}, nil +} + +// Files returns every file recording across all accessible projects, +// newest-first. The feed is heterogeneous (uploads, documents, attachments); +// each element is an optional-field superset. Optional filters narrow by kind +// and creator. +func (s *EverythingService) Files(ctx context.Context, page int32, opts *EverythingFilesOptions) (result *EverythingFilesPage, err error) { + op := OperationInfo{Service: "Everything", Operation: "Files", ResourceType: "file", IsMutation: false} + ctx, done, err := s.begin(ctx, op) + if err != nil { + return nil, err + } + defer done(&err) + + var params *generated.GetEverythingFilesParams + if opts != nil || page > 0 { + params = &generated.GetEverythingFilesParams{} + if page > 0 { + params.Page = page + } + if opts != nil { + if opts.Kind != "" { + params.Kind = opts.Kind + } + if len(opts.PeopleIDs) > 0 { + ids := append([]int64(nil), opts.PeopleIDs...) + params.PeopleIds = &ids + } + } + } + + resp, err := s.client.parent.gen.GetEverythingFilesWithResponse(ctx, s.client.accountID, params) + if err != nil { + return nil, err + } + if err = checkResponse(resp.HTTPResponse, resp.Body); err != nil { + return nil, err + } + + files, err := decodeEverythingFiles(resp.Body) + if err != nil { + return nil, err + } + totalCount := parseTotalCount(resp.HTTPResponse) + if page > 0 { + return &EverythingFilesPage{Files: files, Meta: ListMeta{TotalCount: totalCount}}, nil + } + rawMore, truncated, err := s.client.parent.followPagination(ctx, resp.HTTPResponse, len(files), 0) + if err != nil { + return nil, err + } + for _, raw := range rawMore { + var gf generated.EverythingFile + if err := json.Unmarshal(raw, &gf); err != nil { + return nil, fmt.Errorf("failed to parse file: %w", err) + } + files = append(files, everythingFileFromGenerated(gf)) + } + return &EverythingFilesPage{Files: files, Meta: ListMeta{TotalCount: totalCount, Truncated: truncated}}, nil +} + +// decodeEverythingFiles decodes a files page (a bare JSON array) into clean +// EverythingFile values via the superset's UnmarshalJSON (which routes through +// the generated FlexInt-aware type). +func decodeEverythingFiles(body []byte) ([]EverythingFile, error) { + var files []EverythingFile + if err := json.Unmarshal(body, &files); err != nil { + return nil, fmt.Errorf("failed to parse files: %w", err) + } + return files, nil +} + +// OverdueTodos returns every overdue to-do across all accessible projects — a +// complete, oldest-due-date-first array (unpaginated, no Link-following). +func (s *EverythingService) OverdueTodos(ctx context.Context) (result []Todo, err error) { + op := OperationInfo{Service: "Everything", Operation: "OverdueTodos", ResourceType: "todo", IsMutation: false} + ctx, done, err := s.begin(ctx, op) + if err != nil { + return nil, err + } + defer done(&err) + + resp, err := s.client.parent.gen.GetEverythingOverdueTodosWithResponse(ctx, s.client.accountID) + if err != nil { + return nil, err + } + if err = checkResponse(resp.HTTPResponse, resp.Body); err != nil { + return nil, err + } + var todos []Todo + if resp.JSON200 != nil { + for _, gt := range *resp.JSON200 { + todos = append(todos, todoFromGenerated(gt)) + } + } + return todos, nil +} + +// OverdueCards returns every overdue card across all accessible projects — a +// complete, oldest-due-date-first array (unpaginated, no Link-following). +func (s *EverythingService) OverdueCards(ctx context.Context) (result []Card, err error) { + op := OperationInfo{Service: "Everything", Operation: "OverdueCards", ResourceType: "card", IsMutation: false} + ctx, done, err := s.begin(ctx, op) + if err != nil { + return nil, err + } + defer done(&err) + + resp, err := s.client.parent.gen.GetEverythingOverdueCardsWithResponse(ctx, s.client.accountID) + if err != nil { + return nil, err + } + if err = checkResponse(resp.HTTPResponse, resp.Body); err != nil { + return nil, err + } + var cards []Card + if resp.JSON200 != nil { + for _, gc := range *resp.JSON200 { + cards = append(cards, cardFromGenerated(gc)) + } + } + return cards, nil +} + +// begin runs the gating + start/end hook lifecycle shared by the everything +// methods and returns the (possibly gated) context plus a deferred finisher. +func (s *EverythingService) begin(ctx context.Context, op OperationInfo) (context.Context, func(*error), error) { + if gater, ok := s.client.parent.hooks.(GatingHooks); ok { + var err error + if ctx, err = gater.OnOperationGate(ctx, op); err != nil { + return ctx, func(*error) {}, err + } + } + start := time.Now() + ctx = s.client.parent.hooks.OnOperationStart(ctx, op) + return ctx, func(errp *error) { + var e error + if errp != nil { + e = *errp + } + s.client.parent.hooks.OnOperationEnd(ctx, op, e, time.Since(start)) + }, nil +} diff --git a/go/pkg/basecamp/everything_test.go b/go/pkg/basecamp/everything_test.go new file mode 100644 index 000000000..965c25d6c --- /dev/null +++ b/go/pkg/basecamp/everything_test.go @@ -0,0 +1,206 @@ +package basecamp + +import ( + "context" + "fmt" + "net/http" + "net/http/httptest" + "net/url" + "testing" +) + +func everythingTestClient(t *testing.T, handler http.HandlerFunc) (*EverythingService, string) { + t.Helper() + server := httptest.NewServer(handler) + t.Cleanup(server.Close) + cfg := DefaultConfig() + cfg.BaseURL = server.URL + client := NewClient(cfg, &StaticTokenProvider{Token: "test-token"}) + return client.ForAccount("99999").Everything(), server.URL +} + +// TestEverythingService_Messages_MultiPage exercises Link-header following and +// X-Total-Count metadata across two pages of the /messages.json root. +func TestEverythingService_Messages_MultiPage(t *testing.T) { + var serverURL string + var page1, page2 int + svc, url := everythingTestClient(t, func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/99999/messages.json" { + t.Errorf("unexpected path: %s", r.URL.Path) + } + w.Header().Set("Content-Type", "application/json") + w.Header().Set("X-Total-Count", "3") + if r.URL.Query().Get("page") == "2" { + page2++ + w.WriteHeader(200) + _, _ = w.Write([]byte(`[{"id":3,"type":"Message","title":"Third","url":"https://x/3.json","status":"active","visible_to_clients":false,"created_at":"2026-01-01T00:00:00Z","updated_at":"2026-01-01T00:00:00Z","inherits_status":true,"app_url":"https://x/3","bucket":{"id":9,"name":"P","type":"Project"},"parent":{"id":8,"title":"MB","type":"Message::Board"},"creator":{"id":1,"name":"A"}}]`)) + return + } + page1++ + w.Header().Set("Link", fmt.Sprintf(`<%s/99999/messages.json?page=2>; rel="next"`, serverURL)) + w.WriteHeader(200) + _, _ = w.Write([]byte(`[{"id":1,"type":"Message","title":"First","url":"https://x/1.json","status":"active","visible_to_clients":false,"created_at":"2026-01-01T00:00:00Z","updated_at":"2026-01-01T00:00:00Z","inherits_status":true,"app_url":"https://x/1","bucket":{"id":9,"name":"P","type":"Project"},"parent":{"id":8,"title":"MB","type":"Message::Board"},"creator":{"id":1,"name":"A"}},{"id":2,"type":"Message","title":"Second","url":"https://x/2.json","status":"active","visible_to_clients":false,"created_at":"2026-01-01T00:00:00Z","updated_at":"2026-01-01T00:00:00Z","inherits_status":true,"app_url":"https://x/2","bucket":{"id":9,"name":"P","type":"Project"},"parent":{"id":8,"title":"MB","type":"Message::Board"},"creator":{"id":1,"name":"A"}}]`)) + }) + serverURL = url + + result, err := svc.Messages(context.Background(), 0) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if page1 != 1 || page2 != 1 { + t.Fatalf("expected one hit per page, got page1=%d page2=%d", page1, page2) + } + if len(result.Recordings) != 3 { + t.Fatalf("expected 3 recordings across pages, got %d", len(result.Recordings)) + } + if result.Recordings[0].ID != 1 || result.Recordings[2].ID != 3 { + t.Errorf("unexpected ordering across pages: %d..%d", result.Recordings[0].ID, result.Recordings[2].ID) + } + if result.Recordings[0].Bucket == nil || result.Recordings[0].Bucket.Name != "P" { + t.Errorf("expected embedded bucket, got %+v", result.Recordings[0].Bucket) + } + if result.Meta.TotalCount != 3 { + t.Errorf("expected TotalCount 3, got %d", result.Meta.TotalCount) + } +} + +// TestEverythingService_OverdueTodos_Unpaginated verifies the overdue list is a +// complete oldest-first array with NO Link-following, even if a Link header is +// present. +func TestEverythingService_OverdueTodos_Unpaginated(t *testing.T) { + var hits int + svc, _ := everythingTestClient(t, func(w http.ResponseWriter, r *http.Request) { + hits++ + if r.URL.Path != "/99999/todos/overdue.json" { + t.Errorf("unexpected path: %s", r.URL.Path) + } + w.Header().Set("Content-Type", "application/json") + // A Link header is present but must NOT be followed for the overdue list. + w.Header().Set("Link", `; rel="next"`) + w.WriteHeader(200) + _, _ = w.Write([]byte(`[ + {"id":10,"content":"Oldest","due_on":"2025-01-01","completed":false}, + {"id":11,"content":"Newer","due_on":"2025-06-01","completed":false} + ]`)) + }) + + todos, err := svc.OverdueTodos(context.Background()) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if hits != 1 { + t.Errorf("expected exactly 1 request (no Link-following), got %d", hits) + } + if len(todos) != 2 { + t.Fatalf("expected 2 overdue todos, got %d", len(todos)) + } + if todos[0].ID != 10 || todos[1].ID != 11 { + t.Errorf("expected oldest-first ordering preserved, got %d, %d", todos[0].ID, todos[1].ID) + } +} + +// TestEverythingService_Files_PerVariantDecode proves the heterogeneous +// /files.json feed decodes all three variants — a full Upload recording, a +// Basecamp Document recording, and a rich-text Attachment envelope — in one +// non-empty array, including a float-spelled and a null width. +func TestEverythingService_Files_PerVariantDecode(t *testing.T) { + svc, _ := everythingTestClient(t, func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + _, _ = w.Write([]byte(`[ + {"id":900,"type":"Upload","title":"logo.png","filename":"logo.png","content_type":"image/png","byte_size":1281,"width":1024.0,"height":768.0,"url":"https://x/uploads/900.json","download_url":"https://x/d/900","app_download_url":"https://storage/900","bucket":{"id":9,"name":"P","type":"Project"}}, + {"id":901,"type":"Document","title":"Spec","url":"https://x/documents/901.json","content_type":"text/html","bucket":{"id":9,"name":"P","type":"Project"}}, + {"id":902,"type":"Attachment","attachable_sgid":"sgid-902","filename":"chart.avif","content_type":"image/avif","byte_size":4096,"width":null,"height":null,"download_url":"https://storage/blobs/902","parent":{"id":800,"title":"A message","type":"Message"}} + ]`)) + }) + + result, err := svc.Files(context.Background(), 1, nil) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if len(result.Files) != 3 { + t.Fatalf("expected 3 files, got %d", len(result.Files)) + } + up := result.Files[0] + if up.Type != "Upload" || up.Filename != "logo.png" || up.AppDownloadURL == "" { + t.Errorf("upload variant not decoded: %+v", up) + } + if up.Width == nil || *up.Width != 1024 { + t.Errorf("expected float-spelled width 1024, got %v", up.Width) + } + if up.AttachableSGID != "" { + t.Errorf("upload variant should not carry attachable_sgid") + } + doc := result.Files[1] + if doc.Type != "Document" || doc.Title != "Spec" { + t.Errorf("document variant not decoded: %+v", doc) + } + att := result.Files[2] + if att.Type != "Attachment" || att.AttachableSGID != "sgid-902" || att.Parent == nil { + t.Errorf("attachment variant not decoded: %+v", att) + } + if att.Width != nil || att.Height != nil { + t.Errorf("expected nil width/height for non-image attachment, got w=%v h=%v", att.Width, att.Height) + } +} + +// TestEverythingService_Files_Filters verifies the kind and people_ids[] query +// parameters reach the wire. +func TestEverythingService_Files_Filters(t *testing.T) { + var captured string + svc, _ := everythingTestClient(t, func(w http.ResponseWriter, r *http.Request) { + captured = r.URL.RawQuery + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + _, _ = w.Write([]byte(`[]`)) + }) + + _, err := svc.Files(context.Background(), 1, &EverythingFilesOptions{Kind: "images", PeopleIDs: []int64{101, 202}}) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + q, _ := url.ParseQuery(captured) + if q.Get("kind") != "images" { + t.Errorf("expected kind=images, got query %q", captured) + } + ids := q["people_ids[]"] + if len(ids) != 2 || ids[0] != "101" || ids[1] != "202" { + t.Errorf("expected people_ids[]=101,202, got %v (query %q)", ids, captured) + } +} + +// TestEverythingService_Messages_ForwardsPage verifies that a positive page is +// forwarded as the ?page= query param (regression: previously page>1 silently +// fetched page 1). Also covers the Files kind+page combination. +func TestEverythingService_Messages_ForwardsPage(t *testing.T) { + var captured string + svc, _ := everythingTestClient(t, func(w http.ResponseWriter, r *http.Request) { + captured = r.URL.RawQuery + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + _, _ = w.Write([]byte(`[]`)) + }) + if _, err := svc.Messages(context.Background(), 2); err != nil { + t.Fatalf("unexpected error: %v", err) + } + if q, _ := url.ParseQuery(captured); q.Get("page") != "2" { + t.Errorf("expected page=2 forwarded, got query %q", captured) + } +} + +func TestEverythingService_Files_ForwardsPageAndKind(t *testing.T) { + var captured string + svc, _ := everythingTestClient(t, func(w http.ResponseWriter, r *http.Request) { + captured = r.URL.RawQuery + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + _, _ = w.Write([]byte(`[]`)) + }) + if _, err := svc.Files(context.Background(), 3, &EverythingFilesOptions{Kind: "pdfs"}); err != nil { + t.Fatalf("unexpected error: %v", err) + } + q, _ := url.ParseQuery(captured) + if q.Get("page") != "3" || q.Get("kind") != "pdfs" { + t.Errorf("expected page=3&kind=pdfs, got %q", captured) + } +} diff --git a/go/pkg/basecamp/recordings.go b/go/pkg/basecamp/recordings.go index 788e905bb..2bc802848 100644 --- a/go/pkg/basecamp/recordings.go +++ b/go/pkg/basecamp/recordings.go @@ -17,6 +17,7 @@ type RecordingType string const ( RecordingTypeComment RecordingType = "Comment" RecordingTypeDocument RecordingType = "Document" + RecordingTypeDoor RecordingType = "Door" RecordingTypeKanbanCard RecordingType = "Kanban::Card" RecordingTypeKanbanStep RecordingType = "Kanban::Step" RecordingTypeMessage RecordingType = "Message" @@ -61,9 +62,28 @@ type Recording struct { CommentsCount int `json:"comments_count,omitempty"` CommentsURL string `json:"comments_url,omitempty"` SubscriptionURL string `json:"subscription_url,omitempty"` - Parent *Parent `json:"parent,omitempty"` - Bucket *Bucket `json:"bucket,omitempty"` - Creator *Person `json:"creator,omitempty"` + // Position, Description, and Service are door-specific (external-link) + // fields, populated only on Door recordings returned by the type=Door + // recordings query (the only endpoint that returns the full door shape). + // See spec/api-gaps/external-links-doors.md. + Position int32 `json:"position,omitempty"` + Description string `json:"description,omitempty"` + Service *DoorService `json:"service,omitempty"` + Parent *Parent `json:"parent,omitempty"` + Bucket *Bucket `json:"bucket,omitempty"` + Creator *Person `json:"creator,omitempty"` +} + +// DoorService describes the recognized external service backing an external +// link (Door recording): its display name, a canonical example URL, a short +// code (or "other" for a generic link), the URL patterns Basecamp recognizes, +// and human supporting text. +type DoorService struct { + Name string `json:"name,omitempty"` + ExampleURL string `json:"example_url,omitempty"` + Code string `json:"code,omitempty"` + ValidPatterns []string `json:"valid_patterns,omitempty"` + SupportingText string `json:"supporting_text,omitempty"` } // DefaultRecordingLimit is the default number of recordings to return when no limit is specified. @@ -425,5 +445,19 @@ func recordingFromGenerated(gr generated.Recording) Recording { r.ContentAttachments = richTextAttachmentsPtrFromGenerated(gr.ContentAttachments) r.DescriptionAttachments = richTextAttachmentsPtrFromGenerated(gr.DescriptionAttachments) + // Door-specific fields (populated only for type=Door recordings). + r.Position = gr.Position + r.Description = gr.Description + if gr.Service.Name != "" || gr.Service.Code != "" || gr.Service.ExampleUrl != "" || + gr.Service.SupportingText != "" || len(gr.Service.ValidPatterns) > 0 { + r.Service = &DoorService{ + Name: gr.Service.Name, + ExampleURL: gr.Service.ExampleUrl, + Code: gr.Service.Code, + ValidPatterns: append([]string(nil), gr.Service.ValidPatterns...), + SupportingText: gr.Service.SupportingText, + } + } + return r } diff --git a/go/pkg/basecamp/recordings_test.go b/go/pkg/basecamp/recordings_test.go index ffae57cb5..6e4db16f6 100644 --- a/go/pkg/basecamp/recordings_test.go +++ b/go/pkg/basecamp/recordings_test.go @@ -268,6 +268,7 @@ func TestRecordingType_Constants(t *testing.T) { }{ {RecordingTypeComment, "Comment"}, {RecordingTypeDocument, "Document"}, + {RecordingTypeDoor, "Door"}, {RecordingTypeKanbanCard, "Kanban::Card"}, {RecordingTypeKanbanStep, "Kanban::Step"}, {RecordingTypeMessage, "Message"}, @@ -286,6 +287,86 @@ func TestRecordingType_Constants(t *testing.T) { } } +// TestRecording_UnmarshalDoor verifies that a type=Door recording (external +// link) decodes with the full door shape — the outside url, the service struct, +// the description, and the position — through the shared Recording projection. +func TestRecording_UnmarshalDoor(t *testing.T) { + data := `[ + { + "id": 1069480290, + "status": "active", + "visible_to_clients": false, + "created_at": "2026-07-22T15:51:54.872Z", + "updated_at": "2026-07-22T15:51:54.886Z", + "title": "Design system", + "inherits_status": true, + "type": "Door", + "url": "https://www.figma.com/file/abc123/Design-system", + "app_url": "https://3.basecampapi.com/195539477/buckets/2085958504/dock/doors/1069480290", + "position": 8, + "bucket": {"id": 2085958504, "name": "The Leto Laptop", "type": "Project"}, + "creator": {"id": 1049715913, "name": "Victor Cooper"}, + "service": { + "name": "Figma", + "example_url": "https://www.figma.com/file/aGVsbG8gZmlnbWEgZmlsZQ", + "code": "figma", + "valid_patterns": ["(.*?\\.)?figma\\.com(\\/.*)?"], + "supporting_text": "a file or project on Figma" + }, + "description": "
Shared Figma workspace
" + } + ]` + + var recordings []Recording + if err := json.Unmarshal([]byte(data), &recordings); err != nil { + t.Fatalf("failed to unmarshal door recording: %v", err) + } + if len(recordings) != 1 { + t.Fatalf("expected 1 recording, got %d", len(recordings)) + } + d := recordings[0] + if d.Type != "Door" { + t.Errorf("expected type Door, got %q", d.Type) + } + if d.URL != "https://www.figma.com/file/abc123/Design-system" { + t.Errorf("expected external url, got %q", d.URL) + } + if d.Position != 8 { + t.Errorf("expected position 8, got %d", d.Position) + } + if d.Description != "
Shared Figma workspace
" { + t.Errorf("unexpected description: %q", d.Description) + } + if d.Service == nil { + t.Fatal("expected service struct to be non-nil for a Door") + } + if d.Service.Name != "Figma" || d.Service.Code != "figma" { + t.Errorf("unexpected service name/code: %q/%q", d.Service.Name, d.Service.Code) + } + if len(d.Service.ValidPatterns) != 1 || d.Service.ValidPatterns[0] == "" { + t.Errorf("expected 1 valid_pattern, got %v", d.Service.ValidPatterns) + } + if d.Service.SupportingText != "a file or project on Figma" { + t.Errorf("unexpected supporting_text: %q", d.Service.SupportingText) + } +} + +// TestRecording_UnmarshalNonDoorOmitsDoorFields verifies a non-door recording +// leaves the door-specific fields empty (they are optional). +func TestRecording_UnmarshalNonDoorOmitsDoorFields(t *testing.T) { + data := `{"id": 1, "type": "Message", "title": "Hi", "url": "https://x/1.json"}` + var r Recording + if err := json.Unmarshal([]byte(data), &r); err != nil { + t.Fatalf("failed to unmarshal: %v", err) + } + if r.Service != nil { + t.Errorf("expected nil service for non-door, got %+v", r.Service) + } + if r.Description != "" || r.Position != 0 { + t.Errorf("expected empty door fields, got description=%q position=%d", r.Description, r.Position) + } +} + func TestRecordingsListOptions_BuildsQueryParams(t *testing.T) { // This is a structural test to ensure the options fields exist opts := RecordingsListOptions{ diff --git a/go/pkg/basecamp/url-routes.json b/go/pkg/basecamp/url-routes.json index bcf7db246..a3ffc6ded 100644 --- a/go/pkg/basecamp/url-routes.json +++ b/go/pkg/basecamp/url-routes.json @@ -56,6 +56,19 @@ } } }, + { + "pattern": "/{accountId}/boosts", + "resource": "Everything", + "operations": { + "GET": "GetEverythingBoosts" + }, + "params": { + "accountId": { + "role": "account", + "type": "string" + } + } + }, { "pattern": "/{accountId}/boosts/{boostId}", "resource": "Boosts", @@ -404,6 +417,19 @@ } } }, + { + "pattern": "/{accountId}/cards/overdue", + "resource": "Everything", + "operations": { + "GET": "GetEverythingOverdueCards" + }, + "params": { + "accountId": { + "role": "account", + "type": "string" + } + } + }, { "pattern": "/{accountId}/categories", "resource": "Messages", @@ -567,6 +593,19 @@ } } }, + { + "pattern": "/{accountId}/checkins", + "resource": "Everything", + "operations": { + "GET": "GetEverythingCheckins" + }, + "params": { + "accountId": { + "role": "account", + "type": "string" + } + } + }, { "pattern": "/{accountId}/circles/people", "resource": "People", @@ -678,6 +717,19 @@ } } }, + { + "pattern": "/{accountId}/comments", + "resource": "Everything", + "operations": { + "GET": "GetEverythingComments" + }, + "params": { + "accountId": { + "role": "account", + "type": "string" + } + } + }, { "pattern": "/{accountId}/comments/{commentId}", "resource": "Messages", @@ -733,6 +785,32 @@ } } }, + { + "pattern": "/{accountId}/files", + "resource": "Everything", + "operations": { + "GET": "GetEverythingFiles" + }, + "params": { + "accountId": { + "role": "account", + "type": "string" + } + } + }, + { + "pattern": "/{accountId}/forwards", + "resource": "Everything", + "operations": { + "GET": "GetEverythingForwards" + }, + "params": { + "accountId": { + "role": "account", + "type": "string" + } + } + }, { "pattern": "/{accountId}/gauge_needles/{needleId}", "resource": "Gauges", @@ -909,6 +987,19 @@ } } }, + { + "pattern": "/{accountId}/messages", + "resource": "Everything", + "operations": { + "GET": "GetEverythingMessages" + }, + "params": { + "accountId": { + "role": "account", + "type": "string" + } + } + }, { "pattern": "/{accountId}/messages/{messageId}", "resource": "Messages", @@ -2030,6 +2121,19 @@ } } }, + { + "pattern": "/{accountId}/todos/overdue", + "resource": "Everything", + "operations": { + "GET": "GetEverythingOverdueTodos" + }, + "params": { + "accountId": { + "role": "account", + "type": "string" + } + } + }, { "pattern": "/{accountId}/todos/{todoId}", "resource": "Todos", diff --git a/go/pkg/generated/client.gen.go b/go/pkg/generated/client.gen.go index 8a901f112..818ceaf21 100644 --- a/go/pkg/generated/client.gen.go +++ b/go/pkg/generated/client.gen.go @@ -806,6 +806,18 @@ type Document struct { VisibleToClients bool `json:"visible_to_clients"` } +// DoorService Metadata describing the recognized external service backing an external link +// (`Door` recording): its display name, a canonical example URL, a short code, +// the URL patterns Basecamp recognizes for it, and human supporting text. `code` +// is `other` for a generic link. +type DoorService struct { + Code string `json:"code,omitempty"` + ExampleUrl string `json:"example_url,omitempty"` + Name string `json:"name,omitempty"` + SupportingText string `json:"supporting_text,omitempty"` + ValidPatterns []string `json:"valid_patterns,omitempty"` +} + // EnableCardColumnOnHoldResponseContent defines model for EnableCardColumnOnHoldResponseContent. type EnableCardColumnOnHoldResponseContent = CardColumn @@ -837,6 +849,59 @@ type EventDetails struct { RemovedPersonIds []int64 `json:"removed_person_ids,omitempty"` } +// EverythingFile A single item in the /files.json feed. An optional-field superset over three +// wire variants — a full Upload recording, a Basecamp Document recording, and a +// rich-text attachment wrapped in a recording envelope (distinguished by +// `attachable_sgid` and blob metadata). Every field is optional; a given +// instance populates only the fields of the variant it represents. Unknown +// fields are ignored by every SDK decoder, so the superset need not enumerate +// every field of the Upload/Document recordings. +type EverythingFile struct { + AppDownloadUrl string `json:"app_download_url,omitempty"` + AppUrl string `json:"app_url,omitempty"` + + // AttachableSgid Present on the rich-text attachment variant: signed global id of the + // attachment (uploads/documents omit it). + AttachableSgid string `json:"attachable_sgid,omitempty"` + BookmarkUrl string `json:"bookmark_url,omitempty"` + BoostsCount int32 `json:"boosts_count,omitempty"` + BoostsUrl string `json:"boosts_url,omitempty"` + Bucket RecordingBucket `json:"bucket,omitempty"` + ByteSize int64 `json:"byte_size,omitempty"` + CommentsCount int32 `json:"comments_count,omitempty"` + CommentsUrl string `json:"comments_url,omitempty"` + ContentType string `json:"content_type,omitempty"` + CreatedAt *time.Time `json:"created_at,omitempty"` + Creator Person `json:"creator,omitempty"` + + // Description Rich-text description (upload/document variants). + Description string `json:"description,omitempty"` + DescriptionAttachments []RichTextAttachment `json:"description_attachments,omitempty"` + DownloadUrl string `json:"download_url,omitempty"` + Filename string `json:"filename,omitempty"` + + // Height Pixel height; null for non-image blobs and may be float-spelled (1024.0). + Height *types.FlexInt `json:"height,omitempty"` + + // Id Recording (Upload/Document) or attachment id. + Id *int64 `json:"id,omitempty"` + InheritsStatus *bool `json:"inherits_status,omitempty"` + Parent RecordingParent `json:"parent,omitempty"` + Position int32 `json:"position,omitempty"` + Status string `json:"status,omitempty"` + SubscriptionUrl string `json:"subscription_url,omitempty"` + Title string `json:"title,omitempty"` + + // Type "Upload", "Document", or "Attachment". + Type string `json:"type,omitempty"` + UpdatedAt *time.Time `json:"updated_at,omitempty"` + Url string `json:"url,omitempty"` + VisibleToClients *bool `json:"visible_to_clients,omitempty"` + + // Width Pixel width; null for non-image blobs and may be float-spelled (1024.0). + Width *types.FlexInt `json:"width,omitempty"` +} + // FirstWeekDay defines model for FirstWeekDay. type FirstWeekDay string @@ -1029,6 +1094,30 @@ type GetCommentResponseContent = Comment // GetDocumentResponseContent defines model for GetDocumentResponseContent. type GetDocumentResponseContent = Document +// GetEverythingBoostsResponseContent defines model for GetEverythingBoostsResponseContent. +type GetEverythingBoostsResponseContent = []Boost + +// GetEverythingCheckinsResponseContent defines model for GetEverythingCheckinsResponseContent. +type GetEverythingCheckinsResponseContent = []Recording + +// GetEverythingCommentsResponseContent defines model for GetEverythingCommentsResponseContent. +type GetEverythingCommentsResponseContent = []Recording + +// GetEverythingFilesResponseContent defines model for GetEverythingFilesResponseContent. +type GetEverythingFilesResponseContent = []EverythingFile + +// GetEverythingForwardsResponseContent defines model for GetEverythingForwardsResponseContent. +type GetEverythingForwardsResponseContent = []Recording + +// GetEverythingMessagesResponseContent defines model for GetEverythingMessagesResponseContent. +type GetEverythingMessagesResponseContent = []Recording + +// GetEverythingOverdueCardsResponseContent defines model for GetEverythingOverdueCardsResponseContent. +type GetEverythingOverdueCardsResponseContent = []Card + +// GetEverythingOverdueTodosResponseContent defines model for GetEverythingOverdueTodosResponseContent. +type GetEverythingOverdueTodosResponseContent = []Todo + // GetForwardReplyResponseContent defines model for GetForwardReplyResponseContent. type GetForwardReplyResponseContent = ForwardReply @@ -1828,18 +1917,40 @@ type Recording struct { CreatedAt time.Time `json:"created_at"` Creator Person `json:"creator"` + // Description Rich-text (HTML) description shown beneath an external link. Present only + // on `Door` recordings returned by the `type=Door` recordings query (the only + // endpoint that returns the full door shape). The external destination + // address is `url` (not this field); `app_url` is the Basecamp redirector. + // See `spec/api-gaps/external-links-doors.md`. + Description string `json:"description,omitempty"` + // DescriptionAttachments See `content_attachments` — the description-attribute companion array. DescriptionAttachments []RichTextAttachment `json:"description_attachments,omitempty"` Id int64 `json:"id"` InheritsStatus bool `json:"inherits_status"` - Parent RecordingParent `json:"parent"` - Status string `json:"status"` - SubscriptionUrl string `json:"subscription_url,omitempty"` - Title string `json:"title"` - Type string `json:"type"` - UpdatedAt time.Time `json:"updated_at"` - Url string `json:"url"` - VisibleToClients bool `json:"visible_to_clients"` + Parent RecordingParent `json:"parent,omitempty"` + + // Position Ordinal position within the project's External links section. Present on + // `Door` (external-link) recordings. + Position int32 `json:"position,omitempty"` + + // Service Metadata describing the recognized external service backing an external link + // (`Door` recording): its display name, a canonical example URL, a short code, + // the URL patterns Basecamp recognizes for it, and human supporting text. `code` + // is `other` for a generic link. + Service DoorService `json:"service,omitempty"` + Status string `json:"status"` + SubscriptionUrl string `json:"subscription_url,omitempty"` + Title string `json:"title"` + Type string `json:"type"` + UpdatedAt time.Time `json:"updated_at"` + + // Url API URL of the recording. Exception: in the `type=Door` (external-link) + // projection, `url` is the door's **external destination address** (e.g. the + // Figma/Dropbox URL) and `app_url` is the Basecamp redirector — see the + // door-specific `service`/`description` fields below. + Url string `json:"url"` + VisibleToClients bool `json:"visible_to_clients"` } // RecordingBucket defines model for RecordingBucket. @@ -2960,6 +3071,12 @@ type CreateAttachmentParams struct { Name string `form:"name" json:"name"` } +// GetEverythingBoostsParams defines parameters for GetEverythingBoosts. +type GetEverythingBoostsParams struct { + // Page Page number for paginating through results. Defaults to 1. + Page int32 `form:"page,omitempty" json:"page,omitempty"` +} + // ListCampfireLinesParams defines parameters for ListCampfireLines. type ListCampfireLinesParams struct { // Sort created_at|updated_at @@ -2984,6 +3101,12 @@ type CreateCampfireUploadParams struct { Name string `form:"name" json:"name"` } +// GetEverythingCheckinsParams defines parameters for GetEverythingCheckins. +type GetEverythingCheckinsParams struct { + // Page Page number for paginating through results. Defaults to 1. + Page int32 `form:"page,omitempty" json:"page,omitempty"` +} + // ListClientApprovalsParams defines parameters for ListClientApprovals. type ListClientApprovalsParams struct { // Sort created_at|updated_at @@ -3002,6 +3125,30 @@ type ListClientCorrespondencesParams struct { Direction string `form:"direction,omitempty" json:"direction,omitempty"` } +// GetEverythingCommentsParams defines parameters for GetEverythingComments. +type GetEverythingCommentsParams struct { + // Page Page number for paginating through results. Defaults to 1. + Page int32 `form:"page,omitempty" json:"page,omitempty"` +} + +// GetEverythingFilesParams defines parameters for GetEverythingFiles. +type GetEverythingFilesParams struct { + // Kind Filter by file kind: all (default), images, pdfs, documents, or videos. + Kind string `form:"kind,omitempty" json:"kind,omitempty"` + + // PeopleIds Restrict to files created by the given people (repeatable). + PeopleIds *[]int64 `form:"people_ids[],omitempty" json:"people_ids[],omitempty"` + + // Page Page number for paginating through results. Defaults to 1. + Page int32 `form:"page,omitempty" json:"page,omitempty"` +} + +// GetEverythingForwardsParams defines parameters for GetEverythingForwards. +type GetEverythingForwardsParams struct { + // Page Page number for paginating through results. Defaults to 1. + Page int32 `form:"page,omitempty" json:"page,omitempty"` +} + // ListForwardsParams defines parameters for ListForwards. type ListForwardsParams struct { // Sort created_at|updated_at @@ -3020,6 +3167,12 @@ type ListMessagesParams struct { Direction string `form:"direction,omitempty" json:"direction,omitempty"` } +// GetEverythingMessagesParams defines parameters for GetEverythingMessages. +type GetEverythingMessagesParams struct { + // Page Page number for paginating through results. Defaults to 1. + Page int32 `form:"page,omitempty" json:"page,omitempty"` +} + // GetMyDueAssignmentsParams defines parameters for GetMyDueAssignments. type GetMyDueAssignmentsParams struct { // Scope Filter by due date range: overdue, due_today, due_tomorrow, @@ -3053,7 +3206,7 @@ type ListProjectsParams struct { // ListRecordingsParams defines parameters for ListRecordings. type ListRecordingsParams struct { - // Type Comment|Document|Kanban::Card|Kanban::Step|Message|Question::Answer|Schedule::Entry|Todo|Todolist|Upload|Vault + // Type Comment|Document|Door|Kanban::Card|Kanban::Step|Message|Question::Answer|Schedule::Entry|Todo|Todolist|Upload|Vault Type string `form:"type" json:"type"` Bucket string `form:"bucket,omitempty" json:"bucket,omitempty"` @@ -3707,6 +3860,9 @@ type ClientInterface interface { // CreateAttachmentWithBody request with any body CreateAttachmentWithBody(ctx context.Context, accountId string, params *CreateAttachmentParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // GetEverythingBoosts request + GetEverythingBoosts(ctx context.Context, accountId string, params *GetEverythingBoostsParams, reqEditors ...RequestEditorFn) (*http.Response, error) + // DeleteBoost request DeleteBoost(ctx context.Context, accountId string, boostId int64, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -3821,6 +3977,9 @@ type ClientInterface interface { MoveCardColumn(ctx context.Context, accountId string, cardTableId int64, body MoveCardColumnJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // GetEverythingOverdueCards request + GetEverythingOverdueCards(ctx context.Context, accountId string, reqEditors ...RequestEditorFn) (*http.Response, error) + // ListMessageTypes request ListMessageTypes(ctx context.Context, accountId string, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -3890,6 +4049,9 @@ type ClientInterface interface { // CreateCampfireUploadWithBody request with any body CreateCampfireUploadWithBody(ctx context.Context, accountId string, campfireId int64, params *CreateCampfireUploadParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // GetEverythingCheckins request + GetEverythingCheckins(ctx context.Context, accountId string, params *GetEverythingCheckinsParams, reqEditors ...RequestEditorFn) (*http.Response, error) + // ListPingablePeople request ListPingablePeople(ctx context.Context, accountId string, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -3911,6 +4073,9 @@ type ClientInterface interface { // GetClientReply request GetClientReply(ctx context.Context, accountId string, recordingId int64, replyId int64, reqEditors ...RequestEditorFn) (*http.Response, error) + // GetEverythingComments request + GetEverythingComments(ctx context.Context, accountId string, params *GetEverythingCommentsParams, reqEditors ...RequestEditorFn) (*http.Response, error) + // GetComment request GetComment(ctx context.Context, accountId string, commentId int64, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -3938,6 +4103,12 @@ type ClientInterface interface { UpdateDocument(ctx context.Context, accountId string, documentId int64, body UpdateDocumentJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // GetEverythingFiles request + GetEverythingFiles(ctx context.Context, accountId string, params *GetEverythingFilesParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetEverythingForwards request + GetEverythingForwards(ctx context.Context, accountId string, params *GetEverythingForwardsParams, reqEditors ...RequestEditorFn) (*http.Response, error) + // DestroyGaugeNeedle request DestroyGaugeNeedle(ctx context.Context, accountId string, needleId int64, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -3996,6 +4167,9 @@ type ClientInterface interface { CreateMessage(ctx context.Context, accountId string, boardId int64, body CreateMessageJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // GetEverythingMessages request + GetEverythingMessages(ctx context.Context, accountId string, params *GetEverythingMessagesParams, reqEditors ...RequestEditorFn) (*http.Response, error) + // GetMessage request GetMessage(ctx context.Context, accountId string, messageId int64, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -4365,6 +4539,9 @@ type ClientInterface interface { CreateTodo(ctx context.Context, accountId string, todolistId int64, body CreateTodoJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // GetEverythingOverdueTodos request + GetEverythingOverdueTodos(ctx context.Context, accountId string, reqEditors ...RequestEditorFn) (*http.Response, error) + // TrashTodo request TrashTodo(ctx context.Context, accountId string, todoId int64, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -4530,6 +4707,16 @@ func (c *Client) CreateAttachmentWithBody(ctx context.Context, accountId string, } +// GetEverythingBoosts is marked as idempotent and will be retried on transient failures. + +func (c *Client) GetEverythingBoosts(ctx context.Context, accountId string, params *GetEverythingBoostsParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + + return c.doWithRetry(ctx, func() (*http.Request, error) { + return NewGetEverythingBoostsRequest(c.Server, accountId, params) + }, true, "GetEverythingBoosts", reqEditors...) + +} + // DeleteBoost is marked as idempotent and will be retried on transient failures. func (c *Client) DeleteBoost(ctx context.Context, accountId string, boostId int64, reqEditors ...RequestEditorFn) (*http.Response, error) { @@ -5044,6 +5231,16 @@ func (c *Client) MoveCardColumn(ctx context.Context, accountId string, cardTable } +// GetEverythingOverdueCards is marked as idempotent and will be retried on transient failures. + +func (c *Client) GetEverythingOverdueCards(ctx context.Context, accountId string, reqEditors ...RequestEditorFn) (*http.Response, error) { + + return c.doWithRetry(ctx, func() (*http.Request, error) { + return NewGetEverythingOverdueCardsRequest(c.Server, accountId) + }, true, "GetEverythingOverdueCards", reqEditors...) + +} + // ListMessageTypes is marked as idempotent and will be retried on transient failures. func (c *Client) ListMessageTypes(ctx context.Context, accountId string, reqEditors ...RequestEditorFn) (*http.Response, error) { @@ -5324,6 +5521,16 @@ func (c *Client) CreateCampfireUploadWithBody(ctx context.Context, accountId str } +// GetEverythingCheckins is marked as idempotent and will be retried on transient failures. + +func (c *Client) GetEverythingCheckins(ctx context.Context, accountId string, params *GetEverythingCheckinsParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + + return c.doWithRetry(ctx, func() (*http.Request, error) { + return NewGetEverythingCheckinsRequest(c.Server, accountId, params) + }, true, "GetEverythingCheckins", reqEditors...) + +} + // ListPingablePeople is marked as idempotent and will be retried on transient failures. func (c *Client) ListPingablePeople(ctx context.Context, accountId string, reqEditors ...RequestEditorFn) (*http.Response, error) { @@ -5394,6 +5601,16 @@ func (c *Client) GetClientReply(ctx context.Context, accountId string, recording } +// GetEverythingComments is marked as idempotent and will be retried on transient failures. + +func (c *Client) GetEverythingComments(ctx context.Context, accountId string, params *GetEverythingCommentsParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + + return c.doWithRetry(ctx, func() (*http.Request, error) { + return NewGetEverythingCommentsRequest(c.Server, accountId, params) + }, true, "GetEverythingComments", reqEditors...) + +} + // GetComment is marked as idempotent and will be retried on transient failures. func (c *Client) GetComment(ctx context.Context, accountId string, commentId int64, reqEditors ...RequestEditorFn) (*http.Response, error) { @@ -5488,6 +5705,26 @@ func (c *Client) UpdateDocument(ctx context.Context, accountId string, documentI } +// GetEverythingFiles is marked as idempotent and will be retried on transient failures. + +func (c *Client) GetEverythingFiles(ctx context.Context, accountId string, params *GetEverythingFilesParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + + return c.doWithRetry(ctx, func() (*http.Request, error) { + return NewGetEverythingFilesRequest(c.Server, accountId, params) + }, true, "GetEverythingFiles", reqEditors...) + +} + +// GetEverythingForwards is marked as idempotent and will be retried on transient failures. + +func (c *Client) GetEverythingForwards(ctx context.Context, accountId string, params *GetEverythingForwardsParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + + return c.doWithRetry(ctx, func() (*http.Request, error) { + return NewGetEverythingForwardsRequest(c.Server, accountId, params) + }, true, "GetEverythingForwards", reqEditors...) + +} + // DestroyGaugeNeedle is marked as idempotent and will be retried on transient failures. func (c *Client) DestroyGaugeNeedle(ctx context.Context, accountId string, needleId int64, reqEditors ...RequestEditorFn) (*http.Response, error) { @@ -5724,6 +5961,16 @@ func (c *Client) CreateMessage(ctx context.Context, accountId string, boardId in } +// GetEverythingMessages is marked as idempotent and will be retried on transient failures. + +func (c *Client) GetEverythingMessages(ctx context.Context, accountId string, params *GetEverythingMessagesParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + + return c.doWithRetry(ctx, func() (*http.Request, error) { + return NewGetEverythingMessagesRequest(c.Server, accountId, params) + }, true, "GetEverythingMessages", reqEditors...) + +} + // GetMessage is marked as idempotent and will be retried on transient failures. func (c *Client) GetMessage(ctx context.Context, accountId string, messageId int64, reqEditors ...RequestEditorFn) (*http.Response, error) { @@ -7184,6 +7431,16 @@ func (c *Client) CreateTodo(ctx context.Context, accountId string, todolistId in } +// GetEverythingOverdueTodos is marked as idempotent and will be retried on transient failures. + +func (c *Client) GetEverythingOverdueTodos(ctx context.Context, accountId string, reqEditors ...RequestEditorFn) (*http.Response, error) { + + return c.doWithRetry(ctx, func() (*http.Request, error) { + return NewGetEverythingOverdueTodosRequest(c.Server, accountId) + }, true, "GetEverythingOverdueTodos", reqEditors...) + +} + // TrashTodo is marked as idempotent and will be retried on transient failures. func (c *Client) TrashTodo(ctx context.Context, accountId string, todoId int64, reqEditors ...RequestEditorFn) (*http.Response, error) { @@ -7785,6 +8042,62 @@ func NewCreateAttachmentRequestWithBody(server string, accountId string, params return req, nil } +// NewGetEverythingBoostsRequest generates requests for GetEverythingBoosts +func NewGetEverythingBoostsRequest(server string, accountId string, params *GetEverythingBoostsParams) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "accountId", runtime.ParamLocationPath, accountId) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/%s/boosts.json", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + queryValues := queryURL.Query() + + if params.Page != 0 { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "page", runtime.ParamLocationQuery, params.Page); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + queryURL.RawQuery = queryValues.Encode() + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + // NewDeleteBoostRequest generates requests for DeleteBoost func NewDeleteBoostRequest(server string, accountId string, boostId int64) (*http.Request, error) { var err error @@ -9170,8 +9483,8 @@ func NewMoveCardColumnRequestWithBody(server string, accountId string, cardTable return req, nil } -// NewListMessageTypesRequest generates requests for ListMessageTypes -func NewListMessageTypesRequest(server string, accountId string) (*http.Request, error) { +// NewGetEverythingOverdueCardsRequest generates requests for GetEverythingOverdueCards +func NewGetEverythingOverdueCardsRequest(server string, accountId string) (*http.Request, error) { var err error var pathParam0 string @@ -9186,7 +9499,7 @@ func NewListMessageTypesRequest(server string, accountId string) (*http.Request, return nil, err } - operationPath := fmt.Sprintf("/%s/categories.json", pathParam0) + operationPath := fmt.Sprintf("/%s/cards/overdue.json", pathParam0) if operationPath[0] == '/' { operationPath = "." + operationPath } @@ -9204,19 +9517,8 @@ func NewListMessageTypesRequest(server string, accountId string) (*http.Request, return req, nil } -// NewCreateMessageTypeRequest calls the generic CreateMessageType builder with application/json body -func NewCreateMessageTypeRequest(server string, accountId string, body CreateMessageTypeJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewCreateMessageTypeRequestWithBody(server, accountId, "application/json", bodyReader) -} - -// NewCreateMessageTypeRequestWithBody generates requests for CreateMessageType with any type of body -func NewCreateMessageTypeRequestWithBody(server string, accountId string, contentType string, body io.Reader) (*http.Request, error) { +// NewListMessageTypesRequest generates requests for ListMessageTypes +func NewListMessageTypesRequest(server string, accountId string) (*http.Request, error) { var err error var pathParam0 string @@ -9241,59 +9543,27 @@ func NewCreateMessageTypeRequestWithBody(server string, accountId string, conten return nil, err } - req, err := http.NewRequest("POST", queryURL.String(), body) + req, err := http.NewRequest("GET", queryURL.String(), nil) if err != nil { return nil, err } - req.Header.Add("Content-Type", contentType) - return req, nil } -// NewDeleteMessageTypeRequest generates requests for DeleteMessageType -func NewDeleteMessageTypeRequest(server string, accountId string, typeId int64) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "accountId", runtime.ParamLocationPath, accountId) - if err != nil { - return nil, err - } - - var pathParam1 string - - pathParam1, err = runtime.StyleParamWithLocation("simple", false, "typeId", runtime.ParamLocationPath, typeId) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/%s/categories/%s", pathParam0, pathParam1) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest("DELETE", queryURL.String(), nil) +// NewCreateMessageTypeRequest calls the generic CreateMessageType builder with application/json body +func NewCreateMessageTypeRequest(server string, accountId string, body CreateMessageTypeJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) if err != nil { return nil, err } - - return req, nil + bodyReader = bytes.NewReader(buf) + return NewCreateMessageTypeRequestWithBody(server, accountId, "application/json", bodyReader) } -// NewGetMessageTypeRequest generates requests for GetMessageType -func NewGetMessageTypeRequest(server string, accountId string, typeId int64) (*http.Request, error) { +// NewCreateMessageTypeRequestWithBody generates requests for CreateMessageType with any type of body +func NewCreateMessageTypeRequestWithBody(server string, accountId string, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -9303,19 +9573,12 @@ func NewGetMessageTypeRequest(server string, accountId string, typeId int64) (*h return nil, err } - var pathParam1 string - - pathParam1, err = runtime.StyleParamWithLocation("simple", false, "typeId", runtime.ParamLocationPath, typeId) - if err != nil { - return nil, err - } - serverURL, err := url.Parse(server) if err != nil { return nil, err } - operationPath := fmt.Sprintf("/%s/categories/%s", pathParam0, pathParam1) + operationPath := fmt.Sprintf("/%s/categories.json", pathParam0) if operationPath[0] == '/' { operationPath = "." + operationPath } @@ -9325,27 +9588,111 @@ func NewGetMessageTypeRequest(server string, accountId string, typeId int64) (*h return nil, err } - req, err := http.NewRequest("GET", queryURL.String(), nil) + req, err := http.NewRequest("POST", queryURL.String(), body) if err != nil { return nil, err } - return req, nil -} + req.Header.Add("Content-Type", contentType) -// NewUpdateMessageTypeRequest calls the generic UpdateMessageType builder with application/json body -func NewUpdateMessageTypeRequest(server string, accountId string, typeId int64, body UpdateMessageTypeJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewUpdateMessageTypeRequestWithBody(server, accountId, typeId, "application/json", bodyReader) + return req, nil } -// NewUpdateMessageTypeRequestWithBody generates requests for UpdateMessageType with any type of body -func NewUpdateMessageTypeRequestWithBody(server string, accountId string, typeId int64, contentType string, body io.Reader) (*http.Request, error) { +// NewDeleteMessageTypeRequest generates requests for DeleteMessageType +func NewDeleteMessageTypeRequest(server string, accountId string, typeId int64) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "accountId", runtime.ParamLocationPath, accountId) + if err != nil { + return nil, err + } + + var pathParam1 string + + pathParam1, err = runtime.StyleParamWithLocation("simple", false, "typeId", runtime.ParamLocationPath, typeId) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/%s/categories/%s", pathParam0, pathParam1) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("DELETE", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetMessageTypeRequest generates requests for GetMessageType +func NewGetMessageTypeRequest(server string, accountId string, typeId int64) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "accountId", runtime.ParamLocationPath, accountId) + if err != nil { + return nil, err + } + + var pathParam1 string + + pathParam1, err = runtime.StyleParamWithLocation("simple", false, "typeId", runtime.ParamLocationPath, typeId) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/%s/categories/%s", pathParam0, pathParam1) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewUpdateMessageTypeRequest calls the generic UpdateMessageType builder with application/json body +func NewUpdateMessageTypeRequest(server string, accountId string, typeId int64, body UpdateMessageTypeJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewUpdateMessageTypeRequestWithBody(server, accountId, typeId, "application/json", bodyReader) +} + +// NewUpdateMessageTypeRequestWithBody generates requests for UpdateMessageType with any type of body +func NewUpdateMessageTypeRequestWithBody(server string, accountId string, typeId int64, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -10144,42 +10491,8 @@ func NewCreateCampfireUploadRequestWithBody(server string, accountId string, cam return req, nil } -// NewListPingablePeopleRequest generates requests for ListPingablePeople -func NewListPingablePeopleRequest(server string, accountId string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "accountId", runtime.ParamLocationPath, accountId) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/%s/circles/people.json", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest("GET", queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewListClientApprovalsRequest generates requests for ListClientApprovals -func NewListClientApprovalsRequest(server string, accountId string, params *ListClientApprovalsParams) (*http.Request, error) { +// NewGetEverythingCheckinsRequest generates requests for GetEverythingCheckins +func NewGetEverythingCheckinsRequest(server string, accountId string, params *GetEverythingCheckinsParams) (*http.Request, error) { var err error var pathParam0 string @@ -10194,7 +10507,7 @@ func NewListClientApprovalsRequest(server string, accountId string, params *List return nil, err } - operationPath := fmt.Sprintf("/%s/client/approvals.json", pathParam0) + operationPath := fmt.Sprintf("/%s/checkins.json", pathParam0) if operationPath[0] == '/' { operationPath = "." + operationPath } @@ -10207,138 +10520,228 @@ func NewListClientApprovalsRequest(server string, accountId string, params *List if params != nil { queryValues := queryURL.Query() - if params.Sort != "" { - - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "sort", runtime.ParamLocationQuery, params.Sort); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { - return nil, err - } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } - } - } - - } - - if params.Direction != "" { - - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "direction", runtime.ParamLocationQuery, params.Direction); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { - return nil, err - } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } - } - } - - } - - queryURL.RawQuery = queryValues.Encode() - } - - req, err := http.NewRequest("GET", queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewGetClientApprovalRequest generates requests for GetClientApproval -func NewGetClientApprovalRequest(server string, accountId string, approvalId int64) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "accountId", runtime.ParamLocationPath, accountId) - if err != nil { - return nil, err - } - - var pathParam1 string - - pathParam1, err = runtime.StyleParamWithLocation("simple", false, "approvalId", runtime.ParamLocationPath, approvalId) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/%s/client/approvals/%s", pathParam0, pathParam1) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest("GET", queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewListClientCorrespondencesRequest generates requests for ListClientCorrespondences -func NewListClientCorrespondencesRequest(server string, accountId string, params *ListClientCorrespondencesParams) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "accountId", runtime.ParamLocationPath, accountId) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/%s/client/correspondences.json", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - if params != nil { - queryValues := queryURL.Query() - - if params.Sort != "" { - - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "sort", runtime.ParamLocationQuery, params.Sort); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { - return nil, err - } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } - } - } - - } - - if params.Direction != "" { + if params.Page != 0 { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "direction", runtime.ParamLocationQuery, params.Direction); err != nil { + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "page", runtime.ParamLocationQuery, params.Page); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + queryURL.RawQuery = queryValues.Encode() + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewListPingablePeopleRequest generates requests for ListPingablePeople +func NewListPingablePeopleRequest(server string, accountId string) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "accountId", runtime.ParamLocationPath, accountId) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/%s/circles/people.json", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewListClientApprovalsRequest generates requests for ListClientApprovals +func NewListClientApprovalsRequest(server string, accountId string, params *ListClientApprovalsParams) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "accountId", runtime.ParamLocationPath, accountId) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/%s/client/approvals.json", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + queryValues := queryURL.Query() + + if params.Sort != "" { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "sort", runtime.ParamLocationQuery, params.Sort); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + if params.Direction != "" { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "direction", runtime.ParamLocationQuery, params.Direction); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + queryURL.RawQuery = queryValues.Encode() + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetClientApprovalRequest generates requests for GetClientApproval +func NewGetClientApprovalRequest(server string, accountId string, approvalId int64) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "accountId", runtime.ParamLocationPath, accountId) + if err != nil { + return nil, err + } + + var pathParam1 string + + pathParam1, err = runtime.StyleParamWithLocation("simple", false, "approvalId", runtime.ParamLocationPath, approvalId) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/%s/client/approvals/%s", pathParam0, pathParam1) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewListClientCorrespondencesRequest generates requests for ListClientCorrespondences +func NewListClientCorrespondencesRequest(server string, accountId string, params *ListClientCorrespondencesParams) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "accountId", runtime.ParamLocationPath, accountId) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/%s/client/correspondences.json", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + queryValues := queryURL.Query() + + if params.Sort != "" { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "sort", runtime.ParamLocationQuery, params.Sort); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + if params.Direction != "" { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "direction", runtime.ParamLocationQuery, params.Direction); err != nil { return nil, err } else if parsed, err := url.ParseQuery(queryFrag); err != nil { return nil, err @@ -10493,6 +10896,62 @@ func NewGetClientReplyRequest(server string, accountId string, recordingId int64 return req, nil } +// NewGetEverythingCommentsRequest generates requests for GetEverythingComments +func NewGetEverythingCommentsRequest(server string, accountId string, params *GetEverythingCommentsParams) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "accountId", runtime.ParamLocationPath, accountId) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/%s/comments.json", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + queryValues := queryURL.Query() + + if params.Page != 0 { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "page", runtime.ParamLocationQuery, params.Page); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + queryURL.RawQuery = queryValues.Encode() + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + // NewGetCommentRequest generates requests for GetComment func NewGetCommentRequest(server string, accountId string, commentId int64) (*http.Request, error) { var err error @@ -10819,6 +11278,150 @@ func NewUpdateDocumentRequestWithBody(server string, accountId string, documentI return req, nil } +// NewGetEverythingFilesRequest generates requests for GetEverythingFiles +func NewGetEverythingFilesRequest(server string, accountId string, params *GetEverythingFilesParams) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "accountId", runtime.ParamLocationPath, accountId) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/%s/files.json", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + queryValues := queryURL.Query() + + if params.Kind != "" { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "kind", runtime.ParamLocationQuery, params.Kind); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + if params.PeopleIds != nil && len(*params.PeopleIds) > 0 { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "people_ids[]", runtime.ParamLocationQuery, *params.PeopleIds); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + if params.Page != 0 { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "page", runtime.ParamLocationQuery, params.Page); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + queryURL.RawQuery = queryValues.Encode() + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetEverythingForwardsRequest generates requests for GetEverythingForwards +func NewGetEverythingForwardsRequest(server string, accountId string, params *GetEverythingForwardsParams) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "accountId", runtime.ParamLocationPath, accountId) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/%s/forwards.json", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + queryValues := queryURL.Query() + + if params.Page != 0 { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "page", runtime.ParamLocationQuery, params.Page); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + queryURL.RawQuery = queryValues.Encode() + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + // NewDestroyGaugeNeedleRequest generates requests for DestroyGaugeNeedle func NewDestroyGaugeNeedleRequest(server string, accountId string, needleId int64) (*http.Request, error) { var err error @@ -11609,6 +12212,62 @@ func NewCreateMessageRequestWithBody(server string, accountId string, boardId in return req, nil } +// NewGetEverythingMessagesRequest generates requests for GetEverythingMessages +func NewGetEverythingMessagesRequest(server string, accountId string, params *GetEverythingMessagesParams) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "accountId", runtime.ParamLocationPath, accountId) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/%s/messages.json", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + queryValues := queryURL.Query() + + if params.Page != 0 { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "page", runtime.ParamLocationQuery, params.Page); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + queryURL.RawQuery = queryValues.Encode() + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + // NewGetMessageRequest generates requests for GetMessage func NewGetMessageRequest(server string, accountId string, messageId int64) (*http.Request, error) { var err error @@ -16729,6 +17388,40 @@ func NewCreateTodoRequestWithBody(server string, accountId string, todolistId in return req, nil } +// NewGetEverythingOverdueTodosRequest generates requests for GetEverythingOverdueTodos +func NewGetEverythingOverdueTodosRequest(server string, accountId string) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "accountId", runtime.ParamLocationPath, accountId) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/%s/todos/overdue.json", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + // NewTrashTodoRequest generates requests for TrashTodo func NewTrashTodoRequest(server string, accountId string, todoId int64) (*http.Request, error) { var err error @@ -17991,6 +18684,7 @@ var operationMetadata = map[string]OperationMetadata{ "UpdateAccountLogo": {Idempotent: true, HasSensitiveParams: false}, "UpdateAccountName": {Idempotent: true, HasSensitiveParams: false}, "CreateAttachment": {Idempotent: false, HasSensitiveParams: false}, + "GetEverythingBoosts": {Idempotent: true, HasSensitiveParams: false}, "DeleteBoost": {Idempotent: true, HasSensitiveParams: false}, "GetBoost": {Idempotent: true, HasSensitiveParams: false}, "SetCardColumnColor": {Idempotent: true, HasSensitiveParams: false}, @@ -18019,6 +18713,7 @@ var operationMetadata = map[string]OperationMetadata{ "GetCardTable": {Idempotent: true, HasSensitiveParams: false}, "CreateCardColumn": {Idempotent: false, HasSensitiveParams: false}, "MoveCardColumn": {Idempotent: false, HasSensitiveParams: false}, + "GetEverythingOverdueCards": {Idempotent: true, HasSensitiveParams: false}, "ListMessageTypes": {Idempotent: true, HasSensitiveParams: false}, "CreateMessageType": {Idempotent: false, HasSensitiveParams: false}, "DeleteMessageType": {Idempotent: true, HasSensitiveParams: false}, @@ -18038,6 +18733,7 @@ var operationMetadata = map[string]OperationMetadata{ "UpdateCampfireLine": {Idempotent: true, HasSensitiveParams: false}, "ListCampfireUploads": {Idempotent: true, HasSensitiveParams: false}, "CreateCampfireUpload": {Idempotent: false, HasSensitiveParams: false}, + "GetEverythingCheckins": {Idempotent: true, HasSensitiveParams: false}, "ListPingablePeople": {Idempotent: true, HasSensitiveParams: false}, "ListClientApprovals": {Idempotent: true, HasSensitiveParams: false}, "GetClientApproval": {Idempotent: true, HasSensitiveParams: false}, @@ -18045,6 +18741,7 @@ var operationMetadata = map[string]OperationMetadata{ "GetClientCorrespondence": {Idempotent: true, HasSensitiveParams: false}, "ListClientReplies": {Idempotent: true, HasSensitiveParams: false}, "GetClientReply": {Idempotent: true, HasSensitiveParams: false}, + "GetEverythingComments": {Idempotent: true, HasSensitiveParams: false}, "GetComment": {Idempotent: true, HasSensitiveParams: false}, "UpdateComment": {Idempotent: true, HasSensitiveParams: false}, "DeleteTool": {Idempotent: true, HasSensitiveParams: false}, @@ -18052,6 +18749,8 @@ var operationMetadata = map[string]OperationMetadata{ "UpdateTool": {Idempotent: true, HasSensitiveParams: false}, "GetDocument": {Idempotent: true, HasSensitiveParams: false}, "UpdateDocument": {Idempotent: true, HasSensitiveParams: false}, + "GetEverythingFiles": {Idempotent: true, HasSensitiveParams: false}, + "GetEverythingForwards": {Idempotent: true, HasSensitiveParams: false}, "DestroyGaugeNeedle": {Idempotent: true, HasSensitiveParams: false}, "GetGaugeNeedle": {Idempotent: true, HasSensitiveParams: false}, "UpdateGaugeNeedle": {Idempotent: true, HasSensitiveParams: false}, @@ -18068,6 +18767,7 @@ var operationMetadata = map[string]OperationMetadata{ "GetMessageBoard": {Idempotent: true, HasSensitiveParams: false}, "ListMessages": {Idempotent: true, HasSensitiveParams: false}, "CreateMessage": {Idempotent: false, HasSensitiveParams: false}, + "GetEverythingMessages": {Idempotent: true, HasSensitiveParams: false}, "GetMessage": {Idempotent: true, HasSensitiveParams: false}, "UpdateMessage": {Idempotent: true, HasSensitiveParams: false}, "GetMyAssignments": {Idempotent: true, HasSensitiveParams: false}, @@ -18169,6 +18869,7 @@ var operationMetadata = map[string]OperationMetadata{ "CreateTodolistGroup": {Idempotent: false, HasSensitiveParams: false}, "ListTodos": {Idempotent: true, HasSensitiveParams: false}, "CreateTodo": {Idempotent: false, HasSensitiveParams: false}, + "GetEverythingOverdueTodos": {Idempotent: true, HasSensitiveParams: false}, "TrashTodo": {Idempotent: true, HasSensitiveParams: false}, "GetTodo": {Idempotent: true, HasSensitiveParams: false}, "ReplaceTodo": {Idempotent: true, HasSensitiveParams: false}, @@ -19147,6 +19848,9 @@ type ClientWithResponsesInterface interface { // CreateAttachmentWithBodyWithResponse request with any body CreateAttachmentWithBodyWithResponse(ctx context.Context, accountId string, params *CreateAttachmentParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateAttachmentResponse, error) + // GetEverythingBoostsWithResponse request + GetEverythingBoostsWithResponse(ctx context.Context, accountId string, params *GetEverythingBoostsParams, reqEditors ...RequestEditorFn) (*GetEverythingBoostsResponse, error) + // DeleteBoostWithResponse request DeleteBoostWithResponse(ctx context.Context, accountId string, boostId int64, reqEditors ...RequestEditorFn) (*DeleteBoostResponse, error) @@ -19261,6 +19965,9 @@ type ClientWithResponsesInterface interface { MoveCardColumnWithResponse(ctx context.Context, accountId string, cardTableId int64, body MoveCardColumnJSONRequestBody, reqEditors ...RequestEditorFn) (*MoveCardColumnResponse, error) + // GetEverythingOverdueCardsWithResponse request + GetEverythingOverdueCardsWithResponse(ctx context.Context, accountId string, reqEditors ...RequestEditorFn) (*GetEverythingOverdueCardsResponse, error) + // ListMessageTypesWithResponse request ListMessageTypesWithResponse(ctx context.Context, accountId string, reqEditors ...RequestEditorFn) (*ListMessageTypesResponse, error) @@ -19330,6 +20037,9 @@ type ClientWithResponsesInterface interface { // CreateCampfireUploadWithBodyWithResponse request with any body CreateCampfireUploadWithBodyWithResponse(ctx context.Context, accountId string, campfireId int64, params *CreateCampfireUploadParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateCampfireUploadResponse, error) + // GetEverythingCheckinsWithResponse request + GetEverythingCheckinsWithResponse(ctx context.Context, accountId string, params *GetEverythingCheckinsParams, reqEditors ...RequestEditorFn) (*GetEverythingCheckinsResponse, error) + // ListPingablePeopleWithResponse request ListPingablePeopleWithResponse(ctx context.Context, accountId string, reqEditors ...RequestEditorFn) (*ListPingablePeopleResponse, error) @@ -19351,6 +20061,9 @@ type ClientWithResponsesInterface interface { // GetClientReplyWithResponse request GetClientReplyWithResponse(ctx context.Context, accountId string, recordingId int64, replyId int64, reqEditors ...RequestEditorFn) (*GetClientReplyResponse, error) + // GetEverythingCommentsWithResponse request + GetEverythingCommentsWithResponse(ctx context.Context, accountId string, params *GetEverythingCommentsParams, reqEditors ...RequestEditorFn) (*GetEverythingCommentsResponse, error) + // GetCommentWithResponse request GetCommentWithResponse(ctx context.Context, accountId string, commentId int64, reqEditors ...RequestEditorFn) (*GetCommentResponse, error) @@ -19378,6 +20091,12 @@ type ClientWithResponsesInterface interface { UpdateDocumentWithResponse(ctx context.Context, accountId string, documentId int64, body UpdateDocumentJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateDocumentResponse, error) + // GetEverythingFilesWithResponse request + GetEverythingFilesWithResponse(ctx context.Context, accountId string, params *GetEverythingFilesParams, reqEditors ...RequestEditorFn) (*GetEverythingFilesResponse, error) + + // GetEverythingForwardsWithResponse request + GetEverythingForwardsWithResponse(ctx context.Context, accountId string, params *GetEverythingForwardsParams, reqEditors ...RequestEditorFn) (*GetEverythingForwardsResponse, error) + // DestroyGaugeNeedleWithResponse request DestroyGaugeNeedleWithResponse(ctx context.Context, accountId string, needleId int64, reqEditors ...RequestEditorFn) (*DestroyGaugeNeedleResponse, error) @@ -19436,6 +20155,9 @@ type ClientWithResponsesInterface interface { CreateMessageWithResponse(ctx context.Context, accountId string, boardId int64, body CreateMessageJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateMessageResponse, error) + // GetEverythingMessagesWithResponse request + GetEverythingMessagesWithResponse(ctx context.Context, accountId string, params *GetEverythingMessagesParams, reqEditors ...RequestEditorFn) (*GetEverythingMessagesResponse, error) + // GetMessageWithResponse request GetMessageWithResponse(ctx context.Context, accountId string, messageId int64, reqEditors ...RequestEditorFn) (*GetMessageResponse, error) @@ -19805,6 +20527,9 @@ type ClientWithResponsesInterface interface { CreateTodoWithResponse(ctx context.Context, accountId string, todolistId int64, body CreateTodoJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateTodoResponse, error) + // GetEverythingOverdueTodosWithResponse request + GetEverythingOverdueTodosWithResponse(ctx context.Context, accountId string, reqEditors ...RequestEditorFn) (*GetEverythingOverdueTodosResponse, error) + // TrashTodoWithResponse request TrashTodoWithResponse(ctx context.Context, accountId string, todoId int64, reqEditors ...RequestEditorFn) (*TrashTodoResponse, error) @@ -20076,6 +20801,40 @@ func (r CreateAttachmentResponse) ContentType() string { return "" } +type GetEverythingBoostsResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *GetEverythingBoostsResponseContent + JSON401 *UnauthorizedErrorResponseContent + JSON403 *ForbiddenErrorResponseContent + JSON429 *RateLimitErrorResponseContent + JSON500 *InternalServerErrorResponseContent +} + +// Status returns HTTPResponse.Status +func (r GetEverythingBoostsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetEverythingBoostsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetEverythingBoostsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type DeleteBoostResponse struct { Body []byte HTTPResponse *http.Response @@ -21042,6 +21801,39 @@ func (r MoveCardColumnResponse) ContentType() string { return "" } +type GetEverythingOverdueCardsResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *GetEverythingOverdueCardsResponseContent + JSON401 *UnauthorizedErrorResponseContent + JSON403 *ForbiddenErrorResponseContent + JSON500 *InternalServerErrorResponseContent +} + +// Status returns HTTPResponse.Status +func (r GetEverythingOverdueCardsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetEverythingOverdueCardsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetEverythingOverdueCardsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type ListMessageTypesResponse struct { Body []byte HTTPResponse *http.Response @@ -21692,6 +22484,40 @@ func (r CreateCampfireUploadResponse) ContentType() string { return "" } +type GetEverythingCheckinsResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *GetEverythingCheckinsResponseContent + JSON401 *UnauthorizedErrorResponseContent + JSON403 *ForbiddenErrorResponseContent + JSON429 *RateLimitErrorResponseContent + JSON500 *InternalServerErrorResponseContent +} + +// Status returns HTTPResponse.Status +func (r GetEverythingCheckinsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetEverythingCheckinsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetEverythingCheckinsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type ListPingablePeopleResponse struct { Body []byte HTTPResponse *http.Response @@ -21930,6 +22756,40 @@ func (r GetClientReplyResponse) ContentType() string { return "" } +type GetEverythingCommentsResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *GetEverythingCommentsResponseContent + JSON401 *UnauthorizedErrorResponseContent + JSON403 *ForbiddenErrorResponseContent + JSON429 *RateLimitErrorResponseContent + JSON500 *InternalServerErrorResponseContent +} + +// Status returns HTTPResponse.Status +func (r GetEverythingCommentsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetEverythingCommentsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetEverythingCommentsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type GetCommentResponse struct { Body []byte HTTPResponse *http.Response @@ -22170,6 +23030,74 @@ func (r UpdateDocumentResponse) ContentType() string { return "" } +type GetEverythingFilesResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *GetEverythingFilesResponseContent + JSON401 *UnauthorizedErrorResponseContent + JSON403 *ForbiddenErrorResponseContent + JSON429 *RateLimitErrorResponseContent + JSON500 *InternalServerErrorResponseContent +} + +// Status returns HTTPResponse.Status +func (r GetEverythingFilesResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetEverythingFilesResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetEverythingFilesResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetEverythingForwardsResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *GetEverythingForwardsResponseContent + JSON401 *UnauthorizedErrorResponseContent + JSON403 *ForbiddenErrorResponseContent + JSON429 *RateLimitErrorResponseContent + JSON500 *InternalServerErrorResponseContent +} + +// Status returns HTTPResponse.Status +func (r GetEverythingForwardsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetEverythingForwardsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetEverythingForwardsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type DestroyGaugeNeedleResponse struct { Body []byte HTTPResponse *http.Response @@ -22717,6 +23645,40 @@ func (r CreateMessageResponse) ContentType() string { return "" } +type GetEverythingMessagesResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *GetEverythingMessagesResponseContent + JSON401 *UnauthorizedErrorResponseContent + JSON403 *ForbiddenErrorResponseContent + JSON429 *RateLimitErrorResponseContent + JSON500 *InternalServerErrorResponseContent +} + +// Status returns HTTPResponse.Status +func (r GetEverythingMessagesResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetEverythingMessagesResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetEverythingMessagesResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type GetMessageResponse struct { Body []byte HTTPResponse *http.Response @@ -26174,6 +27136,39 @@ func (r CreateTodoResponse) ContentType() string { return "" } +type GetEverythingOverdueTodosResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *GetEverythingOverdueTodosResponseContent + JSON401 *UnauthorizedErrorResponseContent + JSON403 *ForbiddenErrorResponseContent + JSON500 *InternalServerErrorResponseContent +} + +// Status returns HTTPResponse.Status +func (r GetEverythingOverdueTodosResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetEverythingOverdueTodosResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetEverythingOverdueTodosResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type TrashTodoResponse struct { Body []byte HTTPResponse *http.Response @@ -27118,6 +28113,15 @@ func (c *ClientWithResponses) CreateAttachmentWithBodyWithResponse(ctx context.C return ParseCreateAttachmentResponse(rsp) } +// GetEverythingBoostsWithResponse request returning *GetEverythingBoostsResponse +func (c *ClientWithResponses) GetEverythingBoostsWithResponse(ctx context.Context, accountId string, params *GetEverythingBoostsParams, reqEditors ...RequestEditorFn) (*GetEverythingBoostsResponse, error) { + rsp, err := c.GetEverythingBoosts(ctx, accountId, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetEverythingBoostsResponse(rsp) +} + // DeleteBoostWithResponse request returning *DeleteBoostResponse func (c *ClientWithResponses) DeleteBoostWithResponse(ctx context.Context, accountId string, boostId int64, reqEditors ...RequestEditorFn) (*DeleteBoostResponse, error) { rsp, err := c.DeleteBoost(ctx, accountId, boostId, reqEditors...) @@ -27490,6 +28494,15 @@ func (c *ClientWithResponses) MoveCardColumnWithResponse(ctx context.Context, ac return ParseMoveCardColumnResponse(rsp) } +// GetEverythingOverdueCardsWithResponse request returning *GetEverythingOverdueCardsResponse +func (c *ClientWithResponses) GetEverythingOverdueCardsWithResponse(ctx context.Context, accountId string, reqEditors ...RequestEditorFn) (*GetEverythingOverdueCardsResponse, error) { + rsp, err := c.GetEverythingOverdueCards(ctx, accountId, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetEverythingOverdueCardsResponse(rsp) +} + // ListMessageTypesWithResponse request returning *ListMessageTypesResponse func (c *ClientWithResponses) ListMessageTypesWithResponse(ctx context.Context, accountId string, reqEditors ...RequestEditorFn) (*ListMessageTypesResponse, error) { rsp, err := c.ListMessageTypes(ctx, accountId, reqEditors...) @@ -27709,6 +28722,15 @@ func (c *ClientWithResponses) CreateCampfireUploadWithBodyWithResponse(ctx conte return ParseCreateCampfireUploadResponse(rsp) } +// GetEverythingCheckinsWithResponse request returning *GetEverythingCheckinsResponse +func (c *ClientWithResponses) GetEverythingCheckinsWithResponse(ctx context.Context, accountId string, params *GetEverythingCheckinsParams, reqEditors ...RequestEditorFn) (*GetEverythingCheckinsResponse, error) { + rsp, err := c.GetEverythingCheckins(ctx, accountId, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetEverythingCheckinsResponse(rsp) +} + // ListPingablePeopleWithResponse request returning *ListPingablePeopleResponse func (c *ClientWithResponses) ListPingablePeopleWithResponse(ctx context.Context, accountId string, reqEditors ...RequestEditorFn) (*ListPingablePeopleResponse, error) { rsp, err := c.ListPingablePeople(ctx, accountId, reqEditors...) @@ -27772,6 +28794,15 @@ func (c *ClientWithResponses) GetClientReplyWithResponse(ctx context.Context, ac return ParseGetClientReplyResponse(rsp) } +// GetEverythingCommentsWithResponse request returning *GetEverythingCommentsResponse +func (c *ClientWithResponses) GetEverythingCommentsWithResponse(ctx context.Context, accountId string, params *GetEverythingCommentsParams, reqEditors ...RequestEditorFn) (*GetEverythingCommentsResponse, error) { + rsp, err := c.GetEverythingComments(ctx, accountId, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetEverythingCommentsResponse(rsp) +} + // GetCommentWithResponse request returning *GetCommentResponse func (c *ClientWithResponses) GetCommentWithResponse(ctx context.Context, accountId string, commentId int64, reqEditors ...RequestEditorFn) (*GetCommentResponse, error) { rsp, err := c.GetComment(ctx, accountId, commentId, reqEditors...) @@ -27859,6 +28890,24 @@ func (c *ClientWithResponses) UpdateDocumentWithResponse(ctx context.Context, ac return ParseUpdateDocumentResponse(rsp) } +// GetEverythingFilesWithResponse request returning *GetEverythingFilesResponse +func (c *ClientWithResponses) GetEverythingFilesWithResponse(ctx context.Context, accountId string, params *GetEverythingFilesParams, reqEditors ...RequestEditorFn) (*GetEverythingFilesResponse, error) { + rsp, err := c.GetEverythingFiles(ctx, accountId, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetEverythingFilesResponse(rsp) +} + +// GetEverythingForwardsWithResponse request returning *GetEverythingForwardsResponse +func (c *ClientWithResponses) GetEverythingForwardsWithResponse(ctx context.Context, accountId string, params *GetEverythingForwardsParams, reqEditors ...RequestEditorFn) (*GetEverythingForwardsResponse, error) { + rsp, err := c.GetEverythingForwards(ctx, accountId, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetEverythingForwardsResponse(rsp) +} + // DestroyGaugeNeedleWithResponse request returning *DestroyGaugeNeedleResponse func (c *ClientWithResponses) DestroyGaugeNeedleWithResponse(ctx context.Context, accountId string, needleId int64, reqEditors ...RequestEditorFn) (*DestroyGaugeNeedleResponse, error) { rsp, err := c.DestroyGaugeNeedle(ctx, accountId, needleId, reqEditors...) @@ -28043,6 +29092,15 @@ func (c *ClientWithResponses) CreateMessageWithResponse(ctx context.Context, acc return ParseCreateMessageResponse(rsp) } +// GetEverythingMessagesWithResponse request returning *GetEverythingMessagesResponse +func (c *ClientWithResponses) GetEverythingMessagesWithResponse(ctx context.Context, accountId string, params *GetEverythingMessagesParams, reqEditors ...RequestEditorFn) (*GetEverythingMessagesResponse, error) { + rsp, err := c.GetEverythingMessages(ctx, accountId, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetEverythingMessagesResponse(rsp) +} + // GetMessageWithResponse request returning *GetMessageResponse func (c *ClientWithResponses) GetMessageWithResponse(ctx context.Context, accountId string, messageId int64, reqEditors ...RequestEditorFn) (*GetMessageResponse, error) { rsp, err := c.GetMessage(ctx, accountId, messageId, reqEditors...) @@ -29216,6 +30274,15 @@ func (c *ClientWithResponses) CreateTodoWithResponse(ctx context.Context, accoun return ParseCreateTodoResponse(rsp) } +// GetEverythingOverdueTodosWithResponse request returning *GetEverythingOverdueTodosResponse +func (c *ClientWithResponses) GetEverythingOverdueTodosWithResponse(ctx context.Context, accountId string, reqEditors ...RequestEditorFn) (*GetEverythingOverdueTodosResponse, error) { + rsp, err := c.GetEverythingOverdueTodos(ctx, accountId, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetEverythingOverdueTodosResponse(rsp) +} + // TrashTodoWithResponse request returning *TrashTodoResponse func (c *ClientWithResponses) TrashTodoWithResponse(ctx context.Context, accountId string, todoId int64, reqEditors ...RequestEditorFn) (*TrashTodoResponse, error) { rsp, err := c.TrashTodo(ctx, accountId, todoId, reqEditors...) @@ -29635,15 +30702,248 @@ func ParseRemoveAccountLogoResponse(rsp *http.Response) (*RemoveAccountLogoRespo return response, nil } -// ParseUpdateAccountLogoResponse parses an HTTP response from a UpdateAccountLogoWithResponse call -func ParseUpdateAccountLogoResponse(rsp *http.Response) (*UpdateAccountLogoResponse, error) { +// ParseUpdateAccountLogoResponse parses an HTTP response from a UpdateAccountLogoWithResponse call +func ParseUpdateAccountLogoResponse(rsp *http.Response) (*UpdateAccountLogoResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &UpdateAccountLogoResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case rsp.StatusCode == 204: + break // No content-type + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest UnauthorizedErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest ForbiddenErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: + var dest ValidationErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON422 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: + var dest RateLimitErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON429 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest InternalServerErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseUpdateAccountNameResponse parses an HTTP response from a UpdateAccountNameWithResponse call +func ParseUpdateAccountNameResponse(rsp *http.Response) (*UpdateAccountNameResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &UpdateAccountNameResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest UpdateAccountNameResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest BadRequestErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest UnauthorizedErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest ForbiddenErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: + var dest RateLimitErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON429 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest InternalServerErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseCreateAttachmentResponse parses an HTTP response from a CreateAttachmentWithResponse call +func ParseCreateAttachmentResponse(rsp *http.Response) (*CreateAttachmentResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &CreateAttachmentResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: + var dest CreateAttachmentResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON201 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest UnauthorizedErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest ForbiddenErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: + var dest ValidationErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON422 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: + var dest RateLimitErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON429 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest InternalServerErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseGetEverythingBoostsResponse parses an HTTP response from a GetEverythingBoostsWithResponse call +func ParseGetEverythingBoostsResponse(rsp *http.Response) (*GetEverythingBoostsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetEverythingBoostsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest GetEverythingBoostsResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest UnauthorizedErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest ForbiddenErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: + var dest RateLimitErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON429 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest InternalServerErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseDeleteBoostResponse parses an HTTP response from a DeleteBoostWithResponse call +func ParseDeleteBoostResponse(rsp *http.Response) (*DeleteBoostResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &UpdateAccountLogoResponse{ + response := &DeleteBoostResponse{ Body: bodyBytes, HTTPResponse: rsp, } @@ -29666,6 +30966,121 @@ func ParseUpdateAccountLogoResponse(rsp *http.Response) (*UpdateAccountLogoRespo } response.JSON403 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest NotFoundErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest InternalServerErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseGetBoostResponse parses an HTTP response from a GetBoostWithResponse call +func ParseGetBoostResponse(rsp *http.Response) (*GetBoostResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetBoostResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest GetBoostResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest UnauthorizedErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest ForbiddenErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest NotFoundErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest InternalServerErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseSetCardColumnColorResponse parses an HTTP response from a SetCardColumnColorWithResponse call +func ParseSetCardColumnColorResponse(rsp *http.Response) (*SetCardColumnColorResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &SetCardColumnColorResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest SetCardColumnColorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest UnauthorizedErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest ForbiddenErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest NotFoundErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: var dest ValidationErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { @@ -29692,34 +31107,27 @@ func ParseUpdateAccountLogoResponse(rsp *http.Response) (*UpdateAccountLogoRespo return response, nil } -// ParseUpdateAccountNameResponse parses an HTTP response from a UpdateAccountNameWithResponse call -func ParseUpdateAccountNameResponse(rsp *http.Response) (*UpdateAccountNameResponse, error) { +// ParseDisableCardColumnOnHoldResponse parses an HTTP response from a DisableCardColumnOnHoldWithResponse call +func ParseDisableCardColumnOnHoldResponse(rsp *http.Response) (*DisableCardColumnOnHoldResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &UpdateAccountNameResponse{ + response := &DisableCardColumnOnHoldResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest UpdateAccountNameResponseContent + var dest DisableCardColumnOnHoldResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON200 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: - var dest BadRequestErrorResponseContent - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON400 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: var dest UnauthorizedErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { @@ -29734,6 +31142,13 @@ func ParseUpdateAccountNameResponse(rsp *http.Response) (*UpdateAccountNameRespo } response.JSON403 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest NotFoundErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: var dest RateLimitErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { @@ -29753,26 +31168,26 @@ func ParseUpdateAccountNameResponse(rsp *http.Response) (*UpdateAccountNameRespo return response, nil } -// ParseCreateAttachmentResponse parses an HTTP response from a CreateAttachmentWithResponse call -func ParseCreateAttachmentResponse(rsp *http.Response) (*CreateAttachmentResponse, error) { +// ParseEnableCardColumnOnHoldResponse parses an HTTP response from a EnableCardColumnOnHoldWithResponse call +func ParseEnableCardColumnOnHoldResponse(rsp *http.Response) (*EnableCardColumnOnHoldResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &CreateAttachmentResponse{ + response := &EnableCardColumnOnHoldResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest CreateAttachmentResponseContent + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest EnableCardColumnOnHoldResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON201 = &dest + response.JSON200 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: var dest UnauthorizedErrorResponseContent @@ -29814,15 +31229,15 @@ func ParseCreateAttachmentResponse(rsp *http.Response) (*CreateAttachmentRespons return response, nil } -// ParseDeleteBoostResponse parses an HTTP response from a DeleteBoostWithResponse call -func ParseDeleteBoostResponse(rsp *http.Response) (*DeleteBoostResponse, error) { +// ParseDeleteWormholeResponse parses an HTTP response from a DeleteWormholeWithResponse call +func ParseDeleteWormholeResponse(rsp *http.Response) (*DeleteWormholeResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &DeleteBoostResponse{ + response := &DeleteWormholeResponse{ Body: bodyBytes, HTTPResponse: rsp, } @@ -29852,6 +31267,13 @@ func ParseDeleteBoostResponse(rsp *http.Response) (*DeleteBoostResponse, error) } response.JSON404 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: + var dest RateLimitErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON429 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: var dest InternalServerErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { @@ -29864,22 +31286,22 @@ func ParseDeleteBoostResponse(rsp *http.Response) (*DeleteBoostResponse, error) return response, nil } -// ParseGetBoostResponse parses an HTTP response from a GetBoostWithResponse call -func ParseGetBoostResponse(rsp *http.Response) (*GetBoostResponse, error) { +// ParseUpdateWormholeResponse parses an HTTP response from a UpdateWormholeWithResponse call +func ParseUpdateWormholeResponse(rsp *http.Response) (*UpdateWormholeResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &GetBoostResponse{ + response := &UpdateWormholeResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest GetBoostResponseContent + var dest UpdateWormholeResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -29918,26 +31340,26 @@ func ParseGetBoostResponse(rsp *http.Response) (*GetBoostResponse, error) { return response, nil } -// ParseSetCardColumnColorResponse parses an HTTP response from a SetCardColumnColorWithResponse call -func ParseSetCardColumnColorResponse(rsp *http.Response) (*SetCardColumnColorResponse, error) { +// ParseCreateWormholeResponse parses an HTTP response from a CreateWormholeWithResponse call +func ParseCreateWormholeResponse(rsp *http.Response) (*CreateWormholeResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &SetCardColumnColorResponse{ + response := &CreateWormholeResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest SetCardColumnColorResponseContent + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: + var dest CreateWormholeResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON200 = &dest + response.JSON201 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: var dest UnauthorizedErrorResponseContent @@ -29986,26 +31408,26 @@ func ParseSetCardColumnColorResponse(rsp *http.Response) (*SetCardColumnColorRes return response, nil } -// ParseDisableCardColumnOnHoldResponse parses an HTTP response from a DisableCardColumnOnHoldWithResponse call -func ParseDisableCardColumnOnHoldResponse(rsp *http.Response) (*DisableCardColumnOnHoldResponse, error) { +// ParseCreateToolResponse parses an HTTP response from a CreateToolWithResponse call +func ParseCreateToolResponse(rsp *http.Response) (*CreateToolResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &DisableCardColumnOnHoldResponse{ + response := &CreateToolResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest DisableCardColumnOnHoldResponseContent + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: + var dest CreateToolResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON200 = &dest + response.JSON201 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: var dest UnauthorizedErrorResponseContent @@ -30021,12 +31443,12 @@ func ParseDisableCardColumnOnHoldResponse(rsp *http.Response) (*DisableCardColum } response.JSON403 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: - var dest NotFoundErrorResponseContent + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: + var dest ValidationErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON404 = &dest + response.JSON422 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: var dest RateLimitErrorResponseContent @@ -30047,22 +31469,22 @@ func ParseDisableCardColumnOnHoldResponse(rsp *http.Response) (*DisableCardColum return response, nil } -// ParseEnableCardColumnOnHoldResponse parses an HTTP response from a EnableCardColumnOnHoldWithResponse call -func ParseEnableCardColumnOnHoldResponse(rsp *http.Response) (*EnableCardColumnOnHoldResponse, error) { +// ParseListWebhooksResponse parses an HTTP response from a ListWebhooksWithResponse call +func ParseListWebhooksResponse(rsp *http.Response) (*ListWebhooksResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &EnableCardColumnOnHoldResponse{ + response := &ListWebhooksResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest EnableCardColumnOnHoldResponseContent + var dest ListWebhooksResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -30082,13 +31504,6 @@ func ParseEnableCardColumnOnHoldResponse(rsp *http.Response) (*EnableCardColumnO } response.JSON403 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: - var dest ValidationErrorResponseContent - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON422 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: var dest RateLimitErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { @@ -30108,22 +31523,33 @@ func ParseEnableCardColumnOnHoldResponse(rsp *http.Response) (*EnableCardColumnO return response, nil } -// ParseDeleteWormholeResponse parses an HTTP response from a DeleteWormholeWithResponse call -func ParseDeleteWormholeResponse(rsp *http.Response) (*DeleteWormholeResponse, error) { +// ParseCreateWebhookResponse parses an HTTP response from a CreateWebhookWithResponse call +func ParseCreateWebhookResponse(rsp *http.Response) (*CreateWebhookResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &DeleteWormholeResponse{ + response := &CreateWebhookResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { - case rsp.StatusCode == 204: - break // No content-type + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: + var dest CreateWebhookResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON201 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest BadRequestErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: var dest UnauthorizedErrorResponseContent @@ -30139,13 +31565,6 @@ func ParseDeleteWormholeResponse(rsp *http.Response) (*DeleteWormholeResponse, e } response.JSON403 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: - var dest NotFoundErrorResponseContent - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON404 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: var dest RateLimitErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { @@ -30160,27 +31579,34 @@ func ParseDeleteWormholeResponse(rsp *http.Response) (*DeleteWormholeResponse, e } response.JSON500 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 507: + var dest WebhookLimitErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON507 = &dest + } return response, nil } -// ParseUpdateWormholeResponse parses an HTTP response from a UpdateWormholeWithResponse call -func ParseUpdateWormholeResponse(rsp *http.Response) (*UpdateWormholeResponse, error) { +// ParseGetCardResponse parses an HTTP response from a GetCardWithResponse call +func ParseGetCardResponse(rsp *http.Response) (*GetCardResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &UpdateWormholeResponse{ + response := &GetCardResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest UpdateWormholeResponseContent + var dest GetCardResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -30219,26 +31645,26 @@ func ParseUpdateWormholeResponse(rsp *http.Response) (*UpdateWormholeResponse, e return response, nil } -// ParseCreateWormholeResponse parses an HTTP response from a CreateWormholeWithResponse call -func ParseCreateWormholeResponse(rsp *http.Response) (*CreateWormholeResponse, error) { +// ParseUpdateCardResponse parses an HTTP response from a UpdateCardWithResponse call +func ParseUpdateCardResponse(rsp *http.Response) (*UpdateCardResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &CreateWormholeResponse{ + response := &UpdateCardResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest CreateWormholeResponseContent + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest UpdateCardResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON201 = &dest + response.JSON200 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: var dest UnauthorizedErrorResponseContent @@ -30268,13 +31694,6 @@ func ParseCreateWormholeResponse(rsp *http.Response) (*CreateWormholeResponse, e } response.JSON422 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: - var dest RateLimitErrorResponseContent - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON429 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: var dest InternalServerErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { @@ -30287,26 +31706,22 @@ func ParseCreateWormholeResponse(rsp *http.Response) (*CreateWormholeResponse, e return response, nil } -// ParseCreateToolResponse parses an HTTP response from a CreateToolWithResponse call -func ParseCreateToolResponse(rsp *http.Response) (*CreateToolResponse, error) { +// ParseMoveCardResponse parses an HTTP response from a MoveCardWithResponse call +func ParseMoveCardResponse(rsp *http.Response) (*MoveCardResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &CreateToolResponse{ + response := &MoveCardResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest CreateToolResponseContent - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON201 = &dest + case rsp.StatusCode == 204: + break // No content-type case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: var dest UnauthorizedErrorResponseContent @@ -30348,26 +31763,22 @@ func ParseCreateToolResponse(rsp *http.Response) (*CreateToolResponse, error) { return response, nil } -// ParseListWebhooksResponse parses an HTTP response from a ListWebhooksWithResponse call -func ParseListWebhooksResponse(rsp *http.Response) (*ListWebhooksResponse, error) { +// ParseRepositionCardStepResponse parses an HTTP response from a RepositionCardStepWithResponse call +func ParseRepositionCardStepResponse(rsp *http.Response) (*RepositionCardStepResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &ListWebhooksResponse{ + response := &RepositionCardStepResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest ListWebhooksResponseContent - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest + case rsp.StatusCode == 200: + break // No content-type case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: var dest UnauthorizedErrorResponseContent @@ -30383,6 +31794,13 @@ func ParseListWebhooksResponse(rsp *http.Response) (*ListWebhooksResponse, error } response.JSON403 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: + var dest ValidationErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON422 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: var dest RateLimitErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { @@ -30402,34 +31820,27 @@ func ParseListWebhooksResponse(rsp *http.Response) (*ListWebhooksResponse, error return response, nil } -// ParseCreateWebhookResponse parses an HTTP response from a CreateWebhookWithResponse call -func ParseCreateWebhookResponse(rsp *http.Response) (*CreateWebhookResponse, error) { +// ParseCreateCardStepResponse parses an HTTP response from a CreateCardStepWithResponse call +func ParseCreateCardStepResponse(rsp *http.Response) (*CreateCardStepResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &CreateWebhookResponse{ + response := &CreateCardStepResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest CreateWebhookResponseContent + var dest CreateCardStepResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON201 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: - var dest BadRequestErrorResponseContent - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON400 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: var dest UnauthorizedErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { @@ -30444,6 +31855,13 @@ func ParseCreateWebhookResponse(rsp *http.Response) (*CreateWebhookResponse, err } response.JSON403 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: + var dest ValidationErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON422 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: var dest RateLimitErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { @@ -30458,34 +31876,27 @@ func ParseCreateWebhookResponse(rsp *http.Response) (*CreateWebhookResponse, err } response.JSON500 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 507: - var dest WebhookLimitErrorResponseContent - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON507 = &dest - } return response, nil } -// ParseGetCardResponse parses an HTTP response from a GetCardWithResponse call -func ParseGetCardResponse(rsp *http.Response) (*GetCardResponse, error) { +// ParseGetCardColumnResponse parses an HTTP response from a GetCardColumnWithResponse call +func ParseGetCardColumnResponse(rsp *http.Response) (*GetCardColumnResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &GetCardResponse{ + response := &GetCardColumnResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest GetCardResponseContent + var dest GetCardColumnResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -30524,22 +31935,22 @@ func ParseGetCardResponse(rsp *http.Response) (*GetCardResponse, error) { return response, nil } -// ParseUpdateCardResponse parses an HTTP response from a UpdateCardWithResponse call -func ParseUpdateCardResponse(rsp *http.Response) (*UpdateCardResponse, error) { +// ParseUpdateCardColumnResponse parses an HTTP response from a UpdateCardColumnWithResponse call +func ParseUpdateCardColumnResponse(rsp *http.Response) (*UpdateCardColumnResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &UpdateCardResponse{ + response := &UpdateCardColumnResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest UpdateCardResponseContent + var dest UpdateCardColumnResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -30585,22 +31996,26 @@ func ParseUpdateCardResponse(rsp *http.Response) (*UpdateCardResponse, error) { return response, nil } -// ParseMoveCardResponse parses an HTTP response from a MoveCardWithResponse call -func ParseMoveCardResponse(rsp *http.Response) (*MoveCardResponse, error) { +// ParseListCardsResponse parses an HTTP response from a ListCardsWithResponse call +func ParseListCardsResponse(rsp *http.Response) (*ListCardsResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &MoveCardResponse{ + response := &ListCardsResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { - case rsp.StatusCode == 204: - break // No content-type + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ListCardsResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: var dest UnauthorizedErrorResponseContent @@ -30616,13 +32031,6 @@ func ParseMoveCardResponse(rsp *http.Response) (*MoveCardResponse, error) { } response.JSON403 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: - var dest ValidationErrorResponseContent - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON422 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: var dest RateLimitErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { @@ -30642,22 +32050,26 @@ func ParseMoveCardResponse(rsp *http.Response) (*MoveCardResponse, error) { return response, nil } -// ParseRepositionCardStepResponse parses an HTTP response from a RepositionCardStepWithResponse call -func ParseRepositionCardStepResponse(rsp *http.Response) (*RepositionCardStepResponse, error) { +// ParseCreateCardResponse parses an HTTP response from a CreateCardWithResponse call +func ParseCreateCardResponse(rsp *http.Response) (*CreateCardResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &RepositionCardStepResponse{ + response := &CreateCardResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { - case rsp.StatusCode == 200: - break // No content-type + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: + var dest CreateCardResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON201 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: var dest UnauthorizedErrorResponseContent @@ -30699,26 +32111,72 @@ func ParseRepositionCardStepResponse(rsp *http.Response) (*RepositionCardStepRes return response, nil } -// ParseCreateCardStepResponse parses an HTTP response from a CreateCardStepWithResponse call -func ParseCreateCardStepResponse(rsp *http.Response) (*CreateCardStepResponse, error) { +// ParseUnsubscribeFromCardColumnResponse parses an HTTP response from a UnsubscribeFromCardColumnWithResponse call +func ParseUnsubscribeFromCardColumnResponse(rsp *http.Response) (*UnsubscribeFromCardColumnResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &CreateCardStepResponse{ + response := &UnsubscribeFromCardColumnResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest CreateCardStepResponseContent + case rsp.StatusCode == 204: + break // No content-type + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest UnauthorizedErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON201 = &dest + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest ForbiddenErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest NotFoundErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest InternalServerErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseSubscribeToCardColumnResponse parses an HTTP response from a SubscribeToCardColumnWithResponse call +func ParseSubscribeToCardColumnResponse(rsp *http.Response) (*SubscribeToCardColumnResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &SubscribeToCardColumnResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case rsp.StatusCode == 204: + break // No content-type case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: var dest UnauthorizedErrorResponseContent @@ -30734,12 +32192,12 @@ func ParseCreateCardStepResponse(rsp *http.Response) (*CreateCardStepResponse, e } response.JSON403 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: - var dest ValidationErrorResponseContent + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest NotFoundErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON422 = &dest + response.JSON404 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: var dest RateLimitErrorResponseContent @@ -30760,22 +32218,76 @@ func ParseCreateCardStepResponse(rsp *http.Response) (*CreateCardStepResponse, e return response, nil } -// ParseGetCardColumnResponse parses an HTTP response from a GetCardColumnWithResponse call -func ParseGetCardColumnResponse(rsp *http.Response) (*GetCardColumnResponse, error) { +// ParseGetCardStepResponse parses an HTTP response from a GetCardStepWithResponse call +func ParseGetCardStepResponse(rsp *http.Response) (*GetCardStepResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &GetCardColumnResponse{ + response := &GetCardStepResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest GetCardColumnResponseContent + var dest GetCardStepResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest UnauthorizedErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest ForbiddenErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest NotFoundErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest InternalServerErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseUpdateCardStepResponse parses an HTTP response from a UpdateCardStepWithResponse call +func ParseUpdateCardStepResponse(rsp *http.Response) (*UpdateCardStepResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &UpdateCardStepResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest UpdateCardStepResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -30802,6 +32314,13 @@ func ParseGetCardColumnResponse(rsp *http.Response) (*GetCardColumnResponse, err } response.JSON404 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: + var dest ValidationErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON422 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: var dest InternalServerErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { @@ -30814,22 +32333,22 @@ func ParseGetCardColumnResponse(rsp *http.Response) (*GetCardColumnResponse, err return response, nil } -// ParseUpdateCardColumnResponse parses an HTTP response from a UpdateCardColumnWithResponse call -func ParseUpdateCardColumnResponse(rsp *http.Response) (*UpdateCardColumnResponse, error) { +// ParseSetCardStepCompletionResponse parses an HTTP response from a SetCardStepCompletionWithResponse call +func ParseSetCardStepCompletionResponse(rsp *http.Response) (*SetCardStepCompletionResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &UpdateCardColumnResponse{ + response := &SetCardStepCompletionResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest UpdateCardColumnResponseContent + var dest SetCardStepCompletionResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -30875,22 +32394,22 @@ func ParseUpdateCardColumnResponse(rsp *http.Response) (*UpdateCardColumnRespons return response, nil } -// ParseListCardsResponse parses an HTTP response from a ListCardsWithResponse call -func ParseListCardsResponse(rsp *http.Response) (*ListCardsResponse, error) { +// ParseGetCardTableResponse parses an HTTP response from a GetCardTableWithResponse call +func ParseGetCardTableResponse(rsp *http.Response) (*GetCardTableResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &ListCardsResponse{ + response := &GetCardTableResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest ListCardsResponseContent + var dest GetCardTableResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -30910,12 +32429,12 @@ func ParseListCardsResponse(rsp *http.Response) (*ListCardsResponse, error) { } response.JSON403 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: - var dest RateLimitErrorResponseContent + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest NotFoundErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON429 = &dest + response.JSON404 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: var dest InternalServerErrorResponseContent @@ -30929,22 +32448,22 @@ func ParseListCardsResponse(rsp *http.Response) (*ListCardsResponse, error) { return response, nil } -// ParseCreateCardResponse parses an HTTP response from a CreateCardWithResponse call -func ParseCreateCardResponse(rsp *http.Response) (*CreateCardResponse, error) { +// ParseCreateCardColumnResponse parses an HTTP response from a CreateCardColumnWithResponse call +func ParseCreateCardColumnResponse(rsp *http.Response) (*CreateCardColumnResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &CreateCardResponse{ + response := &CreateCardColumnResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest CreateCardResponseContent + var dest CreateCardColumnResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -30990,15 +32509,15 @@ func ParseCreateCardResponse(rsp *http.Response) (*CreateCardResponse, error) { return response, nil } -// ParseUnsubscribeFromCardColumnResponse parses an HTTP response from a UnsubscribeFromCardColumnWithResponse call -func ParseUnsubscribeFromCardColumnResponse(rsp *http.Response) (*UnsubscribeFromCardColumnResponse, error) { +// ParseMoveCardColumnResponse parses an HTTP response from a MoveCardColumnWithResponse call +func ParseMoveCardColumnResponse(rsp *http.Response) (*MoveCardColumnResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &UnsubscribeFromCardColumnResponse{ + response := &MoveCardColumnResponse{ Body: bodyBytes, HTTPResponse: rsp, } @@ -31021,12 +32540,19 @@ func ParseUnsubscribeFromCardColumnResponse(rsp *http.Response) (*UnsubscribeFro } response.JSON403 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: - var dest NotFoundErrorResponseContent + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: + var dest ValidationErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON404 = &dest + response.JSON422 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: + var dest RateLimitErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON429 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: var dest InternalServerErrorResponseContent @@ -31040,22 +32566,26 @@ func ParseUnsubscribeFromCardColumnResponse(rsp *http.Response) (*UnsubscribeFro return response, nil } -// ParseSubscribeToCardColumnResponse parses an HTTP response from a SubscribeToCardColumnWithResponse call -func ParseSubscribeToCardColumnResponse(rsp *http.Response) (*SubscribeToCardColumnResponse, error) { +// ParseGetEverythingOverdueCardsResponse parses an HTTP response from a GetEverythingOverdueCardsWithResponse call +func ParseGetEverythingOverdueCardsResponse(rsp *http.Response) (*GetEverythingOverdueCardsResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &SubscribeToCardColumnResponse{ + response := &GetEverythingOverdueCardsResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { - case rsp.StatusCode == 204: - break // No content-type + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest GetEverythingOverdueCardsResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: var dest UnauthorizedErrorResponseContent @@ -31071,20 +32601,6 @@ func ParseSubscribeToCardColumnResponse(rsp *http.Response) (*SubscribeToCardCol } response.JSON403 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: - var dest NotFoundErrorResponseContent - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON404 = &dest - - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: - var dest RateLimitErrorResponseContent - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON429 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: var dest InternalServerErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { @@ -31097,22 +32613,22 @@ func ParseSubscribeToCardColumnResponse(rsp *http.Response) (*SubscribeToCardCol return response, nil } -// ParseGetCardStepResponse parses an HTTP response from a GetCardStepWithResponse call -func ParseGetCardStepResponse(rsp *http.Response) (*GetCardStepResponse, error) { +// ParseListMessageTypesResponse parses an HTTP response from a ListMessageTypesWithResponse call +func ParseListMessageTypesResponse(rsp *http.Response) (*ListMessageTypesResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &GetCardStepResponse{ + response := &ListMessageTypesResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest GetCardStepResponseContent + var dest ListMessageTypesResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -31132,12 +32648,12 @@ func ParseGetCardStepResponse(rsp *http.Response) (*GetCardStepResponse, error) } response.JSON403 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: - var dest NotFoundErrorResponseContent + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: + var dest RateLimitErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON404 = &dest + response.JSON429 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: var dest InternalServerErrorResponseContent @@ -31151,26 +32667,26 @@ func ParseGetCardStepResponse(rsp *http.Response) (*GetCardStepResponse, error) return response, nil } -// ParseUpdateCardStepResponse parses an HTTP response from a UpdateCardStepWithResponse call -func ParseUpdateCardStepResponse(rsp *http.Response) (*UpdateCardStepResponse, error) { +// ParseCreateMessageTypeResponse parses an HTTP response from a CreateMessageTypeWithResponse call +func ParseCreateMessageTypeResponse(rsp *http.Response) (*CreateMessageTypeResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &UpdateCardStepResponse{ + response := &CreateMessageTypeResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest UpdateCardStepResponseContent + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: + var dest CreateMessageTypeResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON200 = &dest + response.JSON201 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: var dest UnauthorizedErrorResponseContent @@ -31186,19 +32702,19 @@ func ParseUpdateCardStepResponse(rsp *http.Response) (*UpdateCardStepResponse, e } response.JSON403 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: - var dest NotFoundErrorResponseContent + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: + var dest ValidationErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON404 = &dest + response.JSON422 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: - var dest ValidationErrorResponseContent + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: + var dest RateLimitErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON422 = &dest + response.JSON429 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: var dest InternalServerErrorResponseContent @@ -31212,26 +32728,22 @@ func ParseUpdateCardStepResponse(rsp *http.Response) (*UpdateCardStepResponse, e return response, nil } -// ParseSetCardStepCompletionResponse parses an HTTP response from a SetCardStepCompletionWithResponse call -func ParseSetCardStepCompletionResponse(rsp *http.Response) (*SetCardStepCompletionResponse, error) { +// ParseDeleteMessageTypeResponse parses an HTTP response from a DeleteMessageTypeWithResponse call +func ParseDeleteMessageTypeResponse(rsp *http.Response) (*DeleteMessageTypeResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &SetCardStepCompletionResponse{ + response := &DeleteMessageTypeResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest SetCardStepCompletionResponseContent - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest + case rsp.StatusCode == 204: + break // No content-type case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: var dest UnauthorizedErrorResponseContent @@ -31254,13 +32766,6 @@ func ParseSetCardStepCompletionResponse(rsp *http.Response) (*SetCardStepComplet } response.JSON404 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: - var dest ValidationErrorResponseContent - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON422 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: var dest InternalServerErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { @@ -31273,22 +32778,22 @@ func ParseSetCardStepCompletionResponse(rsp *http.Response) (*SetCardStepComplet return response, nil } -// ParseGetCardTableResponse parses an HTTP response from a GetCardTableWithResponse call -func ParseGetCardTableResponse(rsp *http.Response) (*GetCardTableResponse, error) { +// ParseGetMessageTypeResponse parses an HTTP response from a GetMessageTypeWithResponse call +func ParseGetMessageTypeResponse(rsp *http.Response) (*GetMessageTypeResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &GetCardTableResponse{ + response := &GetMessageTypeResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest GetCardTableResponseContent + var dest GetMessageTypeResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -31327,26 +32832,26 @@ func ParseGetCardTableResponse(rsp *http.Response) (*GetCardTableResponse, error return response, nil } -// ParseCreateCardColumnResponse parses an HTTP response from a CreateCardColumnWithResponse call -func ParseCreateCardColumnResponse(rsp *http.Response) (*CreateCardColumnResponse, error) { +// ParseUpdateMessageTypeResponse parses an HTTP response from a UpdateMessageTypeWithResponse call +func ParseUpdateMessageTypeResponse(rsp *http.Response) (*UpdateMessageTypeResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &CreateCardColumnResponse{ + response := &UpdateMessageTypeResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest CreateCardColumnResponseContent + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest UpdateMessageTypeResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON201 = &dest + response.JSON200 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: var dest UnauthorizedErrorResponseContent @@ -31362,19 +32867,19 @@ func ParseCreateCardColumnResponse(rsp *http.Response) (*CreateCardColumnRespons } response.JSON403 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: - var dest ValidationErrorResponseContent + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest NotFoundErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON422 = &dest + response.JSON404 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: - var dest RateLimitErrorResponseContent + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: + var dest ValidationErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON429 = &dest + response.JSON422 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: var dest InternalServerErrorResponseContent @@ -31388,22 +32893,26 @@ func ParseCreateCardColumnResponse(rsp *http.Response) (*CreateCardColumnRespons return response, nil } -// ParseMoveCardColumnResponse parses an HTTP response from a MoveCardColumnWithResponse call -func ParseMoveCardColumnResponse(rsp *http.Response) (*MoveCardColumnResponse, error) { +// ParseListCampfiresResponse parses an HTTP response from a ListCampfiresWithResponse call +func ParseListCampfiresResponse(rsp *http.Response) (*ListCampfiresResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &MoveCardColumnResponse{ + response := &ListCampfiresResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { - case rsp.StatusCode == 204: - break // No content-type + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ListCampfiresResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: var dest UnauthorizedErrorResponseContent @@ -31419,13 +32928,6 @@ func ParseMoveCardColumnResponse(rsp *http.Response) (*MoveCardColumnResponse, e } response.JSON403 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: - var dest ValidationErrorResponseContent - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON422 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: var dest RateLimitErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { @@ -31445,22 +32947,22 @@ func ParseMoveCardColumnResponse(rsp *http.Response) (*MoveCardColumnResponse, e return response, nil } -// ParseListMessageTypesResponse parses an HTTP response from a ListMessageTypesWithResponse call -func ParseListMessageTypesResponse(rsp *http.Response) (*ListMessageTypesResponse, error) { +// ParseGetCampfireResponse parses an HTTP response from a GetCampfireWithResponse call +func ParseGetCampfireResponse(rsp *http.Response) (*GetCampfireResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &ListMessageTypesResponse{ + response := &GetCampfireResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest ListMessageTypesResponseContent + var dest GetCampfireResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -31480,12 +32982,12 @@ func ParseListMessageTypesResponse(rsp *http.Response) (*ListMessageTypesRespons } response.JSON403 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: - var dest RateLimitErrorResponseContent + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest NotFoundErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON429 = &dest + response.JSON404 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: var dest InternalServerErrorResponseContent @@ -31499,26 +33001,26 @@ func ParseListMessageTypesResponse(rsp *http.Response) (*ListMessageTypesRespons return response, nil } -// ParseCreateMessageTypeResponse parses an HTTP response from a CreateMessageTypeWithResponse call -func ParseCreateMessageTypeResponse(rsp *http.Response) (*CreateMessageTypeResponse, error) { +// ParseListChatbotsResponse parses an HTTP response from a ListChatbotsWithResponse call +func ParseListChatbotsResponse(rsp *http.Response) (*ListChatbotsResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &CreateMessageTypeResponse{ + response := &ListChatbotsResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest CreateMessageTypeResponseContent + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ListChatbotsResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON201 = &dest + response.JSON200 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: var dest UnauthorizedErrorResponseContent @@ -31534,13 +33036,6 @@ func ParseCreateMessageTypeResponse(rsp *http.Response) (*CreateMessageTypeRespo } response.JSON403 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: - var dest ValidationErrorResponseContent - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON422 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: var dest RateLimitErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { @@ -31560,22 +33055,26 @@ func ParseCreateMessageTypeResponse(rsp *http.Response) (*CreateMessageTypeRespo return response, nil } -// ParseDeleteMessageTypeResponse parses an HTTP response from a DeleteMessageTypeWithResponse call -func ParseDeleteMessageTypeResponse(rsp *http.Response) (*DeleteMessageTypeResponse, error) { +// ParseCreateChatbotResponse parses an HTTP response from a CreateChatbotWithResponse call +func ParseCreateChatbotResponse(rsp *http.Response) (*CreateChatbotResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &DeleteMessageTypeResponse{ + response := &CreateChatbotResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { - case rsp.StatusCode == 204: - break // No content-type + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: + var dest CreateChatbotResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON201 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: var dest UnauthorizedErrorResponseContent @@ -31591,12 +33090,19 @@ func ParseDeleteMessageTypeResponse(rsp *http.Response) (*DeleteMessageTypeRespo } response.JSON403 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: - var dest NotFoundErrorResponseContent + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: + var dest ValidationErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON404 = &dest + response.JSON422 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: + var dest RateLimitErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON429 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: var dest InternalServerErrorResponseContent @@ -31610,26 +33116,22 @@ func ParseDeleteMessageTypeResponse(rsp *http.Response) (*DeleteMessageTypeRespo return response, nil } -// ParseGetMessageTypeResponse parses an HTTP response from a GetMessageTypeWithResponse call -func ParseGetMessageTypeResponse(rsp *http.Response) (*GetMessageTypeResponse, error) { +// ParseDeleteChatbotResponse parses an HTTP response from a DeleteChatbotWithResponse call +func ParseDeleteChatbotResponse(rsp *http.Response) (*DeleteChatbotResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &GetMessageTypeResponse{ + response := &DeleteChatbotResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest GetMessageTypeResponseContent - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest + case rsp.StatusCode == 204: + break // No content-type case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: var dest UnauthorizedErrorResponseContent @@ -31664,22 +33166,22 @@ func ParseGetMessageTypeResponse(rsp *http.Response) (*GetMessageTypeResponse, e return response, nil } -// ParseUpdateMessageTypeResponse parses an HTTP response from a UpdateMessageTypeWithResponse call -func ParseUpdateMessageTypeResponse(rsp *http.Response) (*UpdateMessageTypeResponse, error) { +// ParseGetChatbotResponse parses an HTTP response from a GetChatbotWithResponse call +func ParseGetChatbotResponse(rsp *http.Response) (*GetChatbotResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &UpdateMessageTypeResponse{ + response := &GetChatbotResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest UpdateMessageTypeResponseContent + var dest GetChatbotResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -31706,13 +33208,6 @@ func ParseUpdateMessageTypeResponse(rsp *http.Response) (*UpdateMessageTypeRespo } response.JSON404 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: - var dest ValidationErrorResponseContent - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON422 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: var dest InternalServerErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { @@ -31725,22 +33220,22 @@ func ParseUpdateMessageTypeResponse(rsp *http.Response) (*UpdateMessageTypeRespo return response, nil } -// ParseListCampfiresResponse parses an HTTP response from a ListCampfiresWithResponse call -func ParseListCampfiresResponse(rsp *http.Response) (*ListCampfiresResponse, error) { +// ParseUpdateChatbotResponse parses an HTTP response from a UpdateChatbotWithResponse call +func ParseUpdateChatbotResponse(rsp *http.Response) (*UpdateChatbotResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &ListCampfiresResponse{ + response := &UpdateChatbotResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest ListCampfiresResponseContent + var dest UpdateChatbotResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -31760,66 +33255,19 @@ func ParseListCampfiresResponse(rsp *http.Response) (*ListCampfiresResponse, err } response.JSON403 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: - var dest RateLimitErrorResponseContent - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON429 = &dest - - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: - var dest InternalServerErrorResponseContent - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON500 = &dest - - } - - return response, nil -} - -// ParseGetCampfireResponse parses an HTTP response from a GetCampfireWithResponse call -func ParseGetCampfireResponse(rsp *http.Response) (*GetCampfireResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &GetCampfireResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest GetCampfireResponseContent - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: - var dest UnauthorizedErrorResponseContent - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON401 = &dest - - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: - var dest ForbiddenErrorResponseContent + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest NotFoundErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON403 = &dest + response.JSON404 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: - var dest NotFoundErrorResponseContent + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: + var dest ValidationErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON404 = &dest + response.JSON422 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: var dest InternalServerErrorResponseContent @@ -31833,22 +33281,22 @@ func ParseGetCampfireResponse(rsp *http.Response) (*GetCampfireResponse, error) return response, nil } -// ParseListChatbotsResponse parses an HTTP response from a ListChatbotsWithResponse call -func ParseListChatbotsResponse(rsp *http.Response) (*ListChatbotsResponse, error) { +// ParseListCampfireLinesResponse parses an HTTP response from a ListCampfireLinesWithResponse call +func ParseListCampfireLinesResponse(rsp *http.Response) (*ListCampfireLinesResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &ListChatbotsResponse{ + response := &ListCampfireLinesResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest ListChatbotsResponseContent + var dest ListCampfireLinesResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -31887,22 +33335,22 @@ func ParseListChatbotsResponse(rsp *http.Response) (*ListChatbotsResponse, error return response, nil } -// ParseCreateChatbotResponse parses an HTTP response from a CreateChatbotWithResponse call -func ParseCreateChatbotResponse(rsp *http.Response) (*CreateChatbotResponse, error) { +// ParseCreateCampfireLineResponse parses an HTTP response from a CreateCampfireLineWithResponse call +func ParseCreateCampfireLineResponse(rsp *http.Response) (*CreateCampfireLineResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &CreateChatbotResponse{ + response := &CreateCampfireLineResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest CreateChatbotResponseContent + var dest CreateCampfireLineResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -31948,15 +33396,15 @@ func ParseCreateChatbotResponse(rsp *http.Response) (*CreateChatbotResponse, err return response, nil } -// ParseDeleteChatbotResponse parses an HTTP response from a DeleteChatbotWithResponse call -func ParseDeleteChatbotResponse(rsp *http.Response) (*DeleteChatbotResponse, error) { +// ParseDeleteCampfireLineResponse parses an HTTP response from a DeleteCampfireLineWithResponse call +func ParseDeleteCampfireLineResponse(rsp *http.Response) (*DeleteCampfireLineResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &DeleteChatbotResponse{ + response := &DeleteCampfireLineResponse{ Body: bodyBytes, HTTPResponse: rsp, } @@ -31998,22 +33446,22 @@ func ParseDeleteChatbotResponse(rsp *http.Response) (*DeleteChatbotResponse, err return response, nil } -// ParseGetChatbotResponse parses an HTTP response from a GetChatbotWithResponse call -func ParseGetChatbotResponse(rsp *http.Response) (*GetChatbotResponse, error) { +// ParseGetCampfireLineResponse parses an HTTP response from a GetCampfireLineWithResponse call +func ParseGetCampfireLineResponse(rsp *http.Response) (*GetCampfireLineResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &GetChatbotResponse{ + response := &GetCampfireLineResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest GetChatbotResponseContent + var dest GetCampfireLineResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -32052,26 +33500,22 @@ func ParseGetChatbotResponse(rsp *http.Response) (*GetChatbotResponse, error) { return response, nil } -// ParseUpdateChatbotResponse parses an HTTP response from a UpdateChatbotWithResponse call -func ParseUpdateChatbotResponse(rsp *http.Response) (*UpdateChatbotResponse, error) { +// ParseUpdateCampfireLineResponse parses an HTTP response from a UpdateCampfireLineWithResponse call +func ParseUpdateCampfireLineResponse(rsp *http.Response) (*UpdateCampfireLineResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &UpdateChatbotResponse{ + response := &UpdateCampfireLineResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest UpdateChatbotResponseContent - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest + case rsp.StatusCode == 204: + break // No content-type case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: var dest UnauthorizedErrorResponseContent @@ -32101,6 +33545,13 @@ func ParseUpdateChatbotResponse(rsp *http.Response) (*UpdateChatbotResponse, err } response.JSON422 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: + var dest RateLimitErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON429 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: var dest InternalServerErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { @@ -32113,22 +33564,22 @@ func ParseUpdateChatbotResponse(rsp *http.Response) (*UpdateChatbotResponse, err return response, nil } -// ParseListCampfireLinesResponse parses an HTTP response from a ListCampfireLinesWithResponse call -func ParseListCampfireLinesResponse(rsp *http.Response) (*ListCampfireLinesResponse, error) { +// ParseListCampfireUploadsResponse parses an HTTP response from a ListCampfireUploadsWithResponse call +func ParseListCampfireUploadsResponse(rsp *http.Response) (*ListCampfireUploadsResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &ListCampfireLinesResponse{ + response := &ListCampfireUploadsResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest ListCampfireLinesResponseContent + var dest ListCampfireUploadsResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -32167,22 +33618,22 @@ func ParseListCampfireLinesResponse(rsp *http.Response) (*ListCampfireLinesRespo return response, nil } -// ParseCreateCampfireLineResponse parses an HTTP response from a CreateCampfireLineWithResponse call -func ParseCreateCampfireLineResponse(rsp *http.Response) (*CreateCampfireLineResponse, error) { +// ParseCreateCampfireUploadResponse parses an HTTP response from a CreateCampfireUploadWithResponse call +func ParseCreateCampfireUploadResponse(rsp *http.Response) (*CreateCampfireUploadResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &CreateCampfireLineResponse{ + response := &CreateCampfireUploadResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest CreateCampfireLineResponseContent + var dest CreateCampfireUploadResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -32228,22 +33679,26 @@ func ParseCreateCampfireLineResponse(rsp *http.Response) (*CreateCampfireLineRes return response, nil } -// ParseDeleteCampfireLineResponse parses an HTTP response from a DeleteCampfireLineWithResponse call -func ParseDeleteCampfireLineResponse(rsp *http.Response) (*DeleteCampfireLineResponse, error) { +// ParseGetEverythingCheckinsResponse parses an HTTP response from a GetEverythingCheckinsWithResponse call +func ParseGetEverythingCheckinsResponse(rsp *http.Response) (*GetEverythingCheckinsResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &DeleteCampfireLineResponse{ + response := &GetEverythingCheckinsResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { - case rsp.StatusCode == 204: - break // No content-type + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest GetEverythingCheckinsResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: var dest UnauthorizedErrorResponseContent @@ -32259,12 +33714,12 @@ func ParseDeleteCampfireLineResponse(rsp *http.Response) (*DeleteCampfireLineRes } response.JSON403 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: - var dest NotFoundErrorResponseContent + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: + var dest RateLimitErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON404 = &dest + response.JSON429 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: var dest InternalServerErrorResponseContent @@ -32278,22 +33733,22 @@ func ParseDeleteCampfireLineResponse(rsp *http.Response) (*DeleteCampfireLineRes return response, nil } -// ParseGetCampfireLineResponse parses an HTTP response from a GetCampfireLineWithResponse call -func ParseGetCampfireLineResponse(rsp *http.Response) (*GetCampfireLineResponse, error) { +// ParseListPingablePeopleResponse parses an HTTP response from a ListPingablePeopleWithResponse call +func ParseListPingablePeopleResponse(rsp *http.Response) (*ListPingablePeopleResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &GetCampfireLineResponse{ + response := &ListPingablePeopleResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest GetCampfireLineResponseContent + var dest ListPingablePeopleResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -32313,12 +33768,12 @@ func ParseGetCampfireLineResponse(rsp *http.Response) (*GetCampfireLineResponse, } response.JSON403 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: - var dest NotFoundErrorResponseContent + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: + var dest RateLimitErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON404 = &dest + response.JSON429 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: var dest InternalServerErrorResponseContent @@ -32332,22 +33787,26 @@ func ParseGetCampfireLineResponse(rsp *http.Response) (*GetCampfireLineResponse, return response, nil } -// ParseUpdateCampfireLineResponse parses an HTTP response from a UpdateCampfireLineWithResponse call -func ParseUpdateCampfireLineResponse(rsp *http.Response) (*UpdateCampfireLineResponse, error) { +// ParseListClientApprovalsResponse parses an HTTP response from a ListClientApprovalsWithResponse call +func ParseListClientApprovalsResponse(rsp *http.Response) (*ListClientApprovalsResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &UpdateCampfireLineResponse{ + response := &ListClientApprovalsResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { - case rsp.StatusCode == 204: - break // No content-type + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ListClientApprovalsResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: var dest UnauthorizedErrorResponseContent @@ -32363,20 +33822,6 @@ func ParseUpdateCampfireLineResponse(rsp *http.Response) (*UpdateCampfireLineRes } response.JSON403 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: - var dest NotFoundErrorResponseContent - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON404 = &dest - - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: - var dest ValidationErrorResponseContent - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON422 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: var dest RateLimitErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { @@ -32396,22 +33841,22 @@ func ParseUpdateCampfireLineResponse(rsp *http.Response) (*UpdateCampfireLineRes return response, nil } -// ParseListCampfireUploadsResponse parses an HTTP response from a ListCampfireUploadsWithResponse call -func ParseListCampfireUploadsResponse(rsp *http.Response) (*ListCampfireUploadsResponse, error) { +// ParseGetClientApprovalResponse parses an HTTP response from a GetClientApprovalWithResponse call +func ParseGetClientApprovalResponse(rsp *http.Response) (*GetClientApprovalResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &ListCampfireUploadsResponse{ + response := &GetClientApprovalResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest ListCampfireUploadsResponseContent + var dest GetClientApprovalResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -32431,12 +33876,12 @@ func ParseListCampfireUploadsResponse(rsp *http.Response) (*ListCampfireUploadsR } response.JSON403 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: - var dest RateLimitErrorResponseContent + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest NotFoundErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON429 = &dest + response.JSON404 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: var dest InternalServerErrorResponseContent @@ -32450,26 +33895,26 @@ func ParseListCampfireUploadsResponse(rsp *http.Response) (*ListCampfireUploadsR return response, nil } -// ParseCreateCampfireUploadResponse parses an HTTP response from a CreateCampfireUploadWithResponse call -func ParseCreateCampfireUploadResponse(rsp *http.Response) (*CreateCampfireUploadResponse, error) { +// ParseListClientCorrespondencesResponse parses an HTTP response from a ListClientCorrespondencesWithResponse call +func ParseListClientCorrespondencesResponse(rsp *http.Response) (*ListClientCorrespondencesResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &CreateCampfireUploadResponse{ + response := &ListClientCorrespondencesResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest CreateCampfireUploadResponseContent + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ListClientCorrespondencesResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON201 = &dest + response.JSON200 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: var dest UnauthorizedErrorResponseContent @@ -32485,13 +33930,6 @@ func ParseCreateCampfireUploadResponse(rsp *http.Response) (*CreateCampfireUploa } response.JSON403 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: - var dest ValidationErrorResponseContent - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON422 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: var dest RateLimitErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { @@ -32511,22 +33949,22 @@ func ParseCreateCampfireUploadResponse(rsp *http.Response) (*CreateCampfireUploa return response, nil } -// ParseListPingablePeopleResponse parses an HTTP response from a ListPingablePeopleWithResponse call -func ParseListPingablePeopleResponse(rsp *http.Response) (*ListPingablePeopleResponse, error) { +// ParseGetClientCorrespondenceResponse parses an HTTP response from a GetClientCorrespondenceWithResponse call +func ParseGetClientCorrespondenceResponse(rsp *http.Response) (*GetClientCorrespondenceResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &ListPingablePeopleResponse{ + response := &GetClientCorrespondenceResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest ListPingablePeopleResponseContent + var dest GetClientCorrespondenceResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -32546,12 +33984,12 @@ func ParseListPingablePeopleResponse(rsp *http.Response) (*ListPingablePeopleRes } response.JSON403 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: - var dest RateLimitErrorResponseContent + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest NotFoundErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON429 = &dest + response.JSON404 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: var dest InternalServerErrorResponseContent @@ -32565,22 +34003,22 @@ func ParseListPingablePeopleResponse(rsp *http.Response) (*ListPingablePeopleRes return response, nil } -// ParseListClientApprovalsResponse parses an HTTP response from a ListClientApprovalsWithResponse call -func ParseListClientApprovalsResponse(rsp *http.Response) (*ListClientApprovalsResponse, error) { +// ParseListClientRepliesResponse parses an HTTP response from a ListClientRepliesWithResponse call +func ParseListClientRepliesResponse(rsp *http.Response) (*ListClientRepliesResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &ListClientApprovalsResponse{ + response := &ListClientRepliesResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest ListClientApprovalsResponseContent + var dest ListClientRepliesResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -32619,22 +34057,22 @@ func ParseListClientApprovalsResponse(rsp *http.Response) (*ListClientApprovalsR return response, nil } -// ParseGetClientApprovalResponse parses an HTTP response from a GetClientApprovalWithResponse call -func ParseGetClientApprovalResponse(rsp *http.Response) (*GetClientApprovalResponse, error) { +// ParseGetClientReplyResponse parses an HTTP response from a GetClientReplyWithResponse call +func ParseGetClientReplyResponse(rsp *http.Response) (*GetClientReplyResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &GetClientApprovalResponse{ + response := &GetClientReplyResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest GetClientApprovalResponseContent + var dest GetClientReplyResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -32673,22 +34111,22 @@ func ParseGetClientApprovalResponse(rsp *http.Response) (*GetClientApprovalRespo return response, nil } -// ParseListClientCorrespondencesResponse parses an HTTP response from a ListClientCorrespondencesWithResponse call -func ParseListClientCorrespondencesResponse(rsp *http.Response) (*ListClientCorrespondencesResponse, error) { +// ParseGetEverythingCommentsResponse parses an HTTP response from a GetEverythingCommentsWithResponse call +func ParseGetEverythingCommentsResponse(rsp *http.Response) (*GetEverythingCommentsResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &ListClientCorrespondencesResponse{ + response := &GetEverythingCommentsResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest ListClientCorrespondencesResponseContent + var dest GetEverythingCommentsResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -32727,22 +34165,22 @@ func ParseListClientCorrespondencesResponse(rsp *http.Response) (*ListClientCorr return response, nil } -// ParseGetClientCorrespondenceResponse parses an HTTP response from a GetClientCorrespondenceWithResponse call -func ParseGetClientCorrespondenceResponse(rsp *http.Response) (*GetClientCorrespondenceResponse, error) { +// ParseGetCommentResponse parses an HTTP response from a GetCommentWithResponse call +func ParseGetCommentResponse(rsp *http.Response) (*GetCommentResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &GetClientCorrespondenceResponse{ + response := &GetCommentResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest GetClientCorrespondenceResponseContent + var dest GetCommentResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -32781,22 +34219,22 @@ func ParseGetClientCorrespondenceResponse(rsp *http.Response) (*GetClientCorresp return response, nil } -// ParseListClientRepliesResponse parses an HTTP response from a ListClientRepliesWithResponse call -func ParseListClientRepliesResponse(rsp *http.Response) (*ListClientRepliesResponse, error) { +// ParseUpdateCommentResponse parses an HTTP response from a UpdateCommentWithResponse call +func ParseUpdateCommentResponse(rsp *http.Response) (*UpdateCommentResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &ListClientRepliesResponse{ + response := &UpdateCommentResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest ListClientRepliesResponseContent + var dest UpdateCommentResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -32816,12 +34254,19 @@ func ParseListClientRepliesResponse(rsp *http.Response) (*ListClientRepliesRespo } response.JSON403 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: - var dest RateLimitErrorResponseContent + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest NotFoundErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON429 = &dest + response.JSON404 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: + var dest ValidationErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON422 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: var dest InternalServerErrorResponseContent @@ -32835,26 +34280,22 @@ func ParseListClientRepliesResponse(rsp *http.Response) (*ListClientRepliesRespo return response, nil } -// ParseGetClientReplyResponse parses an HTTP response from a GetClientReplyWithResponse call -func ParseGetClientReplyResponse(rsp *http.Response) (*GetClientReplyResponse, error) { +// ParseDeleteToolResponse parses an HTTP response from a DeleteToolWithResponse call +func ParseDeleteToolResponse(rsp *http.Response) (*DeleteToolResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &GetClientReplyResponse{ + response := &DeleteToolResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest GetClientReplyResponseContent - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest + case rsp.StatusCode == 204: + break // No content-type case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: var dest UnauthorizedErrorResponseContent @@ -32889,22 +34330,22 @@ func ParseGetClientReplyResponse(rsp *http.Response) (*GetClientReplyResponse, e return response, nil } -// ParseGetCommentResponse parses an HTTP response from a GetCommentWithResponse call -func ParseGetCommentResponse(rsp *http.Response) (*GetCommentResponse, error) { +// ParseGetToolResponse parses an HTTP response from a GetToolWithResponse call +func ParseGetToolResponse(rsp *http.Response) (*GetToolResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &GetCommentResponse{ + response := &GetToolResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest GetCommentResponseContent + var dest GetToolResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -32943,22 +34384,22 @@ func ParseGetCommentResponse(rsp *http.Response) (*GetCommentResponse, error) { return response, nil } -// ParseUpdateCommentResponse parses an HTTP response from a UpdateCommentWithResponse call -func ParseUpdateCommentResponse(rsp *http.Response) (*UpdateCommentResponse, error) { +// ParseUpdateToolResponse parses an HTTP response from a UpdateToolWithResponse call +func ParseUpdateToolResponse(rsp *http.Response) (*UpdateToolResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &UpdateCommentResponse{ + response := &UpdateToolResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest UpdateCommentResponseContent + var dest UpdateToolResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -33004,72 +34445,22 @@ func ParseUpdateCommentResponse(rsp *http.Response) (*UpdateCommentResponse, err return response, nil } -// ParseDeleteToolResponse parses an HTTP response from a DeleteToolWithResponse call -func ParseDeleteToolResponse(rsp *http.Response) (*DeleteToolResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &DeleteToolResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case rsp.StatusCode == 204: - break // No content-type - - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: - var dest UnauthorizedErrorResponseContent - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON401 = &dest - - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: - var dest ForbiddenErrorResponseContent - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON403 = &dest - - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: - var dest NotFoundErrorResponseContent - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON404 = &dest - - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: - var dest InternalServerErrorResponseContent - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON500 = &dest - - } - - return response, nil -} - -// ParseGetToolResponse parses an HTTP response from a GetToolWithResponse call -func ParseGetToolResponse(rsp *http.Response) (*GetToolResponse, error) { +// ParseGetDocumentResponse parses an HTTP response from a GetDocumentWithResponse call +func ParseGetDocumentResponse(rsp *http.Response) (*GetDocumentResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &GetToolResponse{ + response := &GetDocumentResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest GetToolResponseContent + var dest GetDocumentResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -33108,22 +34499,22 @@ func ParseGetToolResponse(rsp *http.Response) (*GetToolResponse, error) { return response, nil } -// ParseUpdateToolResponse parses an HTTP response from a UpdateToolWithResponse call -func ParseUpdateToolResponse(rsp *http.Response) (*UpdateToolResponse, error) { +// ParseUpdateDocumentResponse parses an HTTP response from a UpdateDocumentWithResponse call +func ParseUpdateDocumentResponse(rsp *http.Response) (*UpdateDocumentResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &UpdateToolResponse{ + response := &UpdateDocumentResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest UpdateToolResponseContent + var dest UpdateDocumentResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -33169,22 +34560,22 @@ func ParseUpdateToolResponse(rsp *http.Response) (*UpdateToolResponse, error) { return response, nil } -// ParseGetDocumentResponse parses an HTTP response from a GetDocumentWithResponse call -func ParseGetDocumentResponse(rsp *http.Response) (*GetDocumentResponse, error) { +// ParseGetEverythingFilesResponse parses an HTTP response from a GetEverythingFilesWithResponse call +func ParseGetEverythingFilesResponse(rsp *http.Response) (*GetEverythingFilesResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &GetDocumentResponse{ + response := &GetEverythingFilesResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest GetDocumentResponseContent + var dest GetEverythingFilesResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -33204,12 +34595,12 @@ func ParseGetDocumentResponse(rsp *http.Response) (*GetDocumentResponse, error) } response.JSON403 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: - var dest NotFoundErrorResponseContent + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: + var dest RateLimitErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON404 = &dest + response.JSON429 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: var dest InternalServerErrorResponseContent @@ -33223,22 +34614,22 @@ func ParseGetDocumentResponse(rsp *http.Response) (*GetDocumentResponse, error) return response, nil } -// ParseUpdateDocumentResponse parses an HTTP response from a UpdateDocumentWithResponse call -func ParseUpdateDocumentResponse(rsp *http.Response) (*UpdateDocumentResponse, error) { +// ParseGetEverythingForwardsResponse parses an HTTP response from a GetEverythingForwardsWithResponse call +func ParseGetEverythingForwardsResponse(rsp *http.Response) (*GetEverythingForwardsResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &UpdateDocumentResponse{ + response := &GetEverythingForwardsResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest UpdateDocumentResponseContent + var dest GetEverythingForwardsResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -33258,19 +34649,12 @@ func ParseUpdateDocumentResponse(rsp *http.Response) (*UpdateDocumentResponse, e } response.JSON403 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: - var dest NotFoundErrorResponseContent - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON404 = &dest - - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: - var dest ValidationErrorResponseContent + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: + var dest RateLimitErrorResponseContent if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON422 = &dest + response.JSON429 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: var dest InternalServerErrorResponseContent @@ -34181,6 +35565,60 @@ func ParseCreateMessageResponse(rsp *http.Response) (*CreateMessageResponse, err return response, nil } +// ParseGetEverythingMessagesResponse parses an HTTP response from a GetEverythingMessagesWithResponse call +func ParseGetEverythingMessagesResponse(rsp *http.Response) (*GetEverythingMessagesResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetEverythingMessagesResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest GetEverythingMessagesResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest UnauthorizedErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest ForbiddenErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: + var dest RateLimitErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON429 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest InternalServerErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + // ParseGetMessageResponse parses an HTTP response from a GetMessageWithResponse call func ParseGetMessageResponse(rsp *http.Response) (*GetMessageResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) @@ -39847,6 +41285,53 @@ func ParseCreateTodoResponse(rsp *http.Response) (*CreateTodoResponse, error) { return response, nil } +// ParseGetEverythingOverdueTodosResponse parses an HTTP response from a GetEverythingOverdueTodosWithResponse call +func ParseGetEverythingOverdueTodosResponse(rsp *http.Response) (*GetEverythingOverdueTodosResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetEverythingOverdueTodosResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest GetEverythingOverdueTodosResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest UnauthorizedErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest ForbiddenErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest InternalServerErrorResponseContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + // ParseTrashTodoResponse parses an HTTP response from a TrashTodoWithResponse call func ParseTrashTodoResponse(rsp *http.Response) (*TrashTodoResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) diff --git a/kotlin/conformance/src/main/kotlin/com/basecamp/sdk/conformance/Main.kt b/kotlin/conformance/src/main/kotlin/com/basecamp/sdk/conformance/Main.kt index b28b461fb..06d1c8d3b 100644 --- a/kotlin/conformance/src/main/kotlin/com/basecamp/sdk/conformance/Main.kt +++ b/kotlin/conformance/src/main/kotlin/com/basecamp/sdk/conformance/Main.kt @@ -791,6 +791,46 @@ private suspend fun dispatchOperation(tc: TestCase, account: AccountClient): Dis DispatchResult() } + "GetEverythingMessages" -> { + account.everything.everythingMessages() + DispatchResult() + } + + "GetEverythingComments" -> { + account.everything.everythingComments() + DispatchResult() + } + + "GetEverythingCheckins" -> { + account.everything.everythingCheckins() + DispatchResult() + } + + "GetEverythingForwards" -> { + account.everything.everythingForwards() + DispatchResult() + } + + "GetEverythingBoosts" -> { + account.everything.everythingBoosts() + DispatchResult() + } + + "GetEverythingFiles" -> { + account.everything.everythingFiles() + DispatchResult() + } + + "GetEverythingOverdueTodos" -> { + account.everything.everythingOverdueTodos() + DispatchResult() + } + + "GetEverythingOverdueCards" -> { + account.everything.everythingOverdueCards() + DispatchResult() + } + else -> throw UnsupportedOperationException("Unknown operation: ${tc.operation}") } diff --git a/kotlin/generator/src/main/kotlin/com/basecamp/sdk/generator/Config.kt b/kotlin/generator/src/main/kotlin/com/basecamp/sdk/generator/Config.kt index 8892a0b42..8d53b1565 100644 --- a/kotlin/generator/src/main/kotlin/com/basecamp/sdk/generator/Config.kt +++ b/kotlin/generator/src/main/kotlin/com/basecamp/sdk/generator/Config.kt @@ -314,6 +314,7 @@ val TYPE_ALIASES = mapOf( "ClientReply" to "ClientReply", "Boost" to "Boost", "Notification" to "Notification", + "EverythingFile" to "EverythingFile", "TimelineEvent" to "TimelineEvent", "TimesheetEntry" to "TimesheetEntry", "HillChart" to "HillChart", diff --git a/kotlin/sdk/src/commonMain/kotlin/com/basecamp/sdk/generated/Metadata.kt b/kotlin/sdk/src/commonMain/kotlin/com/basecamp/sdk/generated/Metadata.kt index 896be78a0..b01479ccb 100644 --- a/kotlin/sdk/src/commonMain/kotlin/com/basecamp/sdk/generated/Metadata.kt +++ b/kotlin/sdk/src/commonMain/kotlin/com/basecamp/sdk/generated/Metadata.kt @@ -87,6 +87,14 @@ object Metadata { "GetClientReply" to OperationConfig(false, RetryConfig(3, 1000L, "exponential", setOf(429, 503))), "GetComment" to OperationConfig(false, RetryConfig(3, 1000L, "exponential", setOf(429, 503))), "GetDocument" to OperationConfig(false, RetryConfig(3, 1000L, "exponential", setOf(429, 503))), + "GetEverythingBoosts" to OperationConfig(false, RetryConfig(3, 1000L, "exponential", setOf(429, 503))), + "GetEverythingCheckins" to OperationConfig(false, RetryConfig(3, 1000L, "exponential", setOf(429, 503))), + "GetEverythingComments" to OperationConfig(false, RetryConfig(3, 1000L, "exponential", setOf(429, 503))), + "GetEverythingFiles" to OperationConfig(false, RetryConfig(3, 1000L, "exponential", setOf(429, 503))), + "GetEverythingForwards" to OperationConfig(false, RetryConfig(3, 1000L, "exponential", setOf(429, 503))), + "GetEverythingMessages" to OperationConfig(false, RetryConfig(3, 1000L, "exponential", setOf(429, 503))), + "GetEverythingOverdueCards" to OperationConfig(false, RetryConfig(3, 1000L, "exponential", setOf(429, 503))), + "GetEverythingOverdueTodos" to OperationConfig(false, RetryConfig(3, 1000L, "exponential", setOf(429, 503))), "GetForward" to OperationConfig(false, RetryConfig(3, 1000L, "exponential", setOf(429, 503))), "GetForwardReply" to OperationConfig(false, RetryConfig(3, 1000L, "exponential", setOf(429, 503))), "GetGaugeNeedle" to OperationConfig(false, RetryConfig(3, 1000L, "exponential", setOf(429, 503))), diff --git a/kotlin/sdk/src/commonMain/kotlin/com/basecamp/sdk/generated/ServiceAccessors.kt b/kotlin/sdk/src/commonMain/kotlin/com/basecamp/sdk/generated/ServiceAccessors.kt index 0085111b0..53af221f1 100644 --- a/kotlin/sdk/src/commonMain/kotlin/com/basecamp/sdk/generated/ServiceAccessors.kt +++ b/kotlin/sdk/src/commonMain/kotlin/com/basecamp/sdk/generated/ServiceAccessors.kt @@ -79,6 +79,10 @@ val AccountClient.documents: DocumentsService val AccountClient.events: EventsService get() = service("Events") { EventsService(this) } +/** Everything operations. */ +val AccountClient.everything: EverythingService + get() = service("Everything") { EverythingService(this) } + /** Forwards operations. */ val AccountClient.forwards: ForwardsService get() = service("Forwards") { ForwardsService(this) } diff --git a/kotlin/sdk/src/commonMain/kotlin/com/basecamp/sdk/generated/models/DoorService.kt b/kotlin/sdk/src/commonMain/kotlin/com/basecamp/sdk/generated/models/DoorService.kt new file mode 100644 index 000000000..aca860d91 --- /dev/null +++ b/kotlin/sdk/src/commonMain/kotlin/com/basecamp/sdk/generated/models/DoorService.kt @@ -0,0 +1,20 @@ +package com.basecamp.sdk.generated.models + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonObject + +/** + * DoorService entity from the Basecamp API. + * + * @generated from OpenAPI spec — do not edit directly + */ +@Serializable +data class DoorService( + val name: String? = null, + @SerialName("example_url") val exampleUrl: String? = null, + val code: String? = null, + @SerialName("valid_patterns") val validPatterns: List = emptyList(), + @SerialName("supporting_text") val supportingText: String? = null +) diff --git a/kotlin/sdk/src/commonMain/kotlin/com/basecamp/sdk/generated/models/EverythingFile.kt b/kotlin/sdk/src/commonMain/kotlin/com/basecamp/sdk/generated/models/EverythingFile.kt new file mode 100644 index 000000000..c0459879e --- /dev/null +++ b/kotlin/sdk/src/commonMain/kotlin/com/basecamp/sdk/generated/models/EverythingFile.kt @@ -0,0 +1,48 @@ +package com.basecamp.sdk.generated.models + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonObject +import com.basecamp.sdk.serialization.FlexibleIntSerializer + +/** + * EverythingFile entity from the Basecamp API. + * + * @generated from OpenAPI spec — do not edit directly + */ +@Serializable +data class EverythingFile( + val id: Long = 0L, + val status: String? = null, + @SerialName("visible_to_clients") val visibleToClients: Boolean = false, + @SerialName("created_at") val createdAt: String? = null, + @SerialName("updated_at") val updatedAt: String? = null, + val title: String? = null, + @SerialName("inherits_status") val inheritsStatus: Boolean = false, + val type: String? = null, + val url: String? = null, + @SerialName("app_url") val appUrl: String? = null, + @SerialName("bookmark_url") val bookmarkUrl: String? = null, + @SerialName("subscription_url") val subscriptionUrl: String? = null, + @SerialName("comments_count") val commentsCount: Int = 0, + @SerialName("comments_url") val commentsUrl: String? = null, + @SerialName("boosts_count") val boostsCount: Int = 0, + @SerialName("boosts_url") val boostsUrl: String? = null, + val position: Int = 0, + val parent: RecordingParent? = null, + val bucket: RecordingBucket? = null, + val creator: Person? = null, + @SerialName("attachable_sgid") val attachableSgid: String? = null, + @SerialName("content_type") val contentType: String? = null, + @SerialName("byte_size") val byteSize: Long = 0L, + val filename: String? = null, + @SerialName("download_url") val downloadUrl: String? = null, + @SerialName("app_download_url") val appDownloadUrl: String? = null, + @Serializable(with = FlexibleIntSerializer::class) + val width: Int? = null, + @Serializable(with = FlexibleIntSerializer::class) + val height: Int? = null, + val description: String? = null, + @SerialName("description_attachments") val descriptionAttachments: List? = null +) diff --git a/kotlin/sdk/src/commonMain/kotlin/com/basecamp/sdk/generated/models/Recording.kt b/kotlin/sdk/src/commonMain/kotlin/com/basecamp/sdk/generated/models/Recording.kt index 6b9a71ef1..dff55d691 100644 --- a/kotlin/sdk/src/commonMain/kotlin/com/basecamp/sdk/generated/models/Recording.kt +++ b/kotlin/sdk/src/commonMain/kotlin/com/basecamp/sdk/generated/models/Recording.kt @@ -22,7 +22,6 @@ data class Recording( val type: String, val url: String, @SerialName("app_url") val appUrl: String, - val parent: RecordingParent, val bucket: RecordingBucket, val creator: Person, @SerialName("bookmark_url") val bookmarkUrl: String? = null, @@ -31,5 +30,9 @@ data class Recording( @SerialName("description_attachments") val descriptionAttachments: List? = null, @SerialName("comments_count") val commentsCount: Int = 0, @SerialName("comments_url") val commentsUrl: String? = null, - @SerialName("subscription_url") val subscriptionUrl: String? = null + @SerialName("subscription_url") val subscriptionUrl: String? = null, + val position: Int = 0, + val description: String? = null, + val service: DoorService? = null, + val parent: RecordingParent? = null ) diff --git a/kotlin/sdk/src/commonMain/kotlin/com/basecamp/sdk/generated/services/Types.kt b/kotlin/sdk/src/commonMain/kotlin/com/basecamp/sdk/generated/services/Types.kt index afab0fe30..5d79a4e62 100644 --- a/kotlin/sdk/src/commonMain/kotlin/com/basecamp/sdk/generated/services/Types.kt +++ b/kotlin/sdk/src/commonMain/kotlin/com/basecamp/sdk/generated/services/Types.kt @@ -217,6 +217,56 @@ data class CreateDocumentBody( val visibleToClients: Boolean? = null ) +/** Options for GetEverythingBoosts. */ +data class GetEverythingBoostsOptions( + val page: Long? = null, + val maxItems: Int? = null +) { + fun toPaginationOptions(): PaginationOptions = PaginationOptions(maxItems = maxItems) +} + +/** Options for GetEverythingCheckins. */ +data class GetEverythingCheckinsOptions( + val page: Long? = null, + val maxItems: Int? = null +) { + fun toPaginationOptions(): PaginationOptions = PaginationOptions(maxItems = maxItems) +} + +/** Options for GetEverythingComments. */ +data class GetEverythingCommentsOptions( + val page: Long? = null, + val maxItems: Int? = null +) { + fun toPaginationOptions(): PaginationOptions = PaginationOptions(maxItems = maxItems) +} + +/** Options for GetEverythingFiles. */ +data class GetEverythingFilesOptions( + val kind: String? = null, + val peopleIds: List? = null, + val page: Long? = null, + val maxItems: Int? = null +) { + fun toPaginationOptions(): PaginationOptions = PaginationOptions(maxItems = maxItems) +} + +/** Options for GetEverythingForwards. */ +data class GetEverythingForwardsOptions( + val page: Long? = null, + val maxItems: Int? = null +) { + fun toPaginationOptions(): PaginationOptions = PaginationOptions(maxItems = maxItems) +} + +/** Options for GetEverythingMessages. */ +data class GetEverythingMessagesOptions( + val page: Long? = null, + val maxItems: Int? = null +) { + fun toPaginationOptions(): PaginationOptions = PaginationOptions(maxItems = maxItems) +} + /** Request body for CreateForwardReply. */ data class CreateForwardReplyBody( val content: String diff --git a/kotlin/sdk/src/commonMain/kotlin/com/basecamp/sdk/generated/services/everything.kt b/kotlin/sdk/src/commonMain/kotlin/com/basecamp/sdk/generated/services/everything.kt new file mode 100644 index 000000000..d99f78d12 --- /dev/null +++ b/kotlin/sdk/src/commonMain/kotlin/com/basecamp/sdk/generated/services/everything.kt @@ -0,0 +1,192 @@ +package com.basecamp.sdk.generated.services + +import com.basecamp.sdk.* +import com.basecamp.sdk.generated.models.* +import com.basecamp.sdk.services.BaseService +import kotlinx.serialization.json.JsonElement + +/** + * Service for Everything operations. + * + * @generated from OpenAPI spec — do not edit directly + */ +class EverythingService(client: AccountClient) : BaseService(client) { + + /** + * Get every boost across all accessible projects, newest-first (paginated). + * @param options Optional query parameters and pagination control + */ + suspend fun everythingBoosts(options: GetEverythingBoostsOptions? = null): ListResult { + val info = OperationInfo( + service = "Everything", + operation = "GetEverythingBoosts", + resourceType = "everything_boost", + isMutation = false, + projectId = null, + resourceId = null, + ) + val qs = buildQueryString( + "page" to options?.page, + ) + return requestPaginated(info, options?.toPaginationOptions(), { + httpGet("/boosts.json" + qs, operationName = info.operation) + }) { body -> + json.decodeFromString>(body) + } + } + + /** + * Get every overdue card across all accessible projects, oldest-due-date-first. + */ + suspend fun everythingOverdueCards(): List { + val info = OperationInfo( + service = "Everything", + operation = "GetEverythingOverdueCards", + resourceType = "everything_overdue_card", + isMutation = false, + projectId = null, + resourceId = null, + ) + return request(info, { + httpGet("/cards/overdue.json", operationName = info.operation) + }) { body -> + json.decodeFromString>(body) + } + } + + /** + * Get every automatic check-in answer across all accessible projects, + * @param options Optional query parameters and pagination control + */ + suspend fun everythingCheckins(options: GetEverythingCheckinsOptions? = null): ListResult { + val info = OperationInfo( + service = "Everything", + operation = "GetEverythingCheckins", + resourceType = "everything_checkin", + isMutation = false, + projectId = null, + resourceId = null, + ) + val qs = buildQueryString( + "page" to options?.page, + ) + return requestPaginated(info, options?.toPaginationOptions(), { + httpGet("/checkins.json" + qs, operationName = info.operation) + }) { body -> + json.decodeFromString>(body) + } + } + + /** + * Get every comment across all accessible projects, newest-first (paginated). + * @param options Optional query parameters and pagination control + */ + suspend fun everythingComments(options: GetEverythingCommentsOptions? = null): ListResult { + val info = OperationInfo( + service = "Everything", + operation = "GetEverythingComments", + resourceType = "everything_comment", + isMutation = false, + projectId = null, + resourceId = null, + ) + val qs = buildQueryString( + "page" to options?.page, + ) + return requestPaginated(info, options?.toPaginationOptions(), { + httpGet("/comments.json" + qs, operationName = info.operation) + }) { body -> + json.decodeFromString>(body) + } + } + + /** + * Get every file recording across all accessible projects, newest-first + * @param options Optional query parameters and pagination control + */ + suspend fun everythingFiles(options: GetEverythingFilesOptions? = null): ListResult { + val info = OperationInfo( + service = "Everything", + operation = "GetEverythingFiles", + resourceType = "everything_file", + isMutation = false, + projectId = null, + resourceId = null, + ) + val qs = buildQueryString( + "kind" to options?.kind, + "people_ids[]" to options?.peopleIds, + "page" to options?.page, + ) + return requestPaginated(info, options?.toPaginationOptions(), { + httpGet("/files.json" + qs, operationName = info.operation) + }) { body -> + json.decodeFromString>(body) + } + } + + /** + * Get every inbox forward across all accessible projects, newest-first + * @param options Optional query parameters and pagination control + */ + suspend fun everythingForwards(options: GetEverythingForwardsOptions? = null): ListResult { + val info = OperationInfo( + service = "Everything", + operation = "GetEverythingForwards", + resourceType = "everything_forward", + isMutation = false, + projectId = null, + resourceId = null, + ) + val qs = buildQueryString( + "page" to options?.page, + ) + return requestPaginated(info, options?.toPaginationOptions(), { + httpGet("/forwards.json" + qs, operationName = info.operation) + }) { body -> + json.decodeFromString>(body) + } + } + + /** + * Get every message across all accessible projects, newest-first (paginated). + * @param options Optional query parameters and pagination control + */ + suspend fun everythingMessages(options: GetEverythingMessagesOptions? = null): ListResult { + val info = OperationInfo( + service = "Everything", + operation = "GetEverythingMessages", + resourceType = "everything_message", + isMutation = false, + projectId = null, + resourceId = null, + ) + val qs = buildQueryString( + "page" to options?.page, + ) + return requestPaginated(info, options?.toPaginationOptions(), { + httpGet("/messages.json" + qs, operationName = info.operation) + }) { body -> + json.decodeFromString>(body) + } + } + + /** + * Get every overdue to-do across all accessible projects, oldest-due-date-first. + */ + suspend fun everythingOverdueTodos(): List { + val info = OperationInfo( + service = "Everything", + operation = "GetEverythingOverdueTodos", + resourceType = "everything_overdue_todo", + isMutation = false, + projectId = null, + resourceId = null, + ) + return request(info, { + httpGet("/todos/overdue.json", operationName = info.operation) + }) { body -> + json.decodeFromString>(body) + } + } +} diff --git a/kotlin/sdk/src/commonMain/kotlin/com/basecamp/sdk/generated/services/recordings.kt b/kotlin/sdk/src/commonMain/kotlin/com/basecamp/sdk/generated/services/recordings.kt index 3ebfab40f..d57ab4f90 100644 --- a/kotlin/sdk/src/commonMain/kotlin/com/basecamp/sdk/generated/services/recordings.kt +++ b/kotlin/sdk/src/commonMain/kotlin/com/basecamp/sdk/generated/services/recordings.kt @@ -14,7 +14,7 @@ class RecordingsService(client: AccountClient) : BaseService(client) { /** * List recordings of a given type across projects - * @param type Comment|Document|Kanban::Card|Kanban::Step|Message|Question::Answer|Schedule::Entry|Todo|Todolist|Upload|Vault + * @param type Comment|Document|Door|Kanban::Card|Kanban::Step|Message|Question::Answer|Schedule::Entry|Todo|Todolist|Upload|Vault * @param options Optional query parameters and pagination control */ suspend fun list(type: String, options: ListRecordingsOptions? = null): ListResult { diff --git a/kotlin/sdk/src/commonTest/kotlin/com/basecamp/sdk/EverythingFilesDecodeTest.kt b/kotlin/sdk/src/commonTest/kotlin/com/basecamp/sdk/EverythingFilesDecodeTest.kt new file mode 100644 index 000000000..9d7b2b24d --- /dev/null +++ b/kotlin/sdk/src/commonTest/kotlin/com/basecamp/sdk/EverythingFilesDecodeTest.kt @@ -0,0 +1,103 @@ +package com.basecamp.sdk + +import com.basecamp.sdk.generated.models.EverythingFile +import kotlinx.serialization.json.Json +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull + +class EverythingFilesDecodeTest { + + private val json = Json { ignoreUnknownKeys = true } + + // Canonical /files.json "everything" feed: a heterogeneous superset carrying a full + // Upload recording, a Basecamp Document recording, and a rich-text Attachment envelope + // in a single array. everythingFiles(options) decodes this as List; here + // we prove the generated EverythingFile superset decodes every variant's real per-variant + // fields at runtime. + private val fixtureJson = """ + [ + { + "id": 900, + "type": "Upload", + "status": "active", + "visible_to_clients": false, + "title": "logo.png", + "inherits_status": true, + "filename": "logo.png", + "content_type": "image/png", + "byte_size": 1281, + "width": 1024.0, + "height": 768.0, + "url": "https://3.basecampapi.com/1/buckets/2/uploads/900.json", + "app_url": "https://3.basecamp.com/1/buckets/2/uploads/900", + "download_url": "https://3.basecampapi.com/1/buckets/2/uploads/900/download/logo.png", + "app_download_url": "https://storage.3.basecamp.com/1/buckets/2/uploads/900/download/logo.png", + "bucket": { "id": 2, "name": "The Leto Laptop", "type": "Project" }, + "creator": { "id": 1, "name": "Victor Cooper" } + }, + { + "id": 901, + "type": "Document", + "status": "active", + "visible_to_clients": false, + "title": "Spec", + "inherits_status": true, + "content_type": "text/html", + "url": "https://3.basecampapi.com/1/buckets/2/documents/901.json", + "app_url": "https://3.basecamp.com/1/buckets/2/documents/901", + "bucket": { "id": 2, "name": "The Leto Laptop", "type": "Project" }, + "creator": { "id": 1, "name": "Victor Cooper" } + }, + { + "id": 902, + "type": "Attachment", + "attachable_sgid": "sgid-902", + "filename": "chart.avif", + "content_type": "image/avif", + "byte_size": 4096, + "width": null, + "height": null, + "download_url": "https://storage.3.basecamp.com/1/blobs/902/download/chart.avif", + "parent": { + "id": 800, + "title": "A message", + "type": "Message", + "url": "https://3.basecampapi.com/1/buckets/2/messages/800.json", + "app_url": "https://3.basecamp.com/1/buckets/2/messages/800" + } + } + ] + """.trimIndent() + + @Test + fun decodesHeterogeneousEverythingFilesFeed() { + val files = json.decodeFromString>(fixtureJson) + + assertEquals(3, files.size) + + // Variant 0: full Upload recording; FlexibleIntSerializer collapses 1024.0 -> 1024. + assertEquals("Upload", files[0].type) + assertEquals("logo.png", files[0].filename) + assertNotNull(files[0].appDownloadUrl) + assertEquals( + "https://storage.3.basecamp.com/1/buckets/2/uploads/900/download/logo.png", + files[0].appDownloadUrl, + ) + assertEquals(1024, files[0].width) + + // Variant 1: Basecamp Document recording. + assertEquals("Document", files[1].type) + assertEquals("Spec", files[1].title) + + // Variant 2: rich-text Attachment envelope; null image dimensions decode to null. + assertEquals("Attachment", files[2].type) + assertEquals("sgid-902", files[2].attachableSgid) + assertEquals("chart.avif", files[2].filename) + assertNull(files[2].width) + // The attachment envelope identifies the doc/message the file lives in. + assertNotNull(files[2].parent) + assertEquals("Message", files[2].parent?.type) + } +} diff --git a/openapi.json b/openapi.json index 23e43ad57..abd0dc878 100644 --- a/openapi.json +++ b/openapi.json @@ -492,6 +492,104 @@ } } }, + "/{accountId}/boosts.json": { + "get": { + "description": "Get every boost across all accessible projects, newest-first (paginated).\nEach boost carries its `booster` and the `recording` it boosts.", + "operationId": "GetEverythingBoosts", + "parameters": [ + { + "name": "accountId", + "in": "path", + "description": "Basecamp account ID (numeric string)", + "schema": { + "type": "string", + "pattern": "^[0-9]+$", + "description": "Basecamp account ID (numeric string)" + }, + "required": true + }, + { + "name": "page", + "in": "query", + "description": "Page number for paginating through results. Defaults to 1.", + "schema": { + "type": "integer", + "description": "Page number for paginating through results. Defaults to 1.", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "GetEverythingBoosts 200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetEverythingBoostsResponseContent" + } + } + } + }, + "401": { + "description": "UnauthorizedError 401 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedErrorResponseContent" + } + } + } + }, + "403": { + "description": "ForbiddenError 403 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForbiddenErrorResponseContent" + } + } + } + }, + "429": { + "description": "RateLimitError 429 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RateLimitErrorResponseContent" + } + } + } + }, + "500": { + "description": "InternalServerError 500 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InternalServerErrorResponseContent" + } + } + } + } + }, + "tags": [ + "Everything" + ], + "x-basecamp-pagination": { + "style": "link", + "totalCountHeader": "X-Total-Count", + "maxPageSize": 50 + }, + "x-basecamp-retry": { + "maxAttempts": 3, + "baseDelayMs": 1000, + "backoff": "exponential", + "retryOn": [ + 429, + 503 + ] + } + } + }, "/{accountId}/boosts/{boostId}": { "delete": { "description": "Delete a boost", @@ -3449,6 +3547,79 @@ } } }, + "/{accountId}/cards/overdue.json": { + "get": { + "description": "Get every overdue card across all accessible projects, oldest-due-date-first.\nA complete, unpaginated array; each item embeds its `bucket`.", + "operationId": "GetEverythingOverdueCards", + "parameters": [ + { + "name": "accountId", + "in": "path", + "description": "Basecamp account ID (numeric string)", + "schema": { + "type": "string", + "pattern": "^[0-9]+$", + "description": "Basecamp account ID (numeric string)" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "GetEverythingOverdueCards 200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetEverythingOverdueCardsResponseContent" + } + } + } + }, + "401": { + "description": "UnauthorizedError 401 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedErrorResponseContent" + } + } + } + }, + "403": { + "description": "ForbiddenError 403 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForbiddenErrorResponseContent" + } + } + } + }, + "500": { + "description": "InternalServerError 500 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InternalServerErrorResponseContent" + } + } + } + } + }, + "tags": [ + "Everything" + ], + "x-basecamp-retry": { + "maxAttempts": 3, + "baseDelayMs": 1000, + "backoff": "exponential", + "retryOn": [ + 429, + 503 + ] + } + } + }, "/{accountId}/categories.json": { "get": { "description": "List message types in a project\n\n**Pagination**: Uses Link header (RFC5988). Follow the `next` rel URL\nto fetch additional pages. X-Total-Count header provides total count.", @@ -5414,6 +5585,104 @@ } } }, + "/{accountId}/checkins.json": { + "get": { + "description": "Get every automatic check-in answer across all accessible projects,\nnewest-first (paginated). Each item embeds its `bucket`.", + "operationId": "GetEverythingCheckins", + "parameters": [ + { + "name": "accountId", + "in": "path", + "description": "Basecamp account ID (numeric string)", + "schema": { + "type": "string", + "pattern": "^[0-9]+$", + "description": "Basecamp account ID (numeric string)" + }, + "required": true + }, + { + "name": "page", + "in": "query", + "description": "Page number for paginating through results. Defaults to 1.", + "schema": { + "type": "integer", + "description": "Page number for paginating through results. Defaults to 1.", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "GetEverythingCheckins 200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetEverythingCheckinsResponseContent" + } + } + } + }, + "401": { + "description": "UnauthorizedError 401 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedErrorResponseContent" + } + } + } + }, + "403": { + "description": "ForbiddenError 403 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForbiddenErrorResponseContent" + } + } + } + }, + "429": { + "description": "RateLimitError 429 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RateLimitErrorResponseContent" + } + } + } + }, + "500": { + "description": "InternalServerError 500 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InternalServerErrorResponseContent" + } + } + } + } + }, + "tags": [ + "Everything" + ], + "x-basecamp-pagination": { + "style": "link", + "totalCountHeader": "X-Total-Count", + "maxPageSize": 50 + }, + "x-basecamp-retry": { + "maxAttempts": 3, + "baseDelayMs": 1000, + "backoff": "exponential", + "retryOn": [ + 429, + 503 + ] + } + } + }, "/{accountId}/circles/people.json": { "get": { "description": "List all account users who can be pinged\n\n**Pagination**: Uses Link header (RFC5988). Follow the `next` rel URL\nto fetch additional pages. X-Total-Count header provides total count.", @@ -6096,10 +6365,10 @@ } } }, - "/{accountId}/comments/{commentId}": { + "/{accountId}/comments.json": { "get": { - "description": "Get a single comment by id", - "operationId": "GetComment", + "description": "Get every comment across all accessible projects, newest-first (paginated).\nEach item embeds its `bucket`.", + "operationId": "GetEverythingComments", "parameters": [ { "name": "accountId", @@ -6113,12 +6382,110 @@ "required": true }, { - "name": "commentId", - "in": "path", + "name": "page", + "in": "query", + "description": "Page number for paginating through results. Defaults to 1.", "schema": { "type": "integer", - "format": "int64" - }, + "description": "Page number for paginating through results. Defaults to 1.", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "GetEverythingComments 200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetEverythingCommentsResponseContent" + } + } + } + }, + "401": { + "description": "UnauthorizedError 401 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedErrorResponseContent" + } + } + } + }, + "403": { + "description": "ForbiddenError 403 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForbiddenErrorResponseContent" + } + } + } + }, + "429": { + "description": "RateLimitError 429 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RateLimitErrorResponseContent" + } + } + } + }, + "500": { + "description": "InternalServerError 500 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InternalServerErrorResponseContent" + } + } + } + } + }, + "tags": [ + "Everything" + ], + "x-basecamp-pagination": { + "style": "link", + "totalCountHeader": "X-Total-Count", + "maxPageSize": 50 + }, + "x-basecamp-retry": { + "maxAttempts": 3, + "baseDelayMs": 1000, + "backoff": "exponential", + "retryOn": [ + 429, + 503 + ] + } + } + }, + "/{accountId}/comments/{commentId}": { + "get": { + "description": "Get a single comment by id", + "operationId": "GetComment", + "parameters": [ + { + "name": "accountId", + "in": "path", + "description": "Basecamp account ID (numeric string)", + "schema": { + "type": "string", + "pattern": "^[0-9]+$", + "description": "Basecamp account ID (numeric string)" + }, + "required": true + }, + { + "name": "commentId", + "in": "path", + "schema": { + "type": "integer", + "format": "int64" + }, "required": true } ], @@ -6654,7 +7021,237 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/NotFoundErrorResponseContent" + "$ref": "#/components/schemas/NotFoundErrorResponseContent" + } + } + } + }, + "500": { + "description": "InternalServerError 500 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InternalServerErrorResponseContent" + } + } + } + } + }, + "tags": [ + "Files" + ], + "x-basecamp-retry": { + "maxAttempts": 3, + "baseDelayMs": 1000, + "backoff": "exponential", + "retryOn": [ + 429, + 503 + ] + } + }, + "put": { + "description": "Update an existing document", + "operationId": "UpdateDocument", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateDocumentRequestContent" + } + } + } + }, + "parameters": [ + { + "name": "accountId", + "in": "path", + "description": "Basecamp account ID (numeric string)", + "schema": { + "type": "string", + "pattern": "^[0-9]+$", + "description": "Basecamp account ID (numeric string)" + }, + "required": true + }, + { + "name": "documentId", + "in": "path", + "schema": { + "type": "integer", + "format": "int64" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "UpdateDocument 200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateDocumentResponseContent" + } + } + } + }, + "401": { + "description": "UnauthorizedError 401 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedErrorResponseContent" + } + } + } + }, + "403": { + "description": "ForbiddenError 403 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForbiddenErrorResponseContent" + } + } + } + }, + "404": { + "description": "NotFoundError 404 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundErrorResponseContent" + } + } + } + }, + "422": { + "description": "ValidationError 422 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ValidationErrorResponseContent" + } + } + } + }, + "500": { + "description": "InternalServerError 500 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InternalServerErrorResponseContent" + } + } + } + } + }, + "tags": [ + "Files" + ], + "x-basecamp-idempotent": { + "natural": true + }, + "x-basecamp-retry": { + "maxAttempts": 3, + "baseDelayMs": 1000, + "backoff": "exponential", + "retryOn": [ + 429, + 503 + ] + } + } + }, + "/{accountId}/files.json": { + "get": { + "description": "Get every file recording across all accessible projects, newest-first\n(paginated). Heterogeneous: uploads and Basecamp documents carry their\nstandard recording shapes, while rich-text attachments are wrapped in a\nrecording envelope plus an `attachable_sgid` and blob metadata. Modeled as\nan optional-field superset (EverythingFile) so one element type decodes any\nvariant.", + "operationId": "GetEverythingFiles", + "parameters": [ + { + "name": "accountId", + "in": "path", + "description": "Basecamp account ID (numeric string)", + "schema": { + "type": "string", + "pattern": "^[0-9]+$", + "description": "Basecamp account ID (numeric string)" + }, + "required": true + }, + { + "name": "kind", + "in": "query", + "description": "Filter by file kind: all (default), images, pdfs, documents, or videos.", + "schema": { + "type": "string", + "description": "Filter by file kind: all (default), images, pdfs, documents, or videos." + } + }, + { + "name": "people_ids[]", + "in": "query", + "description": "Restrict to files created by the given people (repeatable).", + "style": "form", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int64" + }, + "description": "Restrict to files created by the given people (repeatable).", + "x-go-type-skip-optional-pointer": false + }, + "explode": true + }, + { + "name": "page", + "in": "query", + "description": "Page number for paginating through results. Defaults to 1.", + "schema": { + "type": "integer", + "description": "Page number for paginating through results. Defaults to 1.", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "GetEverythingFiles 200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetEverythingFilesResponseContent" + } + } + } + }, + "401": { + "description": "UnauthorizedError 401 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedErrorResponseContent" + } + } + } + }, + "403": { + "description": "ForbiddenError 403 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForbiddenErrorResponseContent" + } + } + } + }, + "429": { + "description": "RateLimitError 429 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RateLimitErrorResponseContent" } } } @@ -6671,8 +7268,13 @@ } }, "tags": [ - "Files" + "Everything" ], + "x-basecamp-pagination": { + "style": "link", + "totalCountHeader": "X-Total-Count", + "maxPageSize": 50 + }, "x-basecamp-retry": { "maxAttempts": 3, "baseDelayMs": 1000, @@ -6682,19 +7284,12 @@ 503 ] } - }, - "put": { - "description": "Update an existing document", - "operationId": "UpdateDocument", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateDocumentRequestContent" - } - } - } - }, + } + }, + "/{accountId}/forwards.json": { + "get": { + "description": "Get every inbox forward across all accessible projects, newest-first\n(paginated). Each item embeds its `bucket`.", + "operationId": "GetEverythingForwards", "parameters": [ { "name": "accountId", @@ -6708,22 +7303,23 @@ "required": true }, { - "name": "documentId", - "in": "path", + "name": "page", + "in": "query", + "description": "Page number for paginating through results. Defaults to 1.", "schema": { "type": "integer", - "format": "int64" - }, - "required": true + "description": "Page number for paginating through results. Defaults to 1.", + "format": "int32" + } } ], "responses": { "200": { - "description": "UpdateDocument 200 response", + "description": "GetEverythingForwards 200 response", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UpdateDocumentResponseContent" + "$ref": "#/components/schemas/GetEverythingForwardsResponseContent" } } } @@ -6748,22 +7344,12 @@ } } }, - "404": { - "description": "NotFoundError 404 response", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/NotFoundErrorResponseContent" - } - } - } - }, - "422": { - "description": "ValidationError 422 response", + "429": { + "description": "RateLimitError 429 response", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ValidationErrorResponseContent" + "$ref": "#/components/schemas/RateLimitErrorResponseContent" } } } @@ -6780,10 +7366,12 @@ } }, "tags": [ - "Files" + "Everything" ], - "x-basecamp-idempotent": { - "natural": true + "x-basecamp-pagination": { + "style": "link", + "totalCountHeader": "X-Total-Count", + "maxPageSize": 50 }, "x-basecamp-retry": { "maxAttempts": 3, @@ -8400,6 +8988,104 @@ } } }, + "/{accountId}/messages.json": { + "get": { + "description": "Get every message across all accessible projects, newest-first (paginated).\nEach item embeds its `bucket` for project context.", + "operationId": "GetEverythingMessages", + "parameters": [ + { + "name": "accountId", + "in": "path", + "description": "Basecamp account ID (numeric string)", + "schema": { + "type": "string", + "pattern": "^[0-9]+$", + "description": "Basecamp account ID (numeric string)" + }, + "required": true + }, + { + "name": "page", + "in": "query", + "description": "Page number for paginating through results. Defaults to 1.", + "schema": { + "type": "integer", + "description": "Page number for paginating through results. Defaults to 1.", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "GetEverythingMessages 200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetEverythingMessagesResponseContent" + } + } + } + }, + "401": { + "description": "UnauthorizedError 401 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedErrorResponseContent" + } + } + } + }, + "403": { + "description": "ForbiddenError 403 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForbiddenErrorResponseContent" + } + } + } + }, + "429": { + "description": "RateLimitError 429 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RateLimitErrorResponseContent" + } + } + } + }, + "500": { + "description": "InternalServerError 500 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InternalServerErrorResponseContent" + } + } + } + } + }, + "tags": [ + "Everything" + ], + "x-basecamp-pagination": { + "style": "link", + "totalCountHeader": "X-Total-Count", + "maxPageSize": 50 + }, + "x-basecamp-retry": { + "maxAttempts": 3, + "baseDelayMs": 1000, + "backoff": "exponential", + "retryOn": [ + 429, + 503 + ] + } + } + }, "/{accountId}/messages/{messageId}": { "get": { "description": "Get a single message by id", @@ -10253,10 +10939,10 @@ { "name": "type", "in": "query", - "description": "Comment|Document|Kanban::Card|Kanban::Step|Message|Question::Answer|Schedule::Entry|Todo|Todolist|Upload|Vault", + "description": "Comment|Document|Door|Kanban::Card|Kanban::Step|Message|Question::Answer|Schedule::Entry|Todo|Todolist|Upload|Vault", "schema": { "type": "string", - "description": "Comment|Document|Kanban::Card|Kanban::Step|Message|Question::Answer|Schedule::Entry|Todo|Todolist|Upload|Vault" + "description": "Comment|Document|Door|Kanban::Card|Kanban::Step|Message|Question::Answer|Schedule::Entry|Todo|Todolist|Upload|Vault" }, "required": true, "examples": { @@ -18776,20 +19462,122 @@ 503 ] } - }, - "post": { - "description": "Create a new todo in a todolist", - "operationId": "CreateTodo", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateTodoRequestContent" - } - } - }, - "required": true - }, + }, + "post": { + "description": "Create a new todo in a todolist", + "operationId": "CreateTodo", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateTodoRequestContent" + } + } + }, + "required": true + }, + "parameters": [ + { + "name": "accountId", + "in": "path", + "description": "Basecamp account ID (numeric string)", + "schema": { + "type": "string", + "pattern": "^[0-9]+$", + "description": "Basecamp account ID (numeric string)" + }, + "required": true + }, + { + "name": "todolistId", + "in": "path", + "schema": { + "type": "integer", + "format": "int64" + }, + "required": true + } + ], + "responses": { + "201": { + "description": "CreateTodo 201 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateTodoResponseContent" + } + } + } + }, + "401": { + "description": "UnauthorizedError 401 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedErrorResponseContent" + } + } + } + }, + "403": { + "description": "ForbiddenError 403 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForbiddenErrorResponseContent" + } + } + } + }, + "422": { + "description": "ValidationError 422 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ValidationErrorResponseContent" + } + } + } + }, + "429": { + "description": "RateLimitError 429 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RateLimitErrorResponseContent" + } + } + } + }, + "500": { + "description": "InternalServerError 500 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InternalServerErrorResponseContent" + } + } + } + } + }, + "tags": [ + "Todos" + ], + "x-basecamp-retry": { + "maxAttempts": 3, + "baseDelayMs": 1000, + "backoff": "exponential", + "retryOn": [ + 429, + 503 + ] + } + } + }, + "/{accountId}/todos/overdue.json": { + "get": { + "description": "Get every overdue to-do across all accessible projects, oldest-due-date-first.\nA complete, unpaginated array; each item embeds its `bucket`.", + "operationId": "GetEverythingOverdueTodos", "parameters": [ { "name": "accountId", @@ -18801,24 +19589,15 @@ "description": "Basecamp account ID (numeric string)" }, "required": true - }, - { - "name": "todolistId", - "in": "path", - "schema": { - "type": "integer", - "format": "int64" - }, - "required": true } ], "responses": { - "201": { - "description": "CreateTodo 201 response", + "200": { + "description": "GetEverythingOverdueTodos 200 response", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CreateTodoResponseContent" + "$ref": "#/components/schemas/GetEverythingOverdueTodosResponseContent" } } } @@ -18843,26 +19622,6 @@ } } }, - "422": { - "description": "ValidationError 422 response", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ValidationErrorResponseContent" - } - } - } - }, - "429": { - "description": "RateLimitError 429 response", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RateLimitErrorResponseContent" - } - } - } - }, "500": { "description": "InternalServerError 500 response", "content": { @@ -18875,7 +19634,7 @@ } }, "tags": [ - "Todos" + "Everything" ], "x-basecamp-retry": { "maxAttempts": 3, @@ -23844,6 +24603,30 @@ "visible_to_clients" ] }, + "DoorService": { + "type": "object", + "description": "Metadata describing the recognized external service backing an external link\n(`Door` recording): its display name, a canonical example URL, a short code,\nthe URL patterns Basecamp recognizes for it, and human supporting text. `code`\nis `other` for a generic link.", + "properties": { + "name": { + "type": "string" + }, + "example_url": { + "type": "string" + }, + "code": { + "type": "string" + }, + "valid_patterns": { + "type": "array", + "items": { + "type": "string" + } + }, + "supporting_text": { + "type": "string" + } + } + }, "EnableCardColumnOnHoldResponseContent": { "$ref": "#/components/schemas/CardColumn" }, @@ -23933,6 +24716,144 @@ } } }, + "EverythingFile": { + "type": "object", + "description": "A single item in the /files.json feed. An optional-field superset over three\nwire variants — a full Upload recording, a Basecamp Document recording, and a\nrich-text attachment wrapped in a recording envelope (distinguished by\n`attachable_sgid` and blob metadata). Every field is optional; a given\ninstance populates only the fields of the variant it represents. Unknown\nfields are ignored by every SDK decoder, so the superset need not enumerate\nevery field of the Upload/Document recordings.", + "properties": { + "id": { + "type": "integer", + "description": "Recording (Upload/Document) or attachment id.", + "format": "int64", + "x-go-type-skip-optional-pointer": false + }, + "status": { + "type": "string" + }, + "visible_to_clients": { + "type": "boolean", + "x-go-type-skip-optional-pointer": false + }, + "created_at": { + "type": "string", + "x-go-type": "time.Time", + "x-go-type-import": { + "path": "time" + }, + "x-go-type-skip-optional-pointer": false + }, + "updated_at": { + "type": "string", + "x-go-type": "time.Time", + "x-go-type-import": { + "path": "time" + }, + "x-go-type-skip-optional-pointer": false + }, + "title": { + "type": "string" + }, + "inherits_status": { + "type": "boolean", + "x-go-type-skip-optional-pointer": false + }, + "type": { + "type": "string", + "description": "\"Upload\", \"Document\", or \"Attachment\"." + }, + "url": { + "type": "string" + }, + "app_url": { + "type": "string" + }, + "bookmark_url": { + "type": "string" + }, + "subscription_url": { + "type": "string" + }, + "comments_count": { + "type": "integer", + "format": "int32" + }, + "comments_url": { + "type": "string" + }, + "boosts_count": { + "type": "integer", + "format": "int32" + }, + "boosts_url": { + "type": "string" + }, + "position": { + "type": "integer", + "format": "int32" + }, + "parent": { + "$ref": "#/components/schemas/RecordingParent" + }, + "bucket": { + "$ref": "#/components/schemas/RecordingBucket" + }, + "creator": { + "$ref": "#/components/schemas/Person" + }, + "attachable_sgid": { + "type": "string", + "description": "Present on the rich-text attachment variant: signed global id of the\nattachment (uploads/documents omit it)." + }, + "content_type": { + "type": "string" + }, + "byte_size": { + "type": "integer", + "format": "int64" + }, + "filename": { + "type": "string" + }, + "download_url": { + "type": "string", + "x-basecamp-auth-routable-url": {} + }, + "app_download_url": { + "type": "string" + }, + "width": { + "type": "integer", + "description": "Pixel width; null for non-image blobs and may be float-spelled (1024.0).", + "format": "int32", + "nullable": true, + "x-go-type": "types.FlexInt", + "x-go-type-import": { + "path": "github.com/basecamp/basecamp-sdk/go/pkg/types" + }, + "x-go-type-skip-optional-pointer": false + }, + "height": { + "type": "integer", + "description": "Pixel height; null for non-image blobs and may be float-spelled (1024.0).", + "format": "int32", + "nullable": true, + "x-go-type": "types.FlexInt", + "x-go-type-import": { + "path": "github.com/basecamp/basecamp-sdk/go/pkg/types" + }, + "x-go-type-skip-optional-pointer": false + }, + "description": { + "type": "string", + "description": "Rich-text description (upload/document variants)." + }, + "description_attachments": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextAttachment" + } + } + } + }, "FirstWeekDay": { "type": "string", "enum": [ @@ -24450,6 +25371,54 @@ "GetDocumentResponseContent": { "$ref": "#/components/schemas/Document" }, + "GetEverythingBoostsResponseContent": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Boost" + } + }, + "GetEverythingCheckinsResponseContent": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Recording" + } + }, + "GetEverythingCommentsResponseContent": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Recording" + } + }, + "GetEverythingFilesResponseContent": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EverythingFile" + } + }, + "GetEverythingForwardsResponseContent": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Recording" + } + }, + "GetEverythingMessagesResponseContent": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Recording" + } + }, + "GetEverythingOverdueCardsResponseContent": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Card" + } + }, + "GetEverythingOverdueTodosResponseContent": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Todo" + } + }, "GetForwardReplyResponseContent": { "$ref": "#/components/schemas/ForwardReply" }, @@ -26591,7 +27560,8 @@ "type": "string" }, "url": { - "type": "string" + "type": "string", + "description": "API URL of the recording. Exception: in the `type=Door` (external-link)\nprojection, `url` is the door's **external destination address** (e.g. the\nFigma/Dropbox URL) and `app_url` is the Basecamp redirector — see the\ndoor-specific `service`/`description` fields below." }, "app_url": { "type": "string" @@ -26626,6 +27596,18 @@ "subscription_url": { "type": "string" }, + "position": { + "type": "integer", + "description": "Ordinal position within the project's External links section. Present on\n`Door` (external-link) recordings.", + "format": "int32" + }, + "description": { + "type": "string", + "description": "Rich-text (HTML) description shown beneath an external link. Present only\non `Door` recordings returned by the `type=Door` recordings query (the only\nendpoint that returns the full door shape). The external destination\naddress is `url` (not this field); `app_url` is the Basecamp redirector.\nSee `spec/api-gaps/external-links-doors.md`." + }, + "service": { + "$ref": "#/components/schemas/DoorService" + }, "parent": { "$ref": "#/components/schemas/RecordingParent" }, @@ -26643,7 +27625,6 @@ "creator", "id", "inherits_status", - "parent", "status", "title", "type", diff --git a/python/scripts/generate_services.py b/python/scripts/generate_services.py index 203c2ead1..ece97e624 100644 --- a/python/scripts/generate_services.py +++ b/python/scripts/generate_services.py @@ -454,7 +454,13 @@ def parse_operation( success = operation.get("responses", {}).get("200") or operation.get("responses", {}).get("201") response_schema = (success or {}).get("content", {}).get("application/json", {}).get("schema") returns_void = response_schema is None - returns_array = (response_schema or {}).get("type") == "array" + # Resolve through a $ref so bare-array ResponseContent aliases (e.g. the + # unpaginated overdue lists, whose response is a $ref to `Todo[]`) are + # detected as arrays rather than falling through to a dict return type. + resolved_response = response_schema + if isinstance(response_schema, dict) and "$ref" in response_schema: + resolved_response = resolve_schema_ref(response_schema, schemas) or response_schema + returns_array = (resolved_response or {}).get("type") == "array" # Pagination pagination = operation.get("x-basecamp-pagination") @@ -647,7 +653,17 @@ def operation_kwarg(op: dict) -> str: def is_paginated_list(op: dict) -> bool: - return (op["returns_array"] or op["has_pagination"]) and not op["pagination_key"] + # Link-header paginated: the operation is explicitly marked paginated. A bare + # array without that marker is a complete, unpaginated collection (see + # is_unpaginated_array) and must NOT follow Link headers. + return op["has_pagination"] and not op["pagination_key"] + + +def is_unpaginated_array(op: dict) -> bool: + # Returns a bare array but is not marked paginated: the whole collection comes + # back in a single response (e.g. the overdue todo/card feeds). Fetched with a + # single request, no Link-following — matching the other SDKs' plain decode. + return op["returns_array"] and not op["has_pagination"] and not op["pagination_key"] def is_wrapped_paginated(op: dict) -> bool: @@ -659,7 +675,7 @@ def return_type(op: dict) -> str: return "None" if is_wrapped_paginated(op): return "dict[str, Any]" - if is_paginated_list(op): + if is_paginated_list(op) or is_unpaginated_array(op): return "ListResult" return "dict[str, Any]" @@ -687,6 +703,14 @@ def generate_method_body(op: dict, service_name: str, *, is_async: bool) -> list lines.append(" )") else: lines.append(f" return {_await(is_async)}self._request_paginated(OperationInfo({info_kwargs}), {path_expr})") + elif is_unpaginated_array(op): + if op["query_params"]: + lines.append(f" return {_await(is_async)}self._request_list(") + lines.append(f" OperationInfo({info_kwargs}), {path_expr},") + lines.append(f" params={build_query_params_expr(op)},") + lines.append(" )") + else: + lines.append(f" return {_await(is_async)}self._request_list(OperationInfo({info_kwargs}), {path_expr})") elif op["has_binary_body"]: # Binary upload if op["query_params"]: diff --git a/python/src/basecamp/async_client.py b/python/src/basecamp/async_client.py index 7dc4d9e4b..be41eb6af 100644 --- a/python/src/basecamp/async_client.py +++ b/python/src/basecamp/async_client.py @@ -349,6 +349,12 @@ def timeline(self): return self._service("timeline", lambda: AsyncTimelineService(self)) + @property + def everything(self): + from basecamp.generated.services.everything import AsyncEverythingService + + return self._service("everything", lambda: AsyncEverythingService(self)) + @property def tools(self): from basecamp.generated.services.tools import AsyncToolsService diff --git a/python/src/basecamp/client.py b/python/src/basecamp/client.py index 80c496e3e..ad308a821 100644 --- a/python/src/basecamp/client.py +++ b/python/src/basecamp/client.py @@ -350,6 +350,12 @@ def timeline(self): return self._service("timeline", lambda: TimelineService(self)) + @property + def everything(self): + from basecamp.generated.services.everything import EverythingService + + return self._service("everything", lambda: EverythingService(self)) + @property def tools(self): from basecamp.generated.services.tools import ToolsService diff --git a/python/src/basecamp/generated/metadata.json b/python/src/basecamp/generated/metadata.json index c6cf346a4..3ca2959fc 100644 --- a/python/src/basecamp/generated/metadata.json +++ b/python/src/basecamp/generated/metadata.json @@ -751,6 +751,94 @@ ] } }, + "GetEverythingBoosts": { + "retry": { + "backoff": "exponential", + "base_delay_ms": 1000, + "max": 3, + "retry_on": [ + 429, + 503 + ] + } + }, + "GetEverythingCheckins": { + "retry": { + "backoff": "exponential", + "base_delay_ms": 1000, + "max": 3, + "retry_on": [ + 429, + 503 + ] + } + }, + "GetEverythingComments": { + "retry": { + "backoff": "exponential", + "base_delay_ms": 1000, + "max": 3, + "retry_on": [ + 429, + 503 + ] + } + }, + "GetEverythingFiles": { + "retry": { + "backoff": "exponential", + "base_delay_ms": 1000, + "max": 3, + "retry_on": [ + 429, + 503 + ] + } + }, + "GetEverythingForwards": { + "retry": { + "backoff": "exponential", + "base_delay_ms": 1000, + "max": 3, + "retry_on": [ + 429, + 503 + ] + } + }, + "GetEverythingMessages": { + "retry": { + "backoff": "exponential", + "base_delay_ms": 1000, + "max": 3, + "retry_on": [ + 429, + 503 + ] + } + }, + "GetEverythingOverdueCards": { + "retry": { + "backoff": "exponential", + "base_delay_ms": 1000, + "max": 3, + "retry_on": [ + 429, + 503 + ] + } + }, + "GetEverythingOverdueTodos": { + "retry": { + "backoff": "exponential", + "base_delay_ms": 1000, + "max": 3, + "retry_on": [ + 429, + 503 + ] + } + }, "GetForward": { "retry": { "backoff": "exponential", diff --git a/python/src/basecamp/generated/services/__init__.py b/python/src/basecamp/generated/services/__init__.py index 9466a9cfd..23ba27a96 100644 --- a/python/src/basecamp/generated/services/__init__.py +++ b/python/src/basecamp/generated/services/__init__.py @@ -20,6 +20,7 @@ from basecamp.generated.services.comments import CommentsService, AsyncCommentsService from basecamp.generated.services.documents import DocumentsService, AsyncDocumentsService from basecamp.generated.services.events import EventsService, AsyncEventsService +from basecamp.generated.services.everything import EverythingService, AsyncEverythingService from basecamp.generated.services.forwards import ForwardsService, AsyncForwardsService from basecamp.generated.services.gauges import GaugesService, AsyncGaugesService from basecamp.generated.services.hill_charts import HillChartsService, AsyncHillChartsService @@ -84,6 +85,8 @@ "AsyncDocumentsService", "EventsService", "AsyncEventsService", + "EverythingService", + "AsyncEverythingService", "ForwardsService", "AsyncForwardsService", "GaugesService", diff --git a/python/src/basecamp/generated/services/_async_base.py b/python/src/basecamp/generated/services/_async_base.py index 3f825141d..dac6bddbc 100644 --- a/python/src/basecamp/generated/services/_async_base.py +++ b/python/src/basecamp/generated/services/_async_base.py @@ -75,6 +75,36 @@ async def _request( safe_hook(self._hooks.on_operation_end, info, OperationResult(duration_ms=duration_ms, error=e)) raise + async def _request_list( + self, + info: OperationInfo, + path: str, + *, + params: dict | None = None, + ) -> ListResult: + """Fetch a complete, unpaginated array in a single request. + + Unlike ``_request_paginated`` this never follows ``Link`` headers: the + endpoint returns the whole collection at once (e.g. the overdue todo/card + feeds, sorted oldest-first). Matches the plain full-array decode the other + SDKs use for these routes. + """ + start = time.monotonic() + safe_hook(self._hooks.on_operation_start, info) + try: + response = await self._client.http.get(self._client.account_path(path), params=params) + _security.check_body_size(response.content, _security.MAX_RESPONSE_BODY_BYTES) + items = response.json() + _normalize_person_ids(items) + total_count = parse_total_count(dict(response.headers)) + duration_ms = int((time.monotonic() - start) * 1000) + safe_hook(self._hooks.on_operation_end, info, OperationResult(duration_ms=duration_ms)) + return ListResult(items, ListMeta(total_count=total_count, truncated=False)) + except Exception as e: + duration_ms = int((time.monotonic() - start) * 1000) + safe_hook(self._hooks.on_operation_end, info, OperationResult(duration_ms=duration_ms, error=e)) + raise + async def _request_void( self, info: OperationInfo, diff --git a/python/src/basecamp/generated/services/_base.py b/python/src/basecamp/generated/services/_base.py index d5aa6a853..9ca799735 100644 --- a/python/src/basecamp/generated/services/_base.py +++ b/python/src/basecamp/generated/services/_base.py @@ -82,6 +82,36 @@ def _request( safe_hook(self._hooks.on_operation_end, info, OperationResult(duration_ms=duration_ms, error=e)) raise + def _request_list( + self, + info: OperationInfo, + path: str, + *, + params: dict | None = None, + ) -> ListResult: + """Fetch a complete, unpaginated array in a single request. + + Unlike ``_request_paginated`` this never follows ``Link`` headers: the + endpoint returns the whole collection at once (e.g. the overdue todo/card + feeds, sorted oldest-first). Matches the plain full-array decode the other + SDKs use for these routes. + """ + start = time.monotonic() + safe_hook(self._hooks.on_operation_start, info) + try: + response = self._client.http.get(self._client.account_path(path), params=params) + _security.check_body_size(response.content, _security.MAX_RESPONSE_BODY_BYTES) + items = response.json() + _normalize_person_ids(items) + total_count = parse_total_count(dict(response.headers)) + duration_ms = int((time.monotonic() - start) * 1000) + safe_hook(self._hooks.on_operation_end, info, OperationResult(duration_ms=duration_ms)) + return ListResult(items, ListMeta(total_count=total_count, truncated=False)) + except Exception as e: + duration_ms = int((time.monotonic() - start) * 1000) + safe_hook(self._hooks.on_operation_end, info, OperationResult(duration_ms=duration_ms, error=e)) + raise + def _request_void( self, info: OperationInfo, diff --git a/python/src/basecamp/generated/services/automation.py b/python/src/basecamp/generated/services/automation.py index a3f00316b..e8a8991ce 100644 --- a/python/src/basecamp/generated/services/automation.py +++ b/python/src/basecamp/generated/services/automation.py @@ -11,18 +11,16 @@ class AutomationService(BaseService): - def list_lineup_markers(self) -> dict[str, Any]: - return self._request( + def list_lineup_markers(self) -> ListResult: + return self._request_list( OperationInfo(service="automation", operation="list_lineup_markers", is_mutation=False), - "GET", "/lineup/markers.json", ) class AsyncAutomationService(AsyncBaseService): - async def list_lineup_markers(self) -> dict[str, Any]: - return await self._request( + async def list_lineup_markers(self) -> ListResult: + return await self._request_list( OperationInfo(service="automation", operation="list_lineup_markers", is_mutation=False), - "GET", "/lineup/markers.json", ) diff --git a/python/src/basecamp/generated/services/everything.py b/python/src/basecamp/generated/services/everything.py new file mode 100644 index 000000000..d2fee438b --- /dev/null +++ b/python/src/basecamp/generated/services/everything.py @@ -0,0 +1,126 @@ +# @generated from OpenAPI spec — do not edit manually + +from __future__ import annotations + +from typing import Any + +from basecamp.generated.services._base import BaseService +from basecamp.generated.services._async_base import AsyncBaseService +from basecamp._pagination import ListResult +from basecamp.hooks import OperationInfo + + +class EverythingService(BaseService): + def get_everything_boosts(self, *, page: int | None = None) -> ListResult: + return self._request_paginated( + OperationInfo(service="everything", operation="get_everything_boosts", is_mutation=False), + "/boosts.json", + params=self._compact(page=page), + ) + + def get_everything_overdue_cards(self) -> ListResult: + return self._request_list( + OperationInfo(service="everything", operation="get_everything_overdue_cards", is_mutation=False), + "/cards/overdue.json", + ) + + def get_everything_checkins(self, *, page: int | None = None) -> ListResult: + return self._request_paginated( + OperationInfo(service="everything", operation="get_everything_checkins", is_mutation=False), + "/checkins.json", + params=self._compact(page=page), + ) + + def get_everything_comments(self, *, page: int | None = None) -> ListResult: + return self._request_paginated( + OperationInfo(service="everything", operation="get_everything_comments", is_mutation=False), + "/comments.json", + params=self._compact(page=page), + ) + + def get_everything_files( + self, *, kind: str | None = None, people_ids: list[int] | None = None, page: int | None = None + ) -> ListResult: + return self._request_paginated( + OperationInfo(service="everything", operation="get_everything_files", is_mutation=False), + "/files.json", + params={k: v for k, v in {"kind": kind, "people_ids[]": people_ids, "page": page}.items() if v is not None}, + ) + + def get_everything_forwards(self, *, page: int | None = None) -> ListResult: + return self._request_paginated( + OperationInfo(service="everything", operation="get_everything_forwards", is_mutation=False), + "/forwards.json", + params=self._compact(page=page), + ) + + def get_everything_messages(self, *, page: int | None = None) -> ListResult: + return self._request_paginated( + OperationInfo(service="everything", operation="get_everything_messages", is_mutation=False), + "/messages.json", + params=self._compact(page=page), + ) + + def get_everything_overdue_todos(self) -> ListResult: + return self._request_list( + OperationInfo(service="everything", operation="get_everything_overdue_todos", is_mutation=False), + "/todos/overdue.json", + ) + + +class AsyncEverythingService(AsyncBaseService): + async def get_everything_boosts(self, *, page: int | None = None) -> ListResult: + return await self._request_paginated( + OperationInfo(service="everything", operation="get_everything_boosts", is_mutation=False), + "/boosts.json", + params=self._compact(page=page), + ) + + async def get_everything_overdue_cards(self) -> ListResult: + return await self._request_list( + OperationInfo(service="everything", operation="get_everything_overdue_cards", is_mutation=False), + "/cards/overdue.json", + ) + + async def get_everything_checkins(self, *, page: int | None = None) -> ListResult: + return await self._request_paginated( + OperationInfo(service="everything", operation="get_everything_checkins", is_mutation=False), + "/checkins.json", + params=self._compact(page=page), + ) + + async def get_everything_comments(self, *, page: int | None = None) -> ListResult: + return await self._request_paginated( + OperationInfo(service="everything", operation="get_everything_comments", is_mutation=False), + "/comments.json", + params=self._compact(page=page), + ) + + async def get_everything_files( + self, *, kind: str | None = None, people_ids: list[int] | None = None, page: int | None = None + ) -> ListResult: + return await self._request_paginated( + OperationInfo(service="everything", operation="get_everything_files", is_mutation=False), + "/files.json", + params={k: v for k, v in {"kind": kind, "people_ids[]": people_ids, "page": page}.items() if v is not None}, + ) + + async def get_everything_forwards(self, *, page: int | None = None) -> ListResult: + return await self._request_paginated( + OperationInfo(service="everything", operation="get_everything_forwards", is_mutation=False), + "/forwards.json", + params=self._compact(page=page), + ) + + async def get_everything_messages(self, *, page: int | None = None) -> ListResult: + return await self._request_paginated( + OperationInfo(service="everything", operation="get_everything_messages", is_mutation=False), + "/messages.json", + params=self._compact(page=page), + ) + + async def get_everything_overdue_todos(self) -> ListResult: + return await self._request_list( + OperationInfo(service="everything", operation="get_everything_overdue_todos", is_mutation=False), + "/todos/overdue.json", + ) diff --git a/python/src/basecamp/generated/services/my_assignments.py b/python/src/basecamp/generated/services/my_assignments.py index 6bf12169d..16377a596 100644 --- a/python/src/basecamp/generated/services/my_assignments.py +++ b/python/src/basecamp/generated/services/my_assignments.py @@ -18,17 +18,15 @@ def get_my_assignments(self) -> dict[str, Any]: "/my/assignments.json", ) - def get_my_completed_assignments(self) -> dict[str, Any]: - return self._request( + def get_my_completed_assignments(self) -> ListResult: + return self._request_list( OperationInfo(service="myassignments", operation="get_my_completed_assignments", is_mutation=False), - "GET", "/my/assignments/completed.json", ) - def get_my_due_assignments(self, *, scope: str | None = None) -> dict[str, Any]: - return self._request( + def get_my_due_assignments(self, *, scope: str | None = None) -> ListResult: + return self._request_list( OperationInfo(service="myassignments", operation="get_my_due_assignments", is_mutation=False), - "GET", "/my/assignments/due.json", params=self._compact(scope=scope), ) @@ -42,17 +40,15 @@ async def get_my_assignments(self) -> dict[str, Any]: "/my/assignments.json", ) - async def get_my_completed_assignments(self) -> dict[str, Any]: - return await self._request( + async def get_my_completed_assignments(self) -> ListResult: + return await self._request_list( OperationInfo(service="myassignments", operation="get_my_completed_assignments", is_mutation=False), - "GET", "/my/assignments/completed.json", ) - async def get_my_due_assignments(self, *, scope: str | None = None) -> dict[str, Any]: - return await self._request( + async def get_my_due_assignments(self, *, scope: str | None = None) -> ListResult: + return await self._request_list( OperationInfo(service="myassignments", operation="get_my_due_assignments", is_mutation=False), - "GET", "/my/assignments/due.json", params=self._compact(scope=scope), ) diff --git a/python/src/basecamp/generated/services/people.py b/python/src/basecamp/generated/services/people.py index 1b5bf953d..2ff534f98 100644 --- a/python/src/basecamp/generated/services/people.py +++ b/python/src/basecamp/generated/services/people.py @@ -124,10 +124,9 @@ def update_project_access( operation="UpdateProjectAccess", ) - def list_assignable(self) -> dict[str, Any]: - return self._request( + def list_assignable(self) -> ListResult: + return self._request_list( OperationInfo(service="people", operation="list_assignable", is_mutation=False), - "GET", "/reports/todos/assigned.json", ) @@ -246,9 +245,8 @@ async def update_project_access( operation="UpdateProjectAccess", ) - async def list_assignable(self) -> dict[str, Any]: - return await self._request( + async def list_assignable(self) -> ListResult: + return await self._request_list( OperationInfo(service="people", operation="list_assignable", is_mutation=False), - "GET", "/reports/todos/assigned.json", ) diff --git a/python/src/basecamp/generated/services/timesheets.py b/python/src/basecamp/generated/services/timesheets.py index a2667cdea..2700dd28b 100644 --- a/python/src/basecamp/generated/services/timesheets.py +++ b/python/src/basecamp/generated/services/timesheets.py @@ -40,12 +40,9 @@ def create( operation="CreateTimesheetEntry", ) - def report( - self, *, from_: str | None = None, to: str | None = None, person_id: int | None = None - ) -> dict[str, Any]: - return self._request( + def report(self, *, from_: str | None = None, to: str | None = None, person_id: int | None = None) -> ListResult: + return self._request_list( OperationInfo(service="timesheets", operation="report", is_mutation=False), - "GET", "/reports/timesheet.json", params={k: v for k, v in {"from": from_, "to": to, "person_id": person_id}.items() if v is not None}, ) @@ -107,10 +104,9 @@ async def create( async def report( self, *, from_: str | None = None, to: str | None = None, person_id: int | None = None - ) -> dict[str, Any]: - return await self._request( + ) -> ListResult: + return await self._request_list( OperationInfo(service="timesheets", operation="report", is_mutation=False), - "GET", "/reports/timesheet.json", params={k: v for k, v in {"from": from_, "to": to, "person_id": person_id}.items() if v is not None}, ) diff --git a/python/src/basecamp/generated/types.py b/python/src/basecamp/generated/types.py index 83f3aecff..a8dac6fea 100644 --- a/python/src/basecamp/generated/types.py +++ b/python/src/basecamp/generated/types.py @@ -584,6 +584,14 @@ class Document(TypedDict): visible_to_clients: bool +class DoorService(TypedDict): + code: NotRequired[str] + example_url: NotRequired[str] + name: NotRequired[str] + supporting_text: NotRequired[str] + valid_patterns: NotRequired[list[str]] + + class EnableOutOfOfficeRequestContent(TypedDict): out_of_office: OutOfOfficePayload @@ -605,6 +613,39 @@ class EventDetails(TypedDict): removed_person_ids: NotRequired[list[int]] +class EverythingFile(TypedDict): + app_download_url: NotRequired[str] + app_url: NotRequired[str] + attachable_sgid: NotRequired[str] + bookmark_url: NotRequired[str] + boosts_count: NotRequired[int] + boosts_url: NotRequired[str] + bucket: NotRequired[RecordingBucket] + byte_size: NotRequired[int] + comments_count: NotRequired[int] + comments_url: NotRequired[str] + content_type: NotRequired[str] + created_at: NotRequired[str] + creator: NotRequired[Person] + description: NotRequired[str] + description_attachments: NotRequired[list[RichTextAttachment]] + download_url: NotRequired[str] + filename: NotRequired[str] + height: NotRequired[Optional[int | float]] + id: NotRequired[int] + inherits_status: NotRequired[bool] + parent: NotRequired[RecordingParent] + position: NotRequired[int] + status: NotRequired[str] + subscription_url: NotRequired[str] + title: NotRequired[str] + type: NotRequired[str] + updated_at: NotRequired[str] + url: NotRequired[str] + visible_to_clients: NotRequired[bool] + width: NotRequired[Optional[int | float]] + + class ForbiddenErrorResponseContent(TypedDict): error: str message: NotRequired[str] @@ -1174,10 +1215,13 @@ class Recording(TypedDict): content_attachments: NotRequired[list[RichTextAttachment]] created_at: str creator: Person + description: NotRequired[str] description_attachments: NotRequired[list[RichTextAttachment]] id: int inherits_status: bool - parent: RecordingParent + parent: NotRequired[RecordingParent] + position: NotRequired[int] + service: NotRequired[DoorService] status: str subscription_url: NotRequired[str] title: str diff --git a/python/tests/services/test_everything.py b/python/tests/services/test_everything.py new file mode 100644 index 000000000..e3739f67d --- /dev/null +++ b/python/tests/services/test_everything.py @@ -0,0 +1,400 @@ +"""Tests for the heterogeneous /files.json "everything" feed.""" + +from __future__ import annotations + +import httpx +import pytest +import respx + +from basecamp import Client +from basecamp.errors import NotFoundError + +# Canonical heterogeneous feed: a full Upload recording, a Basecamp Document +# recording, and a rich-text Attachment envelope in a single non-empty array. +_FILES_FEED = [ + { + "id": 900, + "type": "Upload", + "status": "active", + "visible_to_clients": False, + "title": "logo.png", + "inherits_status": True, + "filename": "logo.png", + "content_type": "image/png", + "byte_size": 1281, + "width": 1024.0, + "height": 768.0, + "url": "https://3.basecampapi.com/1/buckets/2/uploads/900.json", + "app_url": "https://3.basecamp.com/1/buckets/2/uploads/900", + "download_url": "https://3.basecampapi.com/1/buckets/2/uploads/900/download/logo.png", + "app_download_url": "https://storage.3.basecamp.com/1/buckets/2/uploads/900/download/logo.png", + "bucket": {"id": 2, "name": "The Leto Laptop", "type": "Project"}, + "creator": {"id": 1, "name": "Victor Cooper"}, + }, + { + "id": 901, + "type": "Document", + "status": "active", + "visible_to_clients": False, + "title": "Spec", + "inherits_status": True, + "content_type": "text/html", + "url": "https://3.basecampapi.com/1/buckets/2/documents/901.json", + "app_url": "https://3.basecamp.com/1/buckets/2/documents/901", + "bucket": {"id": 2, "name": "The Leto Laptop", "type": "Project"}, + "creator": {"id": 1, "name": "Victor Cooper"}, + }, + { + "id": 902, + "type": "Attachment", + "attachable_sgid": "sgid-902", + "filename": "chart.avif", + "content_type": "image/avif", + "byte_size": 4096, + "width": None, + "height": None, + "download_url": "https://storage.3.basecamp.com/1/blobs/902/download/chart.avif", + "parent": {"id": 800, "title": "A message", "type": "Message"}, + }, +] + + +class TestEverythingFiles: + @respx.mock + def test_heterogeneous_files_feed_decodes_all_three_variants(self): + route = respx.get("https://3.basecampapi.com/12345/files.json").mock( + return_value=httpx.Response(200, json=_FILES_FEED) + ) + + account = Client(access_token="test-token").for_account("12345") + result = account.everything.get_everything_files(kind=None, people_ids=None) + + assert route.called + assert len(result) == 3 + + upload = result[0] + assert upload["type"] == "Upload" + assert upload["filename"] == "logo.png" + assert "app_download_url" in upload + assert upload["width"] == 1024.0 + + document = result[1] + assert document["type"] == "Document" + assert document["title"] == "Spec" + + attachment = result[2] + assert attachment["type"] == "Attachment" + assert attachment["attachable_sgid"] == "sgid-902" + assert "parent" in attachment + assert attachment["width"] is None + + +# A recording root embeds a `bucket` (the project it lives in). Messages, +# comments, check-in entries, and forwards are all recordings. +_MESSAGES_FEED = [ + { + "id": 1000, + "type": "Message", + "status": "active", + "visible_to_clients": False, + "title": "Kickoff", + "content": "
Let's go
", + "bucket": {"id": 2, "name": "The Leto Laptop", "type": "Project"}, + "creator": {"id": 1, "name": "Victor Cooper"}, + }, + { + "id": 1001, + "type": "Message", + "status": "active", + "visible_to_clients": True, + "title": "Status update", + "content": "
All green
", + "bucket": {"id": 3, "name": "Honcho Design", "type": "Project"}, + "creator": {"id": 1, "name": "Victor Cooper"}, + }, +] + +_COMMENTS_FEED = [ + { + "id": 1100, + "type": "Comment", + "status": "active", + "visible_to_clients": False, + "content": "
Nice work
", + "bucket": {"id": 2, "name": "The Leto Laptop", "type": "Project"}, + "creator": {"id": 1, "name": "Victor Cooper"}, + }, + { + "id": 1101, + "type": "Comment", + "status": "active", + "visible_to_clients": False, + "content": "
Agreed
", + "bucket": {"id": 3, "name": "Honcho Design", "type": "Project"}, + "creator": {"id": 2, "name": "Annie Bryan"}, + }, +] + +_CHECKINS_FEED = [ + { + "id": 1200, + "type": "Question::Answer", + "status": "active", + "visible_to_clients": False, + "content": "
Shipped the SDK tests
", + "bucket": {"id": 2, "name": "The Leto Laptop", "type": "Project"}, + "creator": {"id": 1, "name": "Victor Cooper"}, + }, + { + "id": 1201, + "type": "Question::Answer", + "status": "active", + "visible_to_clients": False, + "content": "
Reviewed PRs
", + "bucket": {"id": 3, "name": "Honcho Design", "type": "Project"}, + "creator": {"id": 2, "name": "Annie Bryan"}, + }, +] + +_FORWARDS_FEED = [ + { + "id": 1300, + "type": "Inbox::Forward", + "status": "active", + "visible_to_clients": False, + "subject": "FW: Invoice", + "bucket": {"id": 2, "name": "The Leto Laptop", "type": "Project"}, + "creator": {"id": 1, "name": "Victor Cooper"}, + }, + { + "id": 1301, + "type": "Inbox::Forward", + "status": "active", + "visible_to_clients": False, + "subject": "FW: Contract", + "bucket": {"id": 3, "name": "Honcho Design", "type": "Project"}, + "creator": {"id": 2, "name": "Annie Bryan"}, + }, +] + +# A boost is a tiny reaction wrapping the `recording` it was applied to. +_BOOSTS_FEED = [ + { + "id": 1400, + "content": "👍", + "created_at": "2024-01-01T00:00:00Z", + "creator": {"id": 1, "name": "Victor Cooper"}, + "recording": {"id": 1000, "type": "Message", "title": "Kickoff"}, + }, + { + "id": 1401, + "content": "🔥", + "created_at": "2024-01-02T00:00:00Z", + "creator": {"id": 2, "name": "Annie Bryan"}, + "recording": {"id": 1100, "type": "Comment", "title": "Nice work"}, + }, +] + +# Overdue feeds are unpaginated bare arrays sorted oldest-first by due_on. +_OVERDUE_TODOS_FEED = [ + { + "id": 1500, + "type": "Todo", + "status": "active", + "visible_to_clients": False, + "title": "Renew domain", + "due_on": "2024-01-01", + "bucket": {"id": 2, "name": "The Leto Laptop", "type": "Project"}, + }, + { + "id": 1501, + "type": "Todo", + "status": "active", + "visible_to_clients": False, + "title": "File taxes", + "due_on": "2024-03-15", + "bucket": {"id": 3, "name": "Honcho Design", "type": "Project"}, + }, +] + +_OVERDUE_CARDS_FEED = [ + { + "id": 1600, + "type": "Kanban::Card", + "status": "active", + "visible_to_clients": False, + "title": "Ship beta", + "due_on": "2024-02-01", + "bucket": {"id": 2, "name": "The Leto Laptop", "type": "Project"}, + }, + { + "id": 1601, + "type": "Kanban::Card", + "status": "active", + "visible_to_clients": False, + "title": "Cut release", + "due_on": "2024-04-01", + "bucket": {"id": 3, "name": "Honcho Design", "type": "Project"}, + }, +] + + +class TestEverythingRecordingFeeds: + @respx.mock + def test_messages_feed_embeds_bucket_roots(self): + route = respx.get("https://3.basecampapi.com/12345/messages.json").mock( + return_value=httpx.Response(200, json=_MESSAGES_FEED) + ) + + account = Client(access_token="test-token").for_account("12345") + result = account.everything.get_everything_messages() + + assert route.called + assert len(result) == 2 + assert result[0]["id"] == 1000 + assert result[0]["type"] == "Message" + assert result[0]["bucket"] == {"id": 2, "name": "The Leto Laptop", "type": "Project"} + assert result[1]["visible_to_clients"] is True + + @respx.mock + def test_comments_feed_embeds_bucket_roots(self): + route = respx.get("https://3.basecampapi.com/12345/comments.json").mock( + return_value=httpx.Response(200, json=_COMMENTS_FEED) + ) + + account = Client(access_token="test-token").for_account("12345") + result = account.everything.get_everything_comments() + + assert route.called + assert len(result) == 2 + assert result[0]["id"] == 1100 + assert result[0]["type"] == "Comment" + assert result[0]["bucket"]["id"] == 2 + assert result[1]["creator"]["name"] == "Annie Bryan" + + @respx.mock + def test_checkins_feed_embeds_bucket_roots(self): + route = respx.get("https://3.basecampapi.com/12345/checkins.json").mock( + return_value=httpx.Response(200, json=_CHECKINS_FEED) + ) + + account = Client(access_token="test-token").for_account("12345") + result = account.everything.get_everything_checkins() + + assert route.called + assert len(result) == 2 + assert result[0]["id"] == 1200 + assert result[0]["type"] == "Question::Answer" + assert result[0]["bucket"]["type"] == "Project" + + @respx.mock + def test_forwards_feed_embeds_bucket_roots(self): + route = respx.get("https://3.basecampapi.com/12345/forwards.json").mock( + return_value=httpx.Response(200, json=_FORWARDS_FEED) + ) + + account = Client(access_token="test-token").for_account("12345") + result = account.everything.get_everything_forwards() + + assert route.called + assert len(result) == 2 + assert result[0]["id"] == 1300 + assert result[0]["type"] == "Inbox::Forward" + assert result[0]["subject"] == "FW: Invoice" + assert result[0]["bucket"]["id"] == 2 + + @respx.mock + def test_boosts_feed_wraps_nested_recording(self): + route = respx.get("https://3.basecampapi.com/12345/boosts.json").mock( + return_value=httpx.Response(200, json=_BOOSTS_FEED) + ) + + account = Client(access_token="test-token").for_account("12345") + result = account.everything.get_everything_boosts() + + assert route.called + assert len(result) == 2 + assert result[0]["id"] == 1400 + assert result[0]["content"] == "👍" + assert result[0]["recording"] == {"id": 1000, "type": "Message", "title": "Kickoff"} + assert result[1]["recording"]["type"] == "Comment" + + +class TestEverythingOverdueFeeds: + @respx.mock + def test_overdue_todos_returns_oldest_first_bare_array(self): + route = respx.get("https://3.basecampapi.com/12345/todos/overdue.json").mock( + return_value=httpx.Response(200, json=_OVERDUE_TODOS_FEED) + ) + + account = Client(access_token="test-token").for_account("12345") + result = account.everything.get_everything_overdue_todos() + + assert route.called + assert len(result) == 2 + assert result[0]["id"] == 1500 + assert result[0]["type"] == "Todo" + assert result[0]["due_on"] == "2024-01-01" + assert result[0]["due_on"] < result[1]["due_on"] + + @respx.mock + def test_overdue_cards_returns_oldest_first_bare_array(self): + route = respx.get("https://3.basecampapi.com/12345/cards/overdue.json").mock( + return_value=httpx.Response(200, json=_OVERDUE_CARDS_FEED) + ) + + account = Client(access_token="test-token").for_account("12345") + result = account.everything.get_everything_overdue_cards() + + assert route.called + assert len(result) == 2 + assert result[0]["id"] == 1600 + assert result[0]["type"] == "Kanban::Card" + assert result[0]["due_on"] == "2024-02-01" + assert result[0]["due_on"] < result[1]["due_on"] + + @respx.mock + def test_overdue_todos_does_not_follow_link_header(self): + # The overdue feeds are complete, unpaginated arrays. Even if the server + # advertises a next page via a Link header, the SDK must issue exactly one + # request and return only that response — matching the other SDKs' plain + # full-array decode (no Link-following). + route = respx.get("https://3.basecampapi.com/12345/todos/overdue.json").mock( + return_value=httpx.Response( + 200, + json=_OVERDUE_TODOS_FEED, + headers={"Link": '; rel="next"'}, + ) + ) + + account = Client(access_token="test-token").for_account("12345") + result = account.everything.get_everything_overdue_todos() + + assert route.call_count == 1 + assert len(result) == 2 + + +# Every flat-family operation must surface a canonical 4xx as a typed error. +_FLAT_ERROR_CASES = [ + ("messages.json", lambda acct: acct.everything.get_everything_messages()), + ("comments.json", lambda acct: acct.everything.get_everything_comments()), + ("checkins.json", lambda acct: acct.everything.get_everything_checkins()), + ("forwards.json", lambda acct: acct.everything.get_everything_forwards()), + ("boosts.json", lambda acct: acct.everything.get_everything_boosts()), + ("files.json", lambda acct: acct.everything.get_everything_files(kind=None, people_ids=None)), + ("todos/overdue.json", lambda acct: acct.everything.get_everything_overdue_todos()), + ("cards/overdue.json", lambda acct: acct.everything.get_everything_overdue_cards()), +] + + +class TestEverythingErrorPropagation: + @pytest.mark.parametrize("path, call", _FLAT_ERROR_CASES) + @respx.mock + def test_flat_family_propagates_not_found(self, path, call): + respx.get(f"https://3.basecampapi.com/12345/{path}").mock( + return_value=httpx.Response(404, json={"error": "Not found"}) + ) + + account = Client(access_token="test-token").for_account("12345") + with pytest.raises(NotFoundError): + call(account) diff --git a/ruby/lib/basecamp/client.rb b/ruby/lib/basecamp/client.rb index de059723b..231f15ab1 100644 --- a/ruby/lib/basecamp/client.rb +++ b/ruby/lib/basecamp/client.rb @@ -501,6 +501,11 @@ def timeline service(:timeline) { Services::TimelineService.new(self) } end + # @return [Services::EverythingService] + def everything + service(:everything) { Services::EverythingService.new(self) } + end + # @return [Services::TimesheetsService] def timesheets service(:timesheets) { Services::TimesheetsService.new(self) } diff --git a/ruby/lib/basecamp/generated/metadata.json b/ruby/lib/basecamp/generated/metadata.json index 99069d09e..25a5aa698 100644 --- a/ruby/lib/basecamp/generated/metadata.json +++ b/ruby/lib/basecamp/generated/metadata.json @@ -1,7 +1,7 @@ { "$schema": "https://basecamp.com/schemas/sdk-metadata.json", "version": "1.0.0", - "generated": "2026-07-25T07:14:10Z", + "generated": "2026-07-25T17:48:06Z", "operations": { "GetAccount": { "retry": { @@ -67,6 +67,22 @@ ] } }, + "GetEverythingBoosts": { + "retry": { + "maxAttempts": 3, + "baseDelayMs": 1000, + "backoff": "exponential", + "retryOn": [ + 429, + 503 + ] + }, + "pagination": { + "style": "link", + "totalCountHeader": "X-Total-Count", + "maxPageSize": 50 + } + }, "GetBoost": { "retry": { "maxAttempts": 3, @@ -418,6 +434,17 @@ ] } }, + "GetEverythingOverdueCards": { + "retry": { + "maxAttempts": 3, + "baseDelayMs": 1000, + "backoff": "exponential", + "retryOn": [ + 429, + 503 + ] + } + }, "ListMessageTypes": { "retry": { "maxAttempts": 3, @@ -670,6 +697,22 @@ ] } }, + "GetEverythingCheckins": { + "retry": { + "maxAttempts": 3, + "baseDelayMs": 1000, + "backoff": "exponential", + "retryOn": [ + 429, + 503 + ] + }, + "pagination": { + "style": "link", + "totalCountHeader": "X-Total-Count", + "maxPageSize": 50 + } + }, "ListPingablePeople": { "retry": { "maxAttempts": 3, @@ -767,6 +810,22 @@ ] } }, + "GetEverythingComments": { + "retry": { + "maxAttempts": 3, + "baseDelayMs": 1000, + "backoff": "exponential", + "retryOn": [ + 429, + 503 + ] + }, + "pagination": { + "style": "link", + "totalCountHeader": "X-Total-Count", + "maxPageSize": 50 + } + }, "GetComment": { "retry": { "maxAttempts": 3, @@ -856,6 +915,38 @@ "natural": true } }, + "GetEverythingFiles": { + "retry": { + "maxAttempts": 3, + "baseDelayMs": 1000, + "backoff": "exponential", + "retryOn": [ + 429, + 503 + ] + }, + "pagination": { + "style": "link", + "totalCountHeader": "X-Total-Count", + "maxPageSize": 50 + } + }, + "GetEverythingForwards": { + "retry": { + "maxAttempts": 3, + "baseDelayMs": 1000, + "backoff": "exponential", + "retryOn": [ + 429, + 503 + ] + }, + "pagination": { + "style": "link", + "totalCountHeader": "X-Total-Count", + "maxPageSize": 50 + } + }, "GetGaugeNeedle": { "retry": { "maxAttempts": 3, @@ -1059,6 +1150,22 @@ ] } }, + "GetEverythingMessages": { + "retry": { + "maxAttempts": 3, + "baseDelayMs": 1000, + "backoff": "exponential", + "retryOn": [ + 429, + 503 + ] + }, + "pagination": { + "style": "link", + "totalCountHeader": "X-Total-Count", + "maxPageSize": 50 + } + }, "GetMessage": { "retry": { "maxAttempts": 3, @@ -2387,6 +2494,17 @@ ] } }, + "GetEverythingOverdueTodos": { + "retry": { + "maxAttempts": 3, + "baseDelayMs": 1000, + "backoff": "exponential", + "retryOn": [ + 429, + 503 + ] + } + }, "GetTodo": { "retry": { "maxAttempts": 3, diff --git a/ruby/lib/basecamp/generated/services/everything_service.rb b/ruby/lib/basecamp/generated/services/everything_service.rb new file mode 100644 index 000000000..9edb61eaa --- /dev/null +++ b/ruby/lib/basecamp/generated/services/everything_service.rb @@ -0,0 +1,89 @@ +# frozen_string_literal: true + +module Basecamp + module Services + # Service for Everything operations + # + # @generated from OpenAPI spec + class EverythingService < BaseService + + # Get every boost across all accessible projects, newest-first (paginated). + # @param page [Integer, nil] Page number for paginating through results. Defaults to 1. + # @return [Enumerator] paginated results + def get_everything_boosts(page: nil) + wrap_paginated(service: "everything", operation: "get_everything_boosts", is_mutation: false) do + params = compact_query_params(page: page) + paginate("/boosts.json", params: params) + end + end + + # Get every overdue card across all accessible projects, oldest-due-date-first. + # @return [Hash] response data + def get_everything_overdue_cards() + with_operation(service: "everything", operation: "get_everything_overdue_cards", is_mutation: false) do + http_get("/cards/overdue.json").json + end + end + + # Get every automatic check-in answer across all accessible projects, + # @param page [Integer, nil] Page number for paginating through results. Defaults to 1. + # @return [Enumerator] paginated results + def get_everything_checkins(page: nil) + wrap_paginated(service: "everything", operation: "get_everything_checkins", is_mutation: false) do + params = compact_query_params(page: page) + paginate("/checkins.json", params: params) + end + end + + # Get every comment across all accessible projects, newest-first (paginated). + # @param page [Integer, nil] Page number for paginating through results. Defaults to 1. + # @return [Enumerator] paginated results + def get_everything_comments(page: nil) + wrap_paginated(service: "everything", operation: "get_everything_comments", is_mutation: false) do + params = compact_query_params(page: page) + paginate("/comments.json", params: params) + end + end + + # Get every file recording across all accessible projects, newest-first + # @param kind [String, nil] Filter by file kind: all (default), images, pdfs, documents, or videos. + # @param people_ids [Array, nil] Restrict to files created by the given people (repeatable). + # @param page [Integer, nil] Page number for paginating through results. Defaults to 1. + # @return [Enumerator] paginated results + def get_everything_files(kind: nil, people_ids: nil, page: nil) + wrap_paginated(service: "everything", operation: "get_everything_files", is_mutation: false) do + params = compact_query_params(kind: kind, people_ids: people_ids, page: page) + paginate("/files.json", params: params) + end + end + + # Get every inbox forward across all accessible projects, newest-first + # @param page [Integer, nil] Page number for paginating through results. Defaults to 1. + # @return [Enumerator] paginated results + def get_everything_forwards(page: nil) + wrap_paginated(service: "everything", operation: "get_everything_forwards", is_mutation: false) do + params = compact_query_params(page: page) + paginate("/forwards.json", params: params) + end + end + + # Get every message across all accessible projects, newest-first (paginated). + # @param page [Integer, nil] Page number for paginating through results. Defaults to 1. + # @return [Enumerator] paginated results + def get_everything_messages(page: nil) + wrap_paginated(service: "everything", operation: "get_everything_messages", is_mutation: false) do + params = compact_query_params(page: page) + paginate("/messages.json", params: params) + end + end + + # Get every overdue to-do across all accessible projects, oldest-due-date-first. + # @return [Hash] response data + def get_everything_overdue_todos() + with_operation(service: "everything", operation: "get_everything_overdue_todos", is_mutation: false) do + http_get("/todos/overdue.json").json + end + end + end + end +end diff --git a/ruby/lib/basecamp/generated/services/recordings_service.rb b/ruby/lib/basecamp/generated/services/recordings_service.rb index 4f80558b2..4b72ca6e9 100644 --- a/ruby/lib/basecamp/generated/services/recordings_service.rb +++ b/ruby/lib/basecamp/generated/services/recordings_service.rb @@ -8,7 +8,7 @@ module Services class RecordingsService < BaseService # List recordings of a given type across projects - # @param type [String] Comment|Document|Kanban::Card|Kanban::Step|Message|Question::Answer|Schedule::Entry|Todo|Todolist|Upload|Vault + # @param type [String] Comment|Document|Door|Kanban::Card|Kanban::Step|Message|Question::Answer|Schedule::Entry|Todo|Todolist|Upload|Vault # @param bucket [String, nil] bucket # @param status [String, nil] active|archived|trashed # @param sort [String, nil] created_at|updated_at diff --git a/ruby/lib/basecamp/generated/types.rb b/ruby/lib/basecamp/generated/types.rb index 56c26cbec..f63de3956 100644 --- a/ruby/lib/basecamp/generated/types.rb +++ b/ruby/lib/basecamp/generated/types.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # Auto-generated from OpenAPI spec. Do not edit manually. -# Generated: 2026-07-25T07:14:10Z +# Generated: 2026-07-25T17:48:06Z require "json" require "time" @@ -1269,6 +1269,34 @@ def to_json(*args) end end + # DoorService + class DoorService + include TypeHelpers + attr_accessor :code, :example_url, :name, :supporting_text, :valid_patterns + + def initialize(data = {}) + @code = data["code"] + @example_url = data["example_url"] + @name = data["name"] + @supporting_text = data["supporting_text"] + @valid_patterns = data["valid_patterns"] + end + + def to_h + { + "code" => @code, + "example_url" => @example_url, + "name" => @name, + "supporting_text" => @supporting_text, + "valid_patterns" => @valid_patterns, + }.compact + end + + def to_json(*args) + to_h.to_json(*args) + end + end + # Event class Event include TypeHelpers @@ -1332,6 +1360,84 @@ def to_json(*args) end end + # EverythingFile + class EverythingFile + include TypeHelpers + attr_accessor :app_download_url, :app_url, :attachable_sgid, :bookmark_url, :boosts_count, :boosts_url, :bucket, :byte_size, :comments_count, :comments_url, :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 + + def initialize(data = {}) + @app_download_url = data["app_download_url"] + @app_url = data["app_url"] + @attachable_sgid = data["attachable_sgid"] + @bookmark_url = data["bookmark_url"] + @boosts_count = parse_integer(data["boosts_count"]) + @boosts_url = data["boosts_url"] + @bucket = parse_type(data["bucket"], "RecordingBucket") + @byte_size = parse_integer(data["byte_size"]) + @comments_count = parse_integer(data["comments_count"]) + @comments_url = data["comments_url"] + @content_type = data["content_type"] + @created_at = parse_datetime(data["created_at"]) + @creator = parse_type(data["creator"], "Person") + @description = data["description"] + @description_attachments = parse_array(data["description_attachments"], "RichTextAttachment") + @download_url = data["download_url"] + @filename = data["filename"] + @height = parse_integer(data["height"]) + @id = parse_integer(data["id"]) + @inherits_status = parse_boolean(data["inherits_status"]) + @parent = parse_type(data["parent"], "RecordingParent") + @position = parse_integer(data["position"]) + @status = data["status"] + @subscription_url = data["subscription_url"] + @title = data["title"] + @type = data["type"] + @updated_at = parse_datetime(data["updated_at"]) + @url = data["url"] + @visible_to_clients = parse_boolean(data["visible_to_clients"]) + @width = parse_integer(data["width"]) + end + + def to_h + { + "app_download_url" => @app_download_url, + "app_url" => @app_url, + "attachable_sgid" => @attachable_sgid, + "bookmark_url" => @bookmark_url, + "boosts_count" => @boosts_count, + "boosts_url" => @boosts_url, + "bucket" => @bucket, + "byte_size" => @byte_size, + "comments_count" => @comments_count, + "comments_url" => @comments_url, + "content_type" => @content_type, + "created_at" => @created_at, + "creator" => @creator, + "description" => @description, + "description_attachments" => @description_attachments, + "download_url" => @download_url, + "filename" => @filename, + "height" => @height, + "id" => @id, + "inherits_status" => @inherits_status, + "parent" => @parent, + "position" => @position, + "status" => @status, + "subscription_url" => @subscription_url, + "title" => @title, + "type" => @type, + "updated_at" => @updated_at, + "url" => @url, + "visible_to_clients" => @visible_to_clients, + "width" => @width, + }.compact + end + + def to_json(*args) + to_h.to_json(*args) + end + end + # Forward class Forward include TypeHelpers @@ -2900,11 +3006,11 @@ def to_json(*args) # Recording class Recording include TypeHelpers - 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 + attr_accessor :app_url, :bucket, :created_at, :creator, :id, :inherits_status, :status, :title, :type, :updated_at, :url, :visible_to_clients, :bookmark_url, :comments_count, :comments_url, :content, :content_attachments, :description, :description_attachments, :parent, :position, :service, :subscription_url # @return [Array] def self.required_fields - %i[app_url bucket created_at creator id inherits_status parent status title type updated_at url visible_to_clients].freeze + %i[app_url bucket created_at creator id inherits_status status title type updated_at url visible_to_clients].freeze end def initialize(data = {}) @@ -2914,7 +3020,6 @@ def initialize(data = {}) @creator = parse_type(data["creator"], "Person") @id = parse_integer(data["id"]) @inherits_status = parse_boolean(data["inherits_status"]) - @parent = parse_type(data["parent"], "RecordingParent") @status = data["status"] @title = data["title"] @type = data["type"] @@ -2926,7 +3031,11 @@ def initialize(data = {}) @comments_url = data["comments_url"] @content = data["content"] @content_attachments = parse_array(data["content_attachments"], "RichTextAttachment") + @description = data["description"] @description_attachments = parse_array(data["description_attachments"], "RichTextAttachment") + @parent = parse_type(data["parent"], "RecordingParent") + @position = parse_integer(data["position"]) + @service = parse_type(data["service"], "DoorService") @subscription_url = data["subscription_url"] end @@ -2938,7 +3047,6 @@ def to_h "creator" => @creator, "id" => @id, "inherits_status" => @inherits_status, - "parent" => @parent, "status" => @status, "title" => @title, "type" => @type, @@ -2950,7 +3058,11 @@ def to_h "comments_url" => @comments_url, "content" => @content, "content_attachments" => @content_attachments, + "description" => @description, "description_attachments" => @description_attachments, + "parent" => @parent, + "position" => @position, + "service" => @service, "subscription_url" => @subscription_url, }.compact end diff --git a/ruby/test/basecamp/services/everything_service_test.rb b/ruby/test/basecamp/services/everything_service_test.rb new file mode 100644 index 000000000..ccb40a77a --- /dev/null +++ b/ruby/test/basecamp/services/everything_service_test.rb @@ -0,0 +1,347 @@ +# frozen_string_literal: true + +# Tests for the EverythingService (generated from OpenAPI spec) +# +# Note: Generated services are spec-conformant: +# - get_everything_files() is paginated and returns the parsed JSON array +# - No client-side validation (API validates) + +require "test_helper" + +class EverythingServiceTest < Minitest::Test + include TestHelper + + def setup + @account = create_account_client(account_id: "12345") + end + + def test_get_everything_files_decodes_heterogeneous_feed + files = [ + { + "id" => 900, + "type" => "Upload", + "status" => "active", + "visible_to_clients" => false, + "title" => "logo.png", + "inherits_status" => true, + "filename" => "logo.png", + "content_type" => "image/png", + "byte_size" => 1281, + "width" => 1024.0, + "height" => 768.0, + "url" => "https://3.basecampapi.com/1/buckets/2/uploads/900.json", + "app_url" => "https://3.basecamp.com/1/buckets/2/uploads/900", + "download_url" => "https://3.basecampapi.com/1/buckets/2/uploads/900/download/logo.png", + "app_download_url" => "https://storage.3.basecamp.com/1/buckets/2/uploads/900/download/logo.png", + "bucket" => { "id" => 2, "name" => "The Leto Laptop", "type" => "Project" }, + "creator" => { "id" => 1, "name" => "Victor Cooper" } + }, + { + "id" => 901, + "type" => "Document", + "status" => "active", + "visible_to_clients" => false, + "title" => "Spec", + "inherits_status" => true, + "content_type" => "text/html", + "url" => "https://3.basecampapi.com/1/buckets/2/documents/901.json", + "app_url" => "https://3.basecamp.com/1/buckets/2/documents/901", + "bucket" => { "id" => 2, "name" => "The Leto Laptop", "type" => "Project" }, + "creator" => { "id" => 1, "name" => "Victor Cooper" } + }, + { + "id" => 902, + "type" => "Attachment", + "attachable_sgid" => "sgid-902", + "filename" => "chart.avif", + "content_type" => "image/avif", + "byte_size" => 4096, + "width" => nil, + "height" => nil, + "download_url" => "https://storage.3.basecamp.com/1/blobs/902/download/chart.avif", + "parent" => { "id" => 800, "title" => "A message", "type" => "Message" } + } + ] + + stub_get("/12345/files.json", response_body: files) + + result = @account.everything.get_everything_files(kind: nil, people_ids: nil).to_a + + assert_kind_of Array, result + assert_equal 3, result.length + + # Variant 0: full Upload recording + assert_equal "Upload", result[0]["type"] + assert_equal "logo.png", result[0]["filename"] + assert_not_nil result[0]["app_download_url"] + assert_equal 1024.0, result[0]["width"] + + # Variant 1: Basecamp Document recording + assert_equal "Document", result[1]["type"] + assert_equal "Spec", result[1]["title"] + + # Variant 2: rich-text Attachment envelope + assert_equal "Attachment", result[2]["type"] + assert_equal "sgid-902", result[2]["attachable_sgid"] + assert_not_nil result[2]["parent"] + assert_nil result[2]["width"] + end + + def test_get_everything_messages_decodes_recording_feed + messages = [ + { + "id" => 1001, + "type" => "Message", + "status" => "active", + "visible_to_clients" => false, + "title" => "Kickoff", + "bucket" => { "id" => 2, "name" => "The Leto Laptop", "type" => "Project" }, + "creator" => { "id" => 1, "name" => "Victor Cooper" } + }, + { + "id" => 1002, + "type" => "Message", + "status" => "active", + "visible_to_clients" => true, + "title" => "Update", + "bucket" => { "id" => 3, "name" => "Honcho Design", "type" => "Project" }, + "creator" => { "id" => 1, "name" => "Victor Cooper" } + } + ] + + stub_get("/12345/messages.json", response_body: messages) + + result = @account.everything.get_everything_messages.to_a + + assert_kind_of Array, result + assert_equal 2, result.length + assert_equal 1001, result[0]["id"] + assert_equal "Message", result[0]["type"] + assert_equal 2, result[0]["bucket"]["id"] + assert_equal "Project", result[0]["bucket"]["type"] + end + + def test_get_everything_comments_decodes_recording_feed + comments = [ + { + "id" => 2001, + "type" => "Comment", + "status" => "active", + "visible_to_clients" => false, + "bucket" => { "id" => 2, "name" => "The Leto Laptop", "type" => "Project" }, + "creator" => { "id" => 1, "name" => "Victor Cooper" } + }, + { + "id" => 2002, + "type" => "Comment", + "status" => "active", + "visible_to_clients" => false, + "bucket" => { "id" => 3, "name" => "Honcho Design", "type" => "Project" }, + "creator" => { "id" => 1, "name" => "Victor Cooper" } + } + ] + + stub_get("/12345/comments.json", response_body: comments) + + result = @account.everything.get_everything_comments.to_a + + assert_kind_of Array, result + assert_equal 2, result.length + assert_equal 2001, result[0]["id"] + assert_equal "Comment", result[0]["type"] + assert_equal 2, result[0]["bucket"]["id"] + end + + def test_get_everything_checkins_decodes_recording_feed + checkins = [ + { + "id" => 3001, + "type" => "Question::Answer", + "status" => "active", + "visible_to_clients" => false, + "bucket" => { "id" => 2, "name" => "The Leto Laptop", "type" => "Project" }, + "creator" => { "id" => 1, "name" => "Victor Cooper" } + }, + { + "id" => 3002, + "type" => "Question::Answer", + "status" => "active", + "visible_to_clients" => false, + "bucket" => { "id" => 3, "name" => "Honcho Design", "type" => "Project" }, + "creator" => { "id" => 1, "name" => "Victor Cooper" } + } + ] + + stub_get("/12345/checkins.json", response_body: checkins) + + result = @account.everything.get_everything_checkins.to_a + + assert_kind_of Array, result + assert_equal 2, result.length + assert_equal 3001, result[0]["id"] + assert_equal "Question::Answer", result[0]["type"] + assert_equal 2, result[0]["bucket"]["id"] + end + + def test_get_everything_forwards_decodes_recording_feed + forwards = [ + { + "id" => 4001, + "type" => "Inbox::Forward", + "status" => "active", + "visible_to_clients" => false, + "subject" => "FW: Invoice", + "bucket" => { "id" => 2, "name" => "The Leto Laptop", "type" => "Project" }, + "creator" => { "id" => 1, "name" => "Victor Cooper" } + }, + { + "id" => 4002, + "type" => "Inbox::Forward", + "status" => "active", + "visible_to_clients" => false, + "subject" => "FW: Contract", + "bucket" => { "id" => 3, "name" => "Honcho Design", "type" => "Project" }, + "creator" => { "id" => 1, "name" => "Victor Cooper" } + } + ] + + stub_get("/12345/forwards.json", response_body: forwards) + + result = @account.everything.get_everything_forwards.to_a + + assert_kind_of Array, result + assert_equal 2, result.length + assert_equal 4001, result[0]["id"] + assert_equal "Inbox::Forward", result[0]["type"] + assert_equal 2, result[0]["bucket"]["id"] + end + + def test_get_everything_boosts_decodes_boost_feed + boosts = [ + { + "id" => 5001, + "content" => "🎉", + "created_at" => "2024-01-15T10:00:00Z", + "creator" => { "id" => 1, "name" => "Victor Cooper" }, + "recording" => { + "id" => 1001, + "type" => "Message", + "title" => "Kickoff", + "bucket" => { "id" => 2, "name" => "The Leto Laptop", "type" => "Project" } + } + }, + { + "id" => 5002, + "content" => "👏", + "created_at" => "2024-01-15T09:00:00Z", + "creator" => { "id" => 1, "name" => "Victor Cooper" }, + "recording" => { + "id" => 2001, + "type" => "Comment", + "bucket" => { "id" => 3, "name" => "Honcho Design", "type" => "Project" } + } + } + ] + + stub_get("/12345/boosts.json", response_body: boosts) + + result = @account.everything.get_everything_boosts.to_a + + assert_kind_of Array, result + assert_equal 2, result.length + assert_equal 5001, result[0]["id"] + assert_equal "🎉", result[0]["content"] + assert_not_nil result[0]["recording"] + assert_equal 1001, result[0]["recording"]["id"] + assert_equal "Message", result[0]["recording"]["type"] + end + + def test_get_everything_overdue_todos_decodes_oldest_first + todos = [ + { + "id" => 6001, + "type" => "Todo", + "status" => "active", + "visible_to_clients" => false, + "title" => "Ship the thing", + "due_on" => "2024-01-01", + "bucket" => { "id" => 2, "name" => "The Leto Laptop", "type" => "Project" } + }, + { + "id" => 6002, + "type" => "Todo", + "status" => "active", + "visible_to_clients" => false, + "title" => "Review the doc", + "due_on" => "2024-02-01", + "bucket" => { "id" => 3, "name" => "Honcho Design", "type" => "Project" } + } + ] + + stub_get("/12345/todos/overdue.json", response_body: todos) + + result = @account.everything.get_everything_overdue_todos.to_a + + assert_kind_of Array, result + assert_equal 2, result.length + assert_equal 6001, result[0]["id"] + assert_equal "2024-01-01", result[0]["due_on"] + # Oldest-first ordering + assert result[0]["due_on"] < result[1]["due_on"] + end + + def test_get_everything_overdue_cards_decodes_oldest_first + cards = [ + { + "id" => 7001, + "type" => "Kanban::Card", + "status" => "active", + "visible_to_clients" => false, + "title" => "Draft proposal", + "due_on" => "2024-01-10", + "bucket" => { "id" => 2, "name" => "The Leto Laptop", "type" => "Project" } + }, + { + "id" => 7002, + "type" => "Kanban::Card", + "status" => "active", + "visible_to_clients" => false, + "title" => "Send invoice", + "due_on" => "2024-02-10", + "bucket" => { "id" => 3, "name" => "Honcho Design", "type" => "Project" } + } + ] + + stub_get("/12345/cards/overdue.json", response_body: cards) + + result = @account.everything.get_everything_overdue_cards.to_a + + assert_kind_of Array, result + assert_equal 2, result.length + assert_equal 7001, result[0]["id"] + assert_equal "2024-01-10", result[0]["due_on"] + # Oldest-first ordering + assert result[0]["due_on"] < result[1]["due_on"] + end + + # Every flat-family operation must surface a canonical 4xx as a typed error. + def test_flat_family_operations_propagate_not_found + calls = { + "/12345/messages.json" => -> { @account.everything.get_everything_messages.to_a }, + "/12345/comments.json" => -> { @account.everything.get_everything_comments.to_a }, + "/12345/checkins.json" => -> { @account.everything.get_everything_checkins.to_a }, + "/12345/forwards.json" => -> { @account.everything.get_everything_forwards.to_a }, + "/12345/boosts.json" => -> { @account.everything.get_everything_boosts.to_a }, + "/12345/files.json" => -> { @account.everything.get_everything_files(kind: nil, people_ids: nil).to_a }, + "/12345/todos/overdue.json" => -> { @account.everything.get_everything_overdue_todos.to_a }, + "/12345/cards/overdue.json" => -> { @account.everything.get_everything_overdue_cards.to_a } + } + + calls.each do |path, call| + stub_get(path, response_body: "", status: 404) + assert_raises(Basecamp::NotFoundError, "expected #{path} to raise NotFoundError") do + call.call + end + end + end +end diff --git a/scripts/enhance-openapi-go-types.sh b/scripts/enhance-openapi-go-types.sh index 09b316af1..24e94395d 100755 --- a/scripts/enhance-openapi-go-types.sh +++ b/scripts/enhance-openapi-go-types.sh @@ -226,6 +226,38 @@ walk( (.previewable // empty) += { "x-go-type-skip-optional-pointer": false } ) | +# Fifth-d pass: EverythingFile width/height → nullable *types.FlexInt +# The /files.json feed mixes uploads and attachments whose pixel dimensions are +# float-spelled (1024.0) and null for non-image blobs, exactly like +# RichTextAttachment/TimelineAttachment. Keep the optional pointer and mark +# nullable so the present-null value types faithfully. +.components.schemas.EverythingFile.properties |= ( + (.width // empty) += { + "nullable": true, + "x-go-type": "types.FlexInt", + "x-go-type-import": {"path": "github.com/basecamp/basecamp-sdk/go/pkg/types"}, + "x-go-type-skip-optional-pointer": false + } | + (.height // empty) += { + "nullable": true, + "x-go-type": "types.FlexInt", + "x-go-type-import": {"path": "github.com/basecamp/basecamp-sdk/go/pkg/types"}, + "x-go-type-skip-optional-pointer": false + } +) +| +# Fifth-f pass: EverythingFile presence-faithful optional scalars (same rationale +# as TimelineAttachment). The /files.json superset populates only one variant per +# instance, so its optional timestamps and booleans must round-trip presence: +# make created_at/updated_at *time.Time and visible_to_clients/inherits_status +# *bool so nil (absent variant) omits and an explicit false is preserved. +.components.schemas.EverythingFile.properties |= ( + (.created_at // empty) += { "x-go-type-skip-optional-pointer": false } | + (.updated_at // empty) += { "x-go-type-skip-optional-pointer": false } | + (.visible_to_clients // empty) += { "x-go-type-skip-optional-pointer": false } | + (.inherits_status // empty) += { "x-go-type-skip-optional-pointer": false } +) +| # Sixth pass: Person.id → types.FlexibleInt64 # The API sometimes returns person IDs as JSON strings (e.g. in notification # responses); Go rejects those into int64 fields. Scoped to Person schema only. diff --git a/spec/api-gaps/README.md b/spec/api-gaps/README.md index 2f04d986b..cf5bae440 100644 --- a/spec/api-gaps/README.md +++ b/spec/api-gaps/README.md @@ -39,7 +39,7 @@ making the absorption journey publicly auditable. | [calendar](calendar.md) | addressed-in-bc3-pr-12321 | 3b | medium | | [scratchpad](scratchpad.md) | addressed-in-bc3-pr-12322 | 3b | medium | | [step-top-level](step-top-level.md) | absorbed-in-sdk | 3b | low | -| [everything-aggregates](everything-aggregates.md) | addressed-in-bc3-pr-11627 | 3c | high | +| [everything-aggregates](everything-aggregates.md) | partial-coverage | 3c | high | | [activity-timeline](activity-timeline.md) | absorbed-in-sdk | 3d | high | | [recordable-subtypes-doc](recordable-subtypes-doc.md) | partial-coverage | 3a | medium | | [stack-doc-and-smithy](stack-doc-and-smithy.md) | confirmed-not-api-resource | 3b | medium | @@ -56,7 +56,7 @@ making the absorption journey publicly auditable. | [todolist-reposition](todolist-reposition.md) | absorbed-in-sdk | pre-BC5 | medium | | [rich-text-attachments-coverage](rich-text-attachments-coverage.md) | absorbed-in-sdk | n/a | medium | | [visible-to-clients-on-creates](visible-to-clients-on-creates.md) | absorbed-in-sdk | post-train | medium | -| [external-links-doors](external-links-doors.md) | addressed-in-bc3-pr-12375 | post-train | low | +| [external-links-doors](external-links-doors.md) | partial-coverage | post-train | low | | [dock-tool-visible-to-clients](dock-tool-visible-to-clients.md) | absorbed-in-sdk | post-train | low | | [card-table-wormholes](card-table-wormholes.md) | absorbed-in-sdk | post-train | medium | | [bubble-ups-surface](bubble-ups-surface.md) | absorbed-in-sdk | launch | high | diff --git a/spec/api-gaps/everything-aggregates.md b/spec/api-gaps/everything-aggregates.md index 419d1e1ba..90576df01 100644 --- a/spec/api-gaps/everything-aggregates.md +++ b/spec/api-gaps/everything-aggregates.md @@ -1,9 +1,12 @@ --- gap: everything-aggregates -status: addressed-in-bc3-pr-11627 +status: partial-coverage detected: 2026-05-01 sdk_demand: high bc3_pr: 11627 +smithy_refs: + - "EverythingService flat family: GetEverythingMessages/Comments/Checkins/Forwards/Boosts/Files + GetEverythingOverdueTodos/OverdueCards (spec/basecamp.smithy)" + - "EverythingFile superset (spec/basecamp.smithy)" bc3_refs: introduced_in: five bc3_plan_phase: 3c @@ -112,16 +115,58 @@ merged doc. ## SDK absorption plan when this lands -- New `EverythingService` with **17** operations matching the merged contract - (one per endpoint in the frontmatter route list). Re-verify the operation - list against `doc/api/sections/everything.md` at absorption time. -- Two response families: a bucket-grouped shape for the todo/card filter - sub-routes (bucket + grouped recordings + steps) and flat recording arrays - for the overdue lists and the 6 roots. Reuse existing recording shapes - (`Todo`, `Card`, `Message`, etc.) for the elements. -- Do **not** model the `//recent.json` aliases — internal web feeds, - not API contract. Do not model bare `/todos.json` / `/cards.json` (HTML - shells). -- Model the `/files.json` `kind` and `people_ids[]` query filters. -- Canary fixture: cover at least one operation per group (8 groups) to catch - shape drift. Pairwise check: BC4 absent → BC5 present is fine. +Absorbed in **two phases** across a stacked PR pair. This entry stays +`partial-coverage` until **both** phases land; it flips to `absorbed-in-sdk` +only when all 17 routes are modeled. + +**PR-5 (flat family) — DONE.** A new `EverythingService` with the 8 flat-family +operations: + +- Six recency-ordered, Link-paginated roots — `GetEverythingMessages`, + `GetEverythingComments`, `GetEverythingCheckins`, `GetEverythingForwards` + (element = the generic `Recording` projection the wire actually returns, which + embeds `bucket`), `GetEverythingBoosts` (element = `Boost`, carrying its + `booster` and nested `recording`), and `GetEverythingFiles`. +- Two unpaginated, oldest-due-date-first arrays — `GetEverythingOverdueTodos` + (`Todo`) and `GetEverythingOverdueCards` (`Card`) — modeled as plain full + arrays (single-member output, no pagination → bare array via the + `smithy-bare-arrays` transform), tested as complete oldest-first with **no** + Link-following. +- `/files.json` is **heterogeneous** (Upload + Document + attachment-envelope); + modeled as the optional-field superset `EverythingFile` (the cross-cutting + untagged-polymorphism default), with the `kind` and repeatable `people_ids[]` + query filters. **Runtime decode proof:** every SDK (Go, TS, Ruby, Python, + Kotlin, Swift) has a non-empty test decoding all three variants in one array. +- The generator auto-derives the `EverythingService` from the `Everything` tag; + client wiring added where hand-written (Go accessor, TS `defineService` **plus + package-root re-export**, Ruby `def everything`, Python sync+async properties; + Kotlin/Swift auto-wired). `EverythingFile`/`Boost` added to the TS and Kotlin + generator type registries so the paginated methods advertise `ListResult` + rather than a raw array alias; the generator now resolves `$ref` array aliases + so the unpaginated overdue methods no longer mis-advertise `dict`/`Hash`. +- Each paginated flat op carries a `page` `@httpQuery` param, forwarded by the + Go wrapper (a positive `page` fetches that page; `page 0` follows the Link + header) — previously the Go `page` argument was silently ignored. +- `EverythingFile` optional timestamps/booleans are pointer-preserving (Go) so + re-marshaling the superset omits absent-variant fields instead of fabricating + a zero timestamp or dropping an explicit `false`. +- Go wrappers with multi-page Link-following (paginated roots) and plain + full-array decode (overdue); the flat-aggregate mirror of `GetMyAssignments`. +- **Tests:** Go multi-page/overdue/files/page-forwarding tests, plus happy-path + per-op tests for all 8 flat ops in TS/Ruby/Python and per-variant `/files.json` + decode in all six SDKs. +- **Conformance/canary:** `paths.json` path-assertion entries + mock-runner + dispatch (Go/TS/Kotlin) for all 8 flat ops; a live-canary entry per group in + `live-my-surface.json` with matching `live-dispatch` cases (live canary + dormant → validates statically). + +**PR-5b (bucket-grouped family) — PENDING.** The 9 todo/card filter sub-routes +(`/todos/{open,completed,unassigned,no_due_date}.json`, +`/cards/{open,completed,unassigned,no_due_date,not_now}.json`) return a +paginated array of `{bucket, todos|cards}` with steps. Modeled + covered +(tests, page params, conformance, canary) in PR-5b, after which this entry +flips to `absorbed-in-sdk`. + +Exclusions honored: never model the `//recent.json` aliases (internal +web feeds) or the bare `/todos.json` / `/cards.json` roots (HTML shells). +Pairwise check: BC4 absent → BC5 present is fine. diff --git a/spec/api-gaps/external-links-doors.md b/spec/api-gaps/external-links-doors.md index 1bce9bd89..422132314 100644 --- a/spec/api-gaps/external-links-doors.md +++ b/spec/api-gaps/external-links-doors.md @@ -1,9 +1,13 @@ --- gap: external-links-doors -status: addressed-in-bc3-pr-12375 +status: partial-coverage detected: 2026-07-22 sdk_demand: low bc3_pr: 12375 +smithy_refs: + - "RecordingType Door enum value (spec/basecamp.smithy:6063)" + - "Recording.position/description/service (spec/basecamp.smithy:6078)" + - "DoorService (spec/basecamp.smithy:6142)" bc3_refs: routes: - GET /:account_id/projects/recordings.json?type=Door @@ -87,22 +91,50 @@ the contract is now documented and must be tracked to keep detection honest. ## SDK absorption plan when this lands -- Model the door **list** as a `type=Door`-scoped recordings query (or a - dedicated `ListExternalLinks` operation) returning the full door shape - (`url`, `service`, `description`). -- Model **create** carefully: the 302/no-JSON-body contract does not fit the - standard "create returns the resource" mold — the operation returns no - usable body, so the SDK surface must not promise one. The wire operation - should model only the 302/empty response; a create-and-discover convenience - (create, then `type=Door` list to find the new door's id) is a **separately - sanctioned composite** — it needs explicit composite treatment, conformance - coverage, and a defined answer for ambiguous concurrent/duplicate creates, - not something the plain create operation implies. -- Get/rename/trash reuse the existing dock-tool GetTool/UpdateTool/DeleteTool - operations. -- Add `Door` to the `RecordingType` documented enum. +**Partially absorbed** (basecamp-sdk PR-4 of the post-#401 follow-up program). +A pre-implementation spike resolved the three blockers and scoped this PR to the +cleanly-absorbable surface; the redirect-dependent create and the multipart +image are recorded as residual gaps below. + +**Absorbed in this PR:** + +- The door **list** is the existing `type=Door`-scoped `ListRecordings` query — + **not** a new operation. `ListRecordings` already carries a `type` query and a + `Recording.url`; the full door shape is reached by adding `Door` to the + `RecordingType` documented enum and adding optional `position`, `description`, + and a `service` struct (`DoorService`) to the shared `Recording` projection. + A runtime decode test proves the door shape (external `url` + `service` + + `description`) decodes through the projection. +- Get/rename/trash reuse the existing `GetTool` / `UpdateTool` / `DeleteTool` + operations — no new work. +- A `type=Door` recordings canary entry validates statically (live dormant). + +**Residual gaps (why this entry is `partial-coverage`, not +`absorbed-in-sdk`):** + +- **Create (`POST /buckets/:b/dock/doors.json`) — deferred.** The endpoint + returns **302 with an empty body** (no ID), redirecting cross-origin to the + web project page. The spike found the six SDK transports handle this + inconsistently: Go, TypeScript (fetch default), Kotlin (Ktor default), and + Swift (URLSession default) **follow** the redirect (landing on the web page, + after cross-origin auth stripping); Python (`follow_redirects=False`) and Ruby + (Faraday, no follow middleware) do **not** follow but then face empty-body + decode, and each classifies a bare 302 differently. Modeling "return only the + 302/empty response" would require coordinated per-operation redirect + suppression + 302-as-success handling across all six generated transports — a + cross-cutting transport change, not an additive absorption. Deferred to a + dedicated PR. +- **`image` thumbnail (multipart) — unmodeled.** `door[image]` is + `multipart/form-data` only; not modeled. Independent of the create-transport + question, this alone keeps the entry from honestly flipping to + `absorbed-in-sdk`. +- **Create-and-discover composite (SPEC §18) — deferred.** It depends on a + shippable `CreateExternalLink`, so it is out of scope until create lands. +- **No JSON update path** for `url`/`service`/`description` (PUT → 406): never + modeled, by contract. ## Compatibility -New documented resource surface; no change to existing modeled operations -beyond the additive `Door` enum value on `ListRecordings` output type. +Additive only: the `Door` enum value plus optional `position`/`description`/ +`service` fields on the shared `Recording` output type. No change to existing +modeled operations; no new operation. diff --git a/spec/basecamp.smithy b/spec/basecamp.smithy index b7b74e1c8..0af4a671e 100644 --- a/spec/basecamp.smithy +++ b/spec/basecamp.smithy @@ -289,6 +289,16 @@ service Basecamp { GetMyCompletedAssignments, GetMyDueAssignments, + // Batch 15b - Everything Aggregates (flat family) + GetEverythingMessages, + GetEverythingComments, + GetEverythingCheckins, + GetEverythingForwards, + GetEverythingBoosts, + GetEverythingFiles, + GetEverythingOverdueTodos, + GetEverythingOverdueCards, + // Batch 16 - My Notifications GetMyNotifications, GetBubbleUps, @@ -6220,7 +6230,7 @@ structure EventDetails { // ===== Recording Shapes ===== -@documentation("Comment|Document|Kanban::Card|Kanban::Step|Message|Question::Answer|Schedule::Entry|Todo|Todolist|Upload|Vault") +@documentation("Comment|Document|Door|Kanban::Card|Kanban::Step|Message|Question::Answer|Schedule::Entry|Todo|Todolist|Upload|Vault") string RecordingType @documentation("active|archived|trashed") @@ -6253,6 +6263,10 @@ structure Recording { inherits_status: Boolean @required type: String + /// API URL of the recording. Exception: in the `type=Door` (external-link) + /// projection, `url` is the door's **external destination address** (e.g. the + /// Figma/Dropbox URL) and `app_url` is the Basecamp redirector — see the + /// door-specific `service`/`description` fields below. @required url: String @required @@ -6272,7 +6286,25 @@ structure Recording { comments_count: Integer comments_url: String subscription_url: String - @required + + /// Ordinal position within the project's External links section. Present on + /// `Door` (external-link) recordings. + position: Integer + + /// Rich-text (HTML) description shown beneath an external link. Present only + /// on `Door` recordings returned by the `type=Door` recordings query (the only + /// endpoint that returns the full door shape). The external destination + /// address is `url` (not this field); `app_url` is the Basecamp redirector. + /// See `spec/api-gaps/external-links-doors.md`. + description: String + + /// Metadata about the recognized external service the link's `url` points to + /// (Figma, Dropbox, GitHub, …). Present only on `Door` recordings. + service: DoorService + + /// Parent container of the recording. Optional because `Door` (external-link) + /// recordings have no parent recording — the `type=Door` projection omits it, + /// so a strict decoder must tolerate its absence. parent: RecordingParent @required bucket: RecordingBucket @@ -6280,6 +6312,18 @@ structure Recording { creator: Person } +/// Metadata describing the recognized external service backing an external link +/// (`Door` recording): its display name, a canonical example URL, a short code, +/// the URL patterns Basecamp recognizes for it, and human supporting text. `code` +/// is `other` for a generic link. +structure DoorService { + name: String + example_url: String + code: String + valid_patterns: StringList + supporting_text: String +} + // ============================================================================= // BATCH 9 - Checkins (Questionnaires, Questions, Answers) // ============================================================================= @@ -8648,6 +8692,287 @@ structure GetMyDueAssignmentsOutput { assignments: MyAssignmentList } +// ============================================================================= +// BATCH 15b - Everything Aggregates (flat family) +// ============================================================================= +// +// Account-wide recording listings served by the everything/*_controller.rb +// namespace under flat top-level paths (the /everything/... segment is the +// Rails controller namespace, not part of the URL). Documented by BC3 #11627 in +// doc/api/sections/everything.md. This is the flat family: six recency-ordered, +// Link-paginated roots plus two unpaginated oldest-first overdue lists. The +// bucket-grouped todo/card filter sub-routes are a separate family. Never model +// the bare /todos.json or /cards.json roots (HTML shells) or the internal +// //recent.json feeds. + +/// Get every message across all accessible projects, newest-first (paginated). +/// Each item embeds its `bucket` for project context. +@readonly +@basecampRetry(maxAttempts: 3, baseDelayMs: 1000, backoff: "exponential", retryOn: [429, 503]) +@basecampPagination(style: "link", totalCountHeader: "X-Total-Count", maxPageSize: 50) +@http(method: "GET", uri: "/{accountId}/messages.json") +operation GetEverythingMessages { + input: GetEverythingMessagesInput + output: GetEverythingMessagesOutput + errors: [UnauthorizedError, ForbiddenError, RateLimitError, InternalServerError] +} + +structure GetEverythingMessagesInput { + @required + @httpLabel + accountId: AccountId + + /// Page number for paginating through results. Defaults to 1. + @httpQuery("page") + page: Integer +} + +structure GetEverythingMessagesOutput { + recordings: RecordingList +} + +/// Get every comment across all accessible projects, newest-first (paginated). +/// Each item embeds its `bucket`. +@readonly +@basecampRetry(maxAttempts: 3, baseDelayMs: 1000, backoff: "exponential", retryOn: [429, 503]) +@basecampPagination(style: "link", totalCountHeader: "X-Total-Count", maxPageSize: 50) +@http(method: "GET", uri: "/{accountId}/comments.json") +operation GetEverythingComments { + input: GetEverythingCommentsInput + output: GetEverythingCommentsOutput + errors: [UnauthorizedError, ForbiddenError, RateLimitError, InternalServerError] +} + +structure GetEverythingCommentsInput { + @required + @httpLabel + accountId: AccountId + + /// Page number for paginating through results. Defaults to 1. + @httpQuery("page") + page: Integer +} + +structure GetEverythingCommentsOutput { + recordings: RecordingList +} + +/// Get every automatic check-in answer across all accessible projects, +/// newest-first (paginated). Each item embeds its `bucket`. +@readonly +@basecampRetry(maxAttempts: 3, baseDelayMs: 1000, backoff: "exponential", retryOn: [429, 503]) +@basecampPagination(style: "link", totalCountHeader: "X-Total-Count", maxPageSize: 50) +@http(method: "GET", uri: "/{accountId}/checkins.json") +operation GetEverythingCheckins { + input: GetEverythingCheckinsInput + output: GetEverythingCheckinsOutput + errors: [UnauthorizedError, ForbiddenError, RateLimitError, InternalServerError] +} + +structure GetEverythingCheckinsInput { + @required + @httpLabel + accountId: AccountId + + /// Page number for paginating through results. Defaults to 1. + @httpQuery("page") + page: Integer +} + +structure GetEverythingCheckinsOutput { + recordings: RecordingList +} + +/// Get every inbox forward across all accessible projects, newest-first +/// (paginated). Each item embeds its `bucket`. +@readonly +@basecampRetry(maxAttempts: 3, baseDelayMs: 1000, backoff: "exponential", retryOn: [429, 503]) +@basecampPagination(style: "link", totalCountHeader: "X-Total-Count", maxPageSize: 50) +@http(method: "GET", uri: "/{accountId}/forwards.json") +operation GetEverythingForwards { + input: GetEverythingForwardsInput + output: GetEverythingForwardsOutput + errors: [UnauthorizedError, ForbiddenError, RateLimitError, InternalServerError] +} + +structure GetEverythingForwardsInput { + @required + @httpLabel + accountId: AccountId + + /// Page number for paginating through results. Defaults to 1. + @httpQuery("page") + page: Integer +} + +structure GetEverythingForwardsOutput { + recordings: RecordingList +} + +/// Get every boost across all accessible projects, newest-first (paginated). +/// Each boost carries its `booster` and the `recording` it boosts. +@readonly +@basecampRetry(maxAttempts: 3, baseDelayMs: 1000, backoff: "exponential", retryOn: [429, 503]) +@basecampPagination(style: "link", totalCountHeader: "X-Total-Count", maxPageSize: 50) +@http(method: "GET", uri: "/{accountId}/boosts.json") +operation GetEverythingBoosts { + input: GetEverythingBoostsInput + output: GetEverythingBoostsOutput + errors: [UnauthorizedError, ForbiddenError, RateLimitError, InternalServerError] +} + +structure GetEverythingBoostsInput { + @required + @httpLabel + accountId: AccountId + + /// Page number for paginating through results. Defaults to 1. + @httpQuery("page") + page: Integer +} + +structure GetEverythingBoostsOutput { + boosts: BoostList +} + +/// Get every file recording across all accessible projects, newest-first +/// (paginated). Heterogeneous: uploads and Basecamp documents carry their +/// standard recording shapes, while rich-text attachments are wrapped in a +/// recording envelope plus an `attachable_sgid` and blob metadata. Modeled as +/// an optional-field superset (EverythingFile) so one element type decodes any +/// variant. +@readonly +@basecampRetry(maxAttempts: 3, baseDelayMs: 1000, backoff: "exponential", retryOn: [429, 503]) +@basecampPagination(style: "link", totalCountHeader: "X-Total-Count", maxPageSize: 50) +@http(method: "GET", uri: "/{accountId}/files.json") +operation GetEverythingFiles { + input: GetEverythingFilesInput + output: GetEverythingFilesOutput + errors: [UnauthorizedError, ForbiddenError, RateLimitError, InternalServerError] +} + +structure GetEverythingFilesInput { + @required + @httpLabel + accountId: AccountId + + /// Filter by file kind: all (default), images, pdfs, documents, or videos. + @httpQuery("kind") + kind: String + + /// Restrict to files created by the given people (repeatable). + @httpQuery("people_ids[]") + people_ids: PersonIdList + + /// Page number for paginating through results. Defaults to 1. + @httpQuery("page") + page: Integer +} + +structure GetEverythingFilesOutput { + files: EverythingFileList +} + +/// Get every overdue to-do across all accessible projects, oldest-due-date-first. +/// A complete, unpaginated array; each item embeds its `bucket`. +@readonly +@basecampRetry(maxAttempts: 3, baseDelayMs: 1000, backoff: "exponential", retryOn: [429, 503]) +@http(method: "GET", uri: "/{accountId}/todos/overdue.json") +operation GetEverythingOverdueTodos { + input: GetEverythingOverdueTodosInput + output: GetEverythingOverdueTodosOutput + errors: [UnauthorizedError, ForbiddenError, InternalServerError] +} + +structure GetEverythingOverdueTodosInput { + @required + @httpLabel + accountId: AccountId +} + +structure GetEverythingOverdueTodosOutput { + todos: TodoItems +} + +/// Get every overdue card across all accessible projects, oldest-due-date-first. +/// A complete, unpaginated array; each item embeds its `bucket`. +@readonly +@basecampRetry(maxAttempts: 3, baseDelayMs: 1000, backoff: "exponential", retryOn: [429, 503]) +@http(method: "GET", uri: "/{accountId}/cards/overdue.json") +operation GetEverythingOverdueCards { + input: GetEverythingOverdueCardsInput + output: GetEverythingOverdueCardsOutput + errors: [UnauthorizedError, ForbiddenError, InternalServerError] +} + +structure GetEverythingOverdueCardsInput { + @required + @httpLabel + accountId: AccountId +} + +structure GetEverythingOverdueCardsOutput { + cards: CardList +} + +// ===== Everything File Shapes ===== + +list EverythingFileList { + member: EverythingFile +} + +/// A single item in the /files.json feed. An optional-field superset over three +/// wire variants — a full Upload recording, a Basecamp Document recording, and a +/// rich-text attachment wrapped in a recording envelope (distinguished by +/// `attachable_sgid` and blob metadata). Every field is optional; a given +/// instance populates only the fields of the variant it represents. Unknown +/// fields are ignored by every SDK decoder, so the superset need not enumerate +/// every field of the Upload/Document recordings. +structure EverythingFile { + /// Recording (Upload/Document) or attachment id. + id: Long + status: String + visible_to_clients: Boolean + created_at: ISO8601Timestamp + updated_at: ISO8601Timestamp + title: String + inherits_status: Boolean + /// "Upload", "Document", or "Attachment". + type: String + url: String + app_url: String + bookmark_url: String + subscription_url: String + comments_count: Integer + comments_url: String + boosts_count: Integer + boosts_url: String + position: Integer + parent: RecordingParent + bucket: RecordingBucket + creator: Person + + /// Present on the rich-text attachment variant: signed global id of the + /// attachment (uploads/documents omit it). + attachable_sgid: String + + // ----- blob/file metadata (uploads and attachments) ----- + content_type: String + byte_size: Long + filename: String + @basecampAuthRoutableUrl + download_url: String + app_download_url: String + /// Pixel width; null for non-image blobs and may be float-spelled (1024.0). + width: Integer + /// Pixel height; null for non-image blobs and may be float-spelled (1024.0). + height: Integer + + /// Rich-text description (upload/document variants). + description: String + description_attachments: RichTextAttachmentList +} + // ===== My Assignment Shapes ===== list MyAssignmentList { diff --git a/spec/overlays/tags.smithy b/spec/overlays/tags.smithy index 958079c46..b969c5ec1 100644 --- a/spec/overlays/tags.smithy +++ b/spec/overlays/tags.smithy @@ -219,6 +219,16 @@ apply GetMyAssignments @tags(["MyAssignments"]) apply GetMyCompletedAssignments @tags(["MyAssignments"]) apply GetMyDueAssignments @tags(["MyAssignments"]) +// Everything Aggregates (flat family) +apply GetEverythingMessages @tags(["Everything"]) +apply GetEverythingComments @tags(["Everything"]) +apply GetEverythingCheckins @tags(["Everything"]) +apply GetEverythingForwards @tags(["Everything"]) +apply GetEverythingBoosts @tags(["Everything"]) +apply GetEverythingFiles @tags(["Everything"]) +apply GetEverythingOverdueTodos @tags(["Everything"]) +apply GetEverythingOverdueCards @tags(["Everything"]) + // My Notifications apply GetMyNotifications @tags(["MyNotifications"]) apply GetBubbleUps @tags(["MyNotifications"]) diff --git a/swift/Sources/Basecamp/Generated/AccountClient+Services.swift b/swift/Sources/Basecamp/Generated/AccountClient+Services.swift index c3b5b72d8..6dc4f4ecd 100644 --- a/swift/Sources/Basecamp/Generated/AccountClient+Services.swift +++ b/swift/Sources/Basecamp/Generated/AccountClient+Services.swift @@ -19,6 +19,7 @@ extension AccountClient { public var comments: CommentsService { service("comments") { CommentsService(accountClient: self) } } public var documents: DocumentsService { service("documents") { DocumentsService(accountClient: self) } } public var events: EventsService { service("events") { EventsService(accountClient: self) } } + public var everything: EverythingService { service("everything") { EverythingService(accountClient: self) } } public var forwards: ForwardsService { service("forwards") { ForwardsService(accountClient: self) } } public var gauges: GaugesService { service("gauges") { GaugesService(accountClient: self) } } public var hillCharts: HillChartsService { service("hillCharts") { HillChartsService(accountClient: self) } } diff --git a/swift/Sources/Basecamp/Generated/Metadata.swift b/swift/Sources/Basecamp/Generated/Metadata.swift index 82a49ffa5..783d9bcb8 100644 --- a/swift/Sources/Basecamp/Generated/Metadata.swift +++ b/swift/Sources/Basecamp/Generated/Metadata.swift @@ -70,6 +70,14 @@ enum Metadata { "GetClientReply": RetryConfig(maxAttempts: 3, baseDelayMs: 1000, backoff: .exponential, retryOn: [429, 503]), "GetComment": RetryConfig(maxAttempts: 3, baseDelayMs: 1000, backoff: .exponential, retryOn: [429, 503]), "GetDocument": RetryConfig(maxAttempts: 3, baseDelayMs: 1000, backoff: .exponential, retryOn: [429, 503]), + "GetEverythingBoosts": RetryConfig(maxAttempts: 3, baseDelayMs: 1000, backoff: .exponential, retryOn: [429, 503]), + "GetEverythingCheckins": RetryConfig(maxAttempts: 3, baseDelayMs: 1000, backoff: .exponential, retryOn: [429, 503]), + "GetEverythingComments": RetryConfig(maxAttempts: 3, baseDelayMs: 1000, backoff: .exponential, retryOn: [429, 503]), + "GetEverythingFiles": RetryConfig(maxAttempts: 3, baseDelayMs: 1000, backoff: .exponential, retryOn: [429, 503]), + "GetEverythingForwards": RetryConfig(maxAttempts: 3, baseDelayMs: 1000, backoff: .exponential, retryOn: [429, 503]), + "GetEverythingMessages": RetryConfig(maxAttempts: 3, baseDelayMs: 1000, backoff: .exponential, retryOn: [429, 503]), + "GetEverythingOverdueCards": RetryConfig(maxAttempts: 3, baseDelayMs: 1000, backoff: .exponential, retryOn: [429, 503]), + "GetEverythingOverdueTodos": RetryConfig(maxAttempts: 3, baseDelayMs: 1000, backoff: .exponential, retryOn: [429, 503]), "GetForward": RetryConfig(maxAttempts: 3, baseDelayMs: 1000, backoff: .exponential, retryOn: [429, 503]), "GetForwardReply": RetryConfig(maxAttempts: 3, baseDelayMs: 1000, backoff: .exponential, retryOn: [429, 503]), "GetGaugeNeedle": RetryConfig(maxAttempts: 3, baseDelayMs: 1000, backoff: .exponential, retryOn: [429, 503]), diff --git a/swift/Sources/Basecamp/Generated/Models/DoorService.swift b/swift/Sources/Basecamp/Generated/Models/DoorService.swift new file mode 100644 index 000000000..39379de58 --- /dev/null +++ b/swift/Sources/Basecamp/Generated/Models/DoorService.swift @@ -0,0 +1,10 @@ +// @generated from OpenAPI spec — do not edit directly +import Foundation + +public struct DoorService: Codable, Sendable { + public var code: String? + public var exampleUrl: String? + public var name: String? + public var supportingText: String? + public var validPatterns: [String]? +} diff --git a/swift/Sources/Basecamp/Generated/Models/EverythingFile.swift b/swift/Sources/Basecamp/Generated/Models/EverythingFile.swift new file mode 100644 index 000000000..aad2f9025 --- /dev/null +++ b/swift/Sources/Basecamp/Generated/Models/EverythingFile.swift @@ -0,0 +1,35 @@ +// @generated from OpenAPI spec — do not edit directly +import Foundation + +public struct EverythingFile: Codable, Sendable { + public var appDownloadUrl: String? + public var appUrl: String? + public var attachableSgid: String? + public var bookmarkUrl: String? + public var boostsCount: Int32? + public var boostsUrl: String? + public var bucket: RecordingBucket? + public var byteSize: Int? + public var commentsCount: Int32? + public var commentsUrl: String? + public var contentType: String? + public var createdAt: String? + public var creator: Person? + public var description: String? + public var descriptionAttachments: [RichTextAttachment]? + public var downloadUrl: String? + public var filename: String? + public var height: Int32? + public var id: Int? + public var inheritsStatus: Bool? + public var parent: RecordingParent? + public var position: Int32? + public var status: String? + public var subscriptionUrl: String? + public var title: String? + public var type: String? + public var updatedAt: String? + public var url: String? + public var visibleToClients: Bool? + public var width: Int32? +} diff --git a/swift/Sources/Basecamp/Generated/Models/Recording.swift b/swift/Sources/Basecamp/Generated/Models/Recording.swift index 3be14f030..cc3a33884 100644 --- a/swift/Sources/Basecamp/Generated/Models/Recording.swift +++ b/swift/Sources/Basecamp/Generated/Models/Recording.swift @@ -8,7 +8,6 @@ public struct Recording: Codable, Sendable { public let creator: Person public let id: Int public let inheritsStatus: Bool - public let parent: RecordingParent public let status: String public let title: String public let type: String @@ -20,7 +19,11 @@ public struct Recording: Codable, Sendable { public var commentsUrl: String? public var content: String? public var contentAttachments: [RichTextAttachment]? + public var description: String? public var descriptionAttachments: [RichTextAttachment]? + public var parent: RecordingParent? + public var position: Int32? + public var service: DoorService? public var subscriptionUrl: String? public init( @@ -30,7 +33,6 @@ public struct Recording: Codable, Sendable { creator: Person, id: Int, inheritsStatus: Bool, - parent: RecordingParent, status: String, title: String, type: String, @@ -42,7 +44,11 @@ public struct Recording: Codable, Sendable { commentsUrl: String? = nil, content: String? = nil, contentAttachments: [RichTextAttachment]? = nil, + description: String? = nil, descriptionAttachments: [RichTextAttachment]? = nil, + parent: RecordingParent? = nil, + position: Int32? = nil, + service: DoorService? = nil, subscriptionUrl: String? = nil ) { self.appUrl = appUrl @@ -51,7 +57,6 @@ public struct Recording: Codable, Sendable { self.creator = creator self.id = id self.inheritsStatus = inheritsStatus - self.parent = parent self.status = status self.title = title self.type = type @@ -63,7 +68,11 @@ public struct Recording: Codable, Sendable { self.commentsUrl = commentsUrl self.content = content self.contentAttachments = contentAttachments + self.description = description self.descriptionAttachments = descriptionAttachments + self.parent = parent + self.position = position + self.service = service self.subscriptionUrl = subscriptionUrl } } diff --git a/swift/Sources/Basecamp/Generated/Services/EverythingService.swift b/swift/Sources/Basecamp/Generated/Services/EverythingService.swift new file mode 100644 index 000000000..08a538ba1 --- /dev/null +++ b/swift/Sources/Basecamp/Generated/Services/EverythingService.swift @@ -0,0 +1,184 @@ +// @generated from OpenAPI spec — do not edit directly +import Foundation + +public struct EverythingBoostsEverythingOptions: Sendable { + public var page: Int? + public var maxItems: Int? + + public init(page: Int? = nil, maxItems: Int? = nil) { + self.page = page + self.maxItems = maxItems + } +} + +public struct EverythingCheckinsEverythingOptions: Sendable { + public var page: Int? + public var maxItems: Int? + + public init(page: Int? = nil, maxItems: Int? = nil) { + self.page = page + self.maxItems = maxItems + } +} + +public struct EverythingCommentsEverythingOptions: Sendable { + public var page: Int? + public var maxItems: Int? + + public init(page: Int? = nil, maxItems: Int? = nil) { + self.page = page + self.maxItems = maxItems + } +} + +public struct EverythingFilesEverythingOptions: Sendable { + public var kind: String? + public var peopleIds: [Int]? + public var page: Int? + public var maxItems: Int? + + public init( + kind: String? = nil, + peopleIds: [Int]? = nil, + page: Int? = nil, + maxItems: Int? = nil + ) { + self.kind = kind + self.peopleIds = peopleIds + self.page = page + self.maxItems = maxItems + } +} + +public struct EverythingForwardsEverythingOptions: Sendable { + public var page: Int? + public var maxItems: Int? + + public init(page: Int? = nil, maxItems: Int? = nil) { + self.page = page + self.maxItems = maxItems + } +} + +public struct EverythingMessagesEverythingOptions: Sendable { + public var page: Int? + public var maxItems: Int? + + public init(page: Int? = nil, maxItems: Int? = nil) { + self.page = page + self.maxItems = maxItems + } +} + + +public final class EverythingService: BaseService, @unchecked Sendable { + public func everythingBoosts(options: EverythingBoostsEverythingOptions? = nil) async throws -> ListResult { + var queryItems: [URLQueryItem] = [] + if let page = options?.page { + queryItems.append(URLQueryItem(name: "page", value: String(page))) + } + return try await requestPaginated( + OperationInfo(service: "Everything", operation: "GetEverythingBoosts", resourceType: "everything_boost", isMutation: false), + path: "/boosts.json", + queryItems: queryItems.isEmpty ? nil : queryItems, + paginationOpts: options.flatMap { PaginationOptions(maxItems: $0.maxItems) }, + retryConfig: Metadata.retryConfig(for: "GetEverythingBoosts") + ) + } + + public func everythingCheckins(options: EverythingCheckinsEverythingOptions? = nil) async throws -> ListResult { + var queryItems: [URLQueryItem] = [] + if let page = options?.page { + queryItems.append(URLQueryItem(name: "page", value: String(page))) + } + return try await requestPaginated( + OperationInfo(service: "Everything", operation: "GetEverythingCheckins", resourceType: "everything_checkin", isMutation: false), + path: "/checkins.json", + queryItems: queryItems.isEmpty ? nil : queryItems, + paginationOpts: options.flatMap { PaginationOptions(maxItems: $0.maxItems) }, + retryConfig: Metadata.retryConfig(for: "GetEverythingCheckins") + ) + } + + public func everythingComments(options: EverythingCommentsEverythingOptions? = nil) async throws -> ListResult { + var queryItems: [URLQueryItem] = [] + if let page = options?.page { + queryItems.append(URLQueryItem(name: "page", value: String(page))) + } + return try await requestPaginated( + OperationInfo(service: "Everything", operation: "GetEverythingComments", resourceType: "everything_comment", isMutation: false), + path: "/comments.json", + queryItems: queryItems.isEmpty ? nil : queryItems, + paginationOpts: options.flatMap { PaginationOptions(maxItems: $0.maxItems) }, + retryConfig: Metadata.retryConfig(for: "GetEverythingComments") + ) + } + + public func everythingFiles(options: EverythingFilesEverythingOptions? = nil) async throws -> ListResult { + var queryItems: [URLQueryItem] = [] + if let kind = options?.kind { + queryItems.append(URLQueryItem(name: "kind", value: kind)) + } + if let peopleIds = options?.peopleIds { + for item in peopleIds { + queryItems.append(URLQueryItem(name: "people_ids[]", value: String(item))) + } + } + if let page = options?.page { + queryItems.append(URLQueryItem(name: "page", value: String(page))) + } + return try await requestPaginated( + OperationInfo(service: "Everything", operation: "GetEverythingFiles", resourceType: "everything_file", isMutation: false), + path: "/files.json", + queryItems: queryItems.isEmpty ? nil : queryItems, + paginationOpts: options.flatMap { PaginationOptions(maxItems: $0.maxItems) }, + retryConfig: Metadata.retryConfig(for: "GetEverythingFiles") + ) + } + + public func everythingForwards(options: EverythingForwardsEverythingOptions? = nil) async throws -> ListResult { + var queryItems: [URLQueryItem] = [] + if let page = options?.page { + queryItems.append(URLQueryItem(name: "page", value: String(page))) + } + return try await requestPaginated( + OperationInfo(service: "Everything", operation: "GetEverythingForwards", resourceType: "everything_forward", isMutation: false), + path: "/forwards.json", + queryItems: queryItems.isEmpty ? nil : queryItems, + paginationOpts: options.flatMap { PaginationOptions(maxItems: $0.maxItems) }, + retryConfig: Metadata.retryConfig(for: "GetEverythingForwards") + ) + } + + public func everythingMessages(options: EverythingMessagesEverythingOptions? = nil) async throws -> ListResult { + var queryItems: [URLQueryItem] = [] + if let page = options?.page { + queryItems.append(URLQueryItem(name: "page", value: String(page))) + } + return try await requestPaginated( + OperationInfo(service: "Everything", operation: "GetEverythingMessages", resourceType: "everything_message", isMutation: false), + path: "/messages.json", + queryItems: queryItems.isEmpty ? nil : queryItems, + paginationOpts: options.flatMap { PaginationOptions(maxItems: $0.maxItems) }, + retryConfig: Metadata.retryConfig(for: "GetEverythingMessages") + ) + } + + public func everythingOverdueCards() async throws -> [Card] { + return try await request( + OperationInfo(service: "Everything", operation: "GetEverythingOverdueCards", resourceType: "everything_overdue_card", isMutation: false), + method: "GET", + path: "/cards/overdue.json", + retryConfig: Metadata.retryConfig(for: "GetEverythingOverdueCards") + ) + } + + public func everythingOverdueTodos() async throws -> [Todo] { + return try await request( + OperationInfo(service: "Everything", operation: "GetEverythingOverdueTodos", resourceType: "everything_overdue_todo", isMutation: false), + method: "GET", + path: "/todos/overdue.json", + retryConfig: Metadata.retryConfig(for: "GetEverythingOverdueTodos") + ) + } +} diff --git a/swift/Tests/BasecampTests/GeneratedServiceTests.swift b/swift/Tests/BasecampTests/GeneratedServiceTests.swift index 205fb94d5..a758fe348 100644 --- a/swift/Tests/BasecampTests/GeneratedServiceTests.swift +++ b/swift/Tests/BasecampTests/GeneratedServiceTests.swift @@ -935,4 +935,94 @@ final class GeneratedServiceTests: XCTestCase { let sentURL = transport.lastRequest!.request.url!.absoluteString XCTAssertTrue(sentURL.hasSuffix("/my/readings/bubble_ups.json"), "Got \(sentURL)") } + + // MARK: - Everything /files.json heterogeneous feed + + // Proves runtime decode of the /files.json "everything" feed through the full + // service lifecycle (.convertFromSnakeCase): a single non-empty array carrying + // three distinct variants — a full Upload recording, a Basecamp Document + // recording, and a rich-text Attachment envelope — each asserted on real + // per-variant fields. The Upload width is float-spelled (1024.0) on the wire; + // Foundation's JSONDecoder folds that into the model's Int32 field. + func testEverythingFilesDecodesHeterogeneousFeed() async throws { + let fixture = """ + [ + { + "id": 900, + "type": "Upload", + "status": "active", + "visible_to_clients": false, + "title": "logo.png", + "inherits_status": true, + "filename": "logo.png", + "content_type": "image/png", + "byte_size": 1281, + "width": 1024.0, + "height": 768.0, + "url": "https://3.basecampapi.com/1/buckets/2/uploads/900.json", + "app_url": "https://3.basecamp.com/1/buckets/2/uploads/900", + "download_url": "https://3.basecampapi.com/1/buckets/2/uploads/900/download/logo.png", + "app_download_url": "https://storage.3.basecamp.com/1/buckets/2/uploads/900/download/logo.png", + "bucket": { "id": 2, "name": "The Leto Laptop", "type": "Project" }, + "creator": { "id": 1, "name": "Victor Cooper" } + }, + { + "id": 901, + "type": "Document", + "status": "active", + "visible_to_clients": false, + "title": "Spec", + "inherits_status": true, + "content_type": "text/html", + "url": "https://3.basecampapi.com/1/buckets/2/documents/901.json", + "app_url": "https://3.basecamp.com/1/buckets/2/documents/901", + "bucket": { "id": 2, "name": "The Leto Laptop", "type": "Project" }, + "creator": { "id": 1, "name": "Victor Cooper" } + }, + { + "id": 902, + "type": "Attachment", + "attachable_sgid": "sgid-902", + "filename": "chart.avif", + "content_type": "image/avif", + "byte_size": 4096, + "width": null, + "height": null, + "download_url": "https://storage.3.basecamp.com/1/blobs/902/download/chart.avif", + "parent": { + "id": 800, + "title": "A message", + "type": "Message", + "app_url": "https://3.basecamp.com/1/buckets/2/messages/800", + "url": "https://3.basecampapi.com/1/buckets/2/messages/800.json" + } + } + ] + """ + let transport = MockTransport(statusCode: 200, data: Data(fixture.utf8), + headers: ["X-Total-Count": "3"]) + let account = makeTestAccountClient(transport: transport) + + let files = try await account.everything.everythingFiles() + XCTAssertEqual(files.count, 3) + + // [0]: full Upload recording (float-spelled width folds into Int32) + XCTAssertEqual(files[0].type, "Upload") + XCTAssertEqual(files[0].filename, "logo.png") + XCTAssertNotNil(files[0].appDownloadUrl) + XCTAssertEqual(files[0].width, 1024) + + // [1]: Basecamp Document recording + XCTAssertEqual(files[1].type, "Document") + XCTAssertEqual(files[1].title, "Spec") + + // [2]: rich-text Attachment envelope + XCTAssertEqual(files[2].type, "Attachment") + XCTAssertEqual(files[2].attachableSgid, "sgid-902") + XCTAssertNotNil(files[2].parent) + XCTAssertNil(files[2].width) + + let sentURL = transport.lastRequest!.request.url!.absoluteString + XCTAssertTrue(sentURL.hasSuffix("/files.json"), "Got \(sentURL)") + } } diff --git a/typescript/scripts/generate-services.ts b/typescript/scripts/generate-services.ts index 559eb266d..0640f470b 100644 --- a/typescript/scripts/generate-services.ts +++ b/typescript/scripts/generate-services.ts @@ -459,6 +459,8 @@ const TYPE_ALIASES: Record ClientReply: ["ClientReply", "entity"], TimelineEvent: ["TimelineEvent", "entity"], Notification: ["Notification", "entity"], + Boost: ["Boost", "entity"], + EverythingFile: ["EverythingFile", "entity"], TimesheetEntry: ["TimesheetEntry", "entity"], }; diff --git a/typescript/src/client.ts b/typescript/src/client.ts index 36190a3c0..3ea57321a 100644 --- a/typescript/src/client.ts +++ b/typescript/src/client.ts @@ -59,6 +59,7 @@ import { TodolistGroupsService } from "./generated/services/todolist-groups.js"; import { ToolsService } from "./generated/services/tools.js"; import { TimesheetsService } from "./generated/services/timesheets.js"; import { TimelineService } from "./generated/services/timeline.js"; +import { EverythingService } from "./generated/services/everything.js"; import { ClientVisibilityService } from "./generated/services/client-visibility.js"; import { BoostsService } from "./generated/services/boosts.js"; import { AccountService } from "./generated/services/account.js"; @@ -174,6 +175,8 @@ export interface BasecampClient extends RawClient { readonly timesheets: TimesheetsService; /** Timeline service - get project timeline */ readonly timeline: TimelineService; + /** Everything service - account-wide aggregate listings */ + readonly everything: EverythingService; /** Client visibility service - manage client visibility */ readonly clientVisibility: ClientVisibilityService; /** Boosts service - manage recording boosts */ @@ -409,6 +412,7 @@ export function createBasecampClient(options: BasecampClientOptions): BasecampCl defineService("tools", () => new ToolsService(client, hooks, fetchPage, maxPages)); defineService("timesheets", () => new TimesheetsService(client, hooks, fetchPage, maxPages)); defineService("timeline", () => new TimelineService(client, hooks, fetchPage, maxPages)); + defineService("everything", () => new EverythingService(client, hooks, fetchPage, maxPages)); defineService("clientVisibility", () => new ClientVisibilityService(client, hooks, fetchPage, maxPages)); defineService("boosts", () => new BoostsService(client, hooks, fetchPage, maxPages)); defineService("account", () => new AccountService(client, hooks, fetchPage, maxPages, authenticatedFetch, baseUrl)); diff --git a/typescript/src/generated/metadata.ts b/typescript/src/generated/metadata.ts index 3f0637e55..302d49371 100644 --- a/typescript/src/generated/metadata.ts +++ b/typescript/src/generated/metadata.ts @@ -37,7 +37,7 @@ export interface MetadataOutput { const metadata: MetadataOutput = { "$schema": "https://basecamp.com/schemas/sdk-metadata.json", "version": "1.0.0", - "generated": "2026-07-25T07:14:09.617Z", + "generated": "2026-07-25T17:48:05.683Z", "operations": { "GetAccount": { "retry": { @@ -103,6 +103,22 @@ const metadata: MetadataOutput = { ] } }, + "GetEverythingBoosts": { + "retry": { + "maxAttempts": 3, + "baseDelayMs": 1000, + "backoff": "exponential", + "retryOn": [ + 429, + 503 + ] + }, + "pagination": { + "style": "link", + "totalCountHeader": "X-Total-Count", + "maxPageSize": 50 + } + }, "GetBoost": { "retry": { "maxAttempts": 3, @@ -454,6 +470,17 @@ const metadata: MetadataOutput = { ] } }, + "GetEverythingOverdueCards": { + "retry": { + "maxAttempts": 3, + "baseDelayMs": 1000, + "backoff": "exponential", + "retryOn": [ + 429, + 503 + ] + } + }, "ListMessageTypes": { "retry": { "maxAttempts": 3, @@ -706,6 +733,22 @@ const metadata: MetadataOutput = { ] } }, + "GetEverythingCheckins": { + "retry": { + "maxAttempts": 3, + "baseDelayMs": 1000, + "backoff": "exponential", + "retryOn": [ + 429, + 503 + ] + }, + "pagination": { + "style": "link", + "totalCountHeader": "X-Total-Count", + "maxPageSize": 50 + } + }, "ListPingablePeople": { "retry": { "maxAttempts": 3, @@ -803,6 +846,22 @@ const metadata: MetadataOutput = { ] } }, + "GetEverythingComments": { + "retry": { + "maxAttempts": 3, + "baseDelayMs": 1000, + "backoff": "exponential", + "retryOn": [ + 429, + 503 + ] + }, + "pagination": { + "style": "link", + "totalCountHeader": "X-Total-Count", + "maxPageSize": 50 + } + }, "GetComment": { "retry": { "maxAttempts": 3, @@ -892,6 +951,38 @@ const metadata: MetadataOutput = { "natural": true } }, + "GetEverythingFiles": { + "retry": { + "maxAttempts": 3, + "baseDelayMs": 1000, + "backoff": "exponential", + "retryOn": [ + 429, + 503 + ] + }, + "pagination": { + "style": "link", + "totalCountHeader": "X-Total-Count", + "maxPageSize": 50 + } + }, + "GetEverythingForwards": { + "retry": { + "maxAttempts": 3, + "baseDelayMs": 1000, + "backoff": "exponential", + "retryOn": [ + 429, + 503 + ] + }, + "pagination": { + "style": "link", + "totalCountHeader": "X-Total-Count", + "maxPageSize": 50 + } + }, "GetGaugeNeedle": { "retry": { "maxAttempts": 3, @@ -1095,6 +1186,22 @@ const metadata: MetadataOutput = { ] } }, + "GetEverythingMessages": { + "retry": { + "maxAttempts": 3, + "baseDelayMs": 1000, + "backoff": "exponential", + "retryOn": [ + 429, + 503 + ] + }, + "pagination": { + "style": "link", + "totalCountHeader": "X-Total-Count", + "maxPageSize": 50 + } + }, "GetMessage": { "retry": { "maxAttempts": 3, @@ -2424,6 +2531,17 @@ const metadata: MetadataOutput = { ] } }, + "GetEverythingOverdueTodos": { + "retry": { + "maxAttempts": 3, + "baseDelayMs": 1000, + "backoff": "exponential", + "retryOn": [ + 429, + 503 + ] + } + }, "GetTodo": { "retry": { "maxAttempts": 3, diff --git a/typescript/src/generated/openapi-stripped.json b/typescript/src/generated/openapi-stripped.json index 16459ccb7..27886656e 100644 --- a/typescript/src/generated/openapi-stripped.json +++ b/typescript/src/generated/openapi-stripped.json @@ -433,6 +433,93 @@ } } }, + "/boosts.json": { + "get": { + "description": "Get every boost across all accessible projects, newest-first (paginated).\nEach boost carries its `booster` and the `recording` it boosts.", + "operationId": "GetEverythingBoosts", + "parameters": [ + { + "name": "page", + "in": "query", + "description": "Page number for paginating through results. Defaults to 1.", + "schema": { + "type": "integer", + "description": "Page number for paginating through results. Defaults to 1.", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "GetEverythingBoosts 200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetEverythingBoostsResponseContent" + } + } + } + }, + "401": { + "description": "UnauthorizedError 401 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedErrorResponseContent" + } + } + } + }, + "403": { + "description": "ForbiddenError 403 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForbiddenErrorResponseContent" + } + } + } + }, + "429": { + "description": "RateLimitError 429 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RateLimitErrorResponseContent" + } + } + } + }, + "500": { + "description": "InternalServerError 500 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InternalServerErrorResponseContent" + } + } + } + } + }, + "tags": [ + "Everything" + ], + "x-basecamp-pagination": { + "style": "link", + "totalCountHeader": "X-Total-Count", + "maxPageSize": 50 + }, + "x-basecamp-retry": { + "maxAttempts": 3, + "baseDelayMs": 1000, + "backoff": "exponential", + "retryOn": [ + 429, + 503 + ] + } + } + }, "/boosts/{boostId}": { "delete": { "description": "Delete a boost", @@ -3082,6 +3169,67 @@ } } }, + "/cards/overdue.json": { + "get": { + "description": "Get every overdue card across all accessible projects, oldest-due-date-first.\nA complete, unpaginated array; each item embeds its `bucket`.", + "operationId": "GetEverythingOverdueCards", + "parameters": [], + "responses": { + "200": { + "description": "GetEverythingOverdueCards 200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetEverythingOverdueCardsResponseContent" + } + } + } + }, + "401": { + "description": "UnauthorizedError 401 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedErrorResponseContent" + } + } + } + }, + "403": { + "description": "ForbiddenError 403 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForbiddenErrorResponseContent" + } + } + } + }, + "500": { + "description": "InternalServerError 500 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InternalServerErrorResponseContent" + } + } + } + } + }, + "tags": [ + "Everything" + ], + "x-basecamp-retry": { + "maxAttempts": 3, + "baseDelayMs": 1000, + "backoff": "exponential", + "retryOn": [ + 429, + 503 + ] + } + } + }, "/categories.json": { "get": { "description": "List message types in a project\n\n**Pagination**: Uses Link header (RFC5988). Follow the `next` rel URL\nto fetch additional pages. X-Total-Count header provides total count.", @@ -4835,6 +4983,93 @@ } } }, + "/checkins.json": { + "get": { + "description": "Get every automatic check-in answer across all accessible projects,\nnewest-first (paginated). Each item embeds its `bucket`.", + "operationId": "GetEverythingCheckins", + "parameters": [ + { + "name": "page", + "in": "query", + "description": "Page number for paginating through results. Defaults to 1.", + "schema": { + "type": "integer", + "description": "Page number for paginating through results. Defaults to 1.", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "GetEverythingCheckins 200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetEverythingCheckinsResponseContent" + } + } + } + }, + "401": { + "description": "UnauthorizedError 401 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedErrorResponseContent" + } + } + } + }, + "403": { + "description": "ForbiddenError 403 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForbiddenErrorResponseContent" + } + } + } + }, + "429": { + "description": "RateLimitError 429 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RateLimitErrorResponseContent" + } + } + } + }, + "500": { + "description": "InternalServerError 500 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InternalServerErrorResponseContent" + } + } + } + } + }, + "tags": [ + "Everything" + ], + "x-basecamp-pagination": { + "style": "link", + "totalCountHeader": "X-Total-Count", + "maxPageSize": 50 + }, + "x-basecamp-retry": { + "maxAttempts": 3, + "baseDelayMs": 1000, + "backoff": "exponential", + "retryOn": [ + 429, + 503 + ] + } + } + }, "/circles/people.json": { "get": { "description": "List all account users who can be pinged\n\n**Pagination**: Uses Link header (RFC5988). Follow the `next` rel URL\nto fetch additional pages. X-Total-Count header provides total count.", @@ -5439,28 +5674,29 @@ } } }, - "/comments/{commentId}": { + "/comments.json": { "get": { - "description": "Get a single comment by id", - "operationId": "GetComment", + "description": "Get every comment across all accessible projects, newest-first (paginated).\nEach item embeds its `bucket`.", + "operationId": "GetEverythingComments", "parameters": [ { - "name": "commentId", - "in": "path", + "name": "page", + "in": "query", + "description": "Page number for paginating through results. Defaults to 1.", "schema": { "type": "integer", - "format": "int64" - }, - "required": true + "description": "Page number for paginating through results. Defaults to 1.", + "format": "int32" + } } ], "responses": { "200": { - "description": "GetComment 200 response", + "description": "GetEverythingComments 200 response", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetCommentResponseContent" + "$ref": "#/components/schemas/GetEverythingCommentsResponseContent" } } } @@ -5485,12 +5721,12 @@ } } }, - "404": { - "description": "NotFoundError 404 response", + "429": { + "description": "RateLimitError 429 response", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/NotFoundErrorResponseContent" + "$ref": "#/components/schemas/RateLimitErrorResponseContent" } } } @@ -5507,8 +5743,13 @@ } }, "tags": [ - "Messages" + "Everything" ], + "x-basecamp-pagination": { + "style": "link", + "totalCountHeader": "X-Total-Count", + "maxPageSize": 50 + }, "x-basecamp-retry": { "maxAttempts": 3, "baseDelayMs": 1000, @@ -5518,23 +5759,104 @@ 503 ] } - }, - "put": { - "description": "Update an existing comment", - "operationId": "UpdateComment", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateCommentRequestContent" - } - } - }, - "required": true - }, - "parameters": [ - { - "name": "commentId", + } + }, + "/comments/{commentId}": { + "get": { + "description": "Get a single comment by id", + "operationId": "GetComment", + "parameters": [ + { + "name": "commentId", + "in": "path", + "schema": { + "type": "integer", + "format": "int64" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "GetComment 200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetCommentResponseContent" + } + } + } + }, + "401": { + "description": "UnauthorizedError 401 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedErrorResponseContent" + } + } + } + }, + "403": { + "description": "ForbiddenError 403 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForbiddenErrorResponseContent" + } + } + } + }, + "404": { + "description": "NotFoundError 404 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundErrorResponseContent" + } + } + } + }, + "500": { + "description": "InternalServerError 500 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InternalServerErrorResponseContent" + } + } + } + } + }, + "tags": [ + "Messages" + ], + "x-basecamp-retry": { + "maxAttempts": 3, + "baseDelayMs": 1000, + "backoff": "exponential", + "retryOn": [ + 429, + 503 + ] + } + }, + "put": { + "description": "Update an existing comment", + "operationId": "UpdateComment", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateCommentRequestContent" + } + } + }, + "required": true + }, + "parameters": [ + { + "name": "commentId", "in": "path", "schema": { "type": "integer", @@ -6062,6 +6384,205 @@ } } }, + "/files.json": { + "get": { + "description": "Get every file recording across all accessible projects, newest-first\n(paginated). Heterogeneous: uploads and Basecamp documents carry their\nstandard recording shapes, while rich-text attachments are wrapped in a\nrecording envelope plus an `attachable_sgid` and blob metadata. Modeled as\nan optional-field superset (EverythingFile) so one element type decodes any\nvariant.", + "operationId": "GetEverythingFiles", + "parameters": [ + { + "name": "kind", + "in": "query", + "description": "Filter by file kind: all (default), images, pdfs, documents, or videos.", + "schema": { + "type": "string", + "description": "Filter by file kind: all (default), images, pdfs, documents, or videos." + } + }, + { + "name": "people_ids[]", + "in": "query", + "description": "Restrict to files created by the given people (repeatable).", + "style": "form", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int64" + }, + "description": "Restrict to files created by the given people (repeatable).", + "x-go-type-skip-optional-pointer": false + }, + "explode": true + }, + { + "name": "page", + "in": "query", + "description": "Page number for paginating through results. Defaults to 1.", + "schema": { + "type": "integer", + "description": "Page number for paginating through results. Defaults to 1.", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "GetEverythingFiles 200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetEverythingFilesResponseContent" + } + } + } + }, + "401": { + "description": "UnauthorizedError 401 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedErrorResponseContent" + } + } + } + }, + "403": { + "description": "ForbiddenError 403 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForbiddenErrorResponseContent" + } + } + } + }, + "429": { + "description": "RateLimitError 429 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RateLimitErrorResponseContent" + } + } + } + }, + "500": { + "description": "InternalServerError 500 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InternalServerErrorResponseContent" + } + } + } + } + }, + "tags": [ + "Everything" + ], + "x-basecamp-pagination": { + "style": "link", + "totalCountHeader": "X-Total-Count", + "maxPageSize": 50 + }, + "x-basecamp-retry": { + "maxAttempts": 3, + "baseDelayMs": 1000, + "backoff": "exponential", + "retryOn": [ + 429, + 503 + ] + } + } + }, + "/forwards.json": { + "get": { + "description": "Get every inbox forward across all accessible projects, newest-first\n(paginated). Each item embeds its `bucket`.", + "operationId": "GetEverythingForwards", + "parameters": [ + { + "name": "page", + "in": "query", + "description": "Page number for paginating through results. Defaults to 1.", + "schema": { + "type": "integer", + "description": "Page number for paginating through results. Defaults to 1.", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "GetEverythingForwards 200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetEverythingForwardsResponseContent" + } + } + } + }, + "401": { + "description": "UnauthorizedError 401 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedErrorResponseContent" + } + } + } + }, + "403": { + "description": "ForbiddenError 403 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForbiddenErrorResponseContent" + } + } + } + }, + "429": { + "description": "RateLimitError 429 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RateLimitErrorResponseContent" + } + } + } + }, + "500": { + "description": "InternalServerError 500 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InternalServerErrorResponseContent" + } + } + } + } + }, + "tags": [ + "Everything" + ], + "x-basecamp-pagination": { + "style": "link", + "totalCountHeader": "X-Total-Count", + "maxPageSize": 50 + }, + "x-basecamp-retry": { + "maxAttempts": 3, + "baseDelayMs": 1000, + "backoff": "exponential", + "retryOn": [ + 429, + 503 + ] + } + } + }, "/gauge_needles/{needleId}": { "delete": { "description": "Destroy a gauge needle", @@ -7488,6 +8009,93 @@ } } }, + "/messages.json": { + "get": { + "description": "Get every message across all accessible projects, newest-first (paginated).\nEach item embeds its `bucket` for project context.", + "operationId": "GetEverythingMessages", + "parameters": [ + { + "name": "page", + "in": "query", + "description": "Page number for paginating through results. Defaults to 1.", + "schema": { + "type": "integer", + "description": "Page number for paginating through results. Defaults to 1.", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "GetEverythingMessages 200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetEverythingMessagesResponseContent" + } + } + } + }, + "401": { + "description": "UnauthorizedError 401 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedErrorResponseContent" + } + } + } + }, + "403": { + "description": "ForbiddenError 403 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForbiddenErrorResponseContent" + } + } + } + }, + "429": { + "description": "RateLimitError 429 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RateLimitErrorResponseContent" + } + } + } + }, + "500": { + "description": "InternalServerError 500 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InternalServerErrorResponseContent" + } + } + } + } + }, + "tags": [ + "Everything" + ], + "x-basecamp-pagination": { + "style": "link", + "totalCountHeader": "X-Total-Count", + "maxPageSize": 50 + }, + "x-basecamp-retry": { + "maxAttempts": 3, + "baseDelayMs": 1000, + "backoff": "exponential", + "retryOn": [ + 429, + 503 + ] + } + } + }, "/messages/{messageId}": { "get": { "description": "Get a single message by id", @@ -9083,10 +9691,10 @@ { "name": "type", "in": "query", - "description": "Comment|Document|Kanban::Card|Kanban::Step|Message|Question::Answer|Schedule::Entry|Todo|Todolist|Upload|Vault", + "description": "Comment|Document|Door|Kanban::Card|Kanban::Step|Message|Question::Answer|Schedule::Entry|Todo|Todolist|Upload|Vault", "schema": { "type": "string", - "description": "Comment|Document|Kanban::Card|Kanban::Step|Message|Question::Answer|Schedule::Entry|Todo|Todolist|Upload|Vault" + "description": "Comment|Document|Door|Kanban::Card|Kanban::Step|Message|Question::Answer|Schedule::Entry|Todo|Todolist|Upload|Vault" }, "required": true, "examples": { @@ -16785,6 +17393,67 @@ } } }, + "/todos/overdue.json": { + "get": { + "description": "Get every overdue to-do across all accessible projects, oldest-due-date-first.\nA complete, unpaginated array; each item embeds its `bucket`.", + "operationId": "GetEverythingOverdueTodos", + "parameters": [], + "responses": { + "200": { + "description": "GetEverythingOverdueTodos 200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetEverythingOverdueTodosResponseContent" + } + } + } + }, + "401": { + "description": "UnauthorizedError 401 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedErrorResponseContent" + } + } + } + }, + "403": { + "description": "ForbiddenError 403 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForbiddenErrorResponseContent" + } + } + } + }, + "500": { + "description": "InternalServerError 500 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InternalServerErrorResponseContent" + } + } + } + } + }, + "tags": [ + "Everything" + ], + "x-basecamp-retry": { + "maxAttempts": 3, + "baseDelayMs": 1000, + "backoff": "exponential", + "retryOn": [ + 429, + 503 + ] + } + } + }, "/todos/{todoId}": { "delete": { "description": "Trash a todo (returns 204 No Content)", @@ -21455,6 +22124,30 @@ "visible_to_clients" ] }, + "DoorService": { + "type": "object", + "description": "Metadata describing the recognized external service backing an external link\n(`Door` recording): its display name, a canonical example URL, a short code,\nthe URL patterns Basecamp recognizes for it, and human supporting text. `code`\nis `other` for a generic link.", + "properties": { + "name": { + "type": "string" + }, + "example_url": { + "type": "string" + }, + "code": { + "type": "string" + }, + "valid_patterns": { + "type": "array", + "items": { + "type": "string" + } + }, + "supporting_text": { + "type": "string" + } + } + }, "EnableCardColumnOnHoldResponseContent": { "$ref": "#/components/schemas/CardColumn" }, @@ -21544,6 +22237,144 @@ } } }, + "EverythingFile": { + "type": "object", + "description": "A single item in the /files.json feed. An optional-field superset over three\nwire variants — a full Upload recording, a Basecamp Document recording, and a\nrich-text attachment wrapped in a recording envelope (distinguished by\n`attachable_sgid` and blob metadata). Every field is optional; a given\ninstance populates only the fields of the variant it represents. Unknown\nfields are ignored by every SDK decoder, so the superset need not enumerate\nevery field of the Upload/Document recordings.", + "properties": { + "id": { + "type": "integer", + "description": "Recording (Upload/Document) or attachment id.", + "format": "int64", + "x-go-type-skip-optional-pointer": false + }, + "status": { + "type": "string" + }, + "visible_to_clients": { + "type": "boolean", + "x-go-type-skip-optional-pointer": false + }, + "created_at": { + "type": "string", + "x-go-type": "time.Time", + "x-go-type-import": { + "path": "time" + }, + "x-go-type-skip-optional-pointer": false + }, + "updated_at": { + "type": "string", + "x-go-type": "time.Time", + "x-go-type-import": { + "path": "time" + }, + "x-go-type-skip-optional-pointer": false + }, + "title": { + "type": "string" + }, + "inherits_status": { + "type": "boolean", + "x-go-type-skip-optional-pointer": false + }, + "type": { + "type": "string", + "description": "\"Upload\", \"Document\", or \"Attachment\"." + }, + "url": { + "type": "string" + }, + "app_url": { + "type": "string" + }, + "bookmark_url": { + "type": "string" + }, + "subscription_url": { + "type": "string" + }, + "comments_count": { + "type": "integer", + "format": "int32" + }, + "comments_url": { + "type": "string" + }, + "boosts_count": { + "type": "integer", + "format": "int32" + }, + "boosts_url": { + "type": "string" + }, + "position": { + "type": "integer", + "format": "int32" + }, + "parent": { + "$ref": "#/components/schemas/RecordingParent" + }, + "bucket": { + "$ref": "#/components/schemas/RecordingBucket" + }, + "creator": { + "$ref": "#/components/schemas/Person" + }, + "attachable_sgid": { + "type": "string", + "description": "Present on the rich-text attachment variant: signed global id of the\nattachment (uploads/documents omit it)." + }, + "content_type": { + "type": "string" + }, + "byte_size": { + "type": "integer", + "format": "int64" + }, + "filename": { + "type": "string" + }, + "download_url": { + "type": "string", + "x-basecamp-auth-routable-url": {} + }, + "app_download_url": { + "type": "string" + }, + "width": { + "type": "integer", + "description": "Pixel width; null for non-image blobs and may be float-spelled (1024.0).", + "format": "int32", + "nullable": true, + "x-go-type": "types.FlexInt", + "x-go-type-import": { + "path": "github.com/basecamp/basecamp-sdk/go/pkg/types" + }, + "x-go-type-skip-optional-pointer": false + }, + "height": { + "type": "integer", + "description": "Pixel height; null for non-image blobs and may be float-spelled (1024.0).", + "format": "int32", + "nullable": true, + "x-go-type": "types.FlexInt", + "x-go-type-import": { + "path": "github.com/basecamp/basecamp-sdk/go/pkg/types" + }, + "x-go-type-skip-optional-pointer": false + }, + "description": { + "type": "string", + "description": "Rich-text description (upload/document variants)." + }, + "description_attachments": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextAttachment" + } + } + } + }, "FirstWeekDay": { "type": "string", "enum": [ @@ -22061,6 +22892,54 @@ "GetDocumentResponseContent": { "$ref": "#/components/schemas/Document" }, + "GetEverythingBoostsResponseContent": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Boost" + } + }, + "GetEverythingCheckinsResponseContent": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Recording" + } + }, + "GetEverythingCommentsResponseContent": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Recording" + } + }, + "GetEverythingFilesResponseContent": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EverythingFile" + } + }, + "GetEverythingForwardsResponseContent": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Recording" + } + }, + "GetEverythingMessagesResponseContent": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Recording" + } + }, + "GetEverythingOverdueCardsResponseContent": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Card" + } + }, + "GetEverythingOverdueTodosResponseContent": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Todo" + } + }, "GetForwardReplyResponseContent": { "$ref": "#/components/schemas/ForwardReply" }, @@ -24202,7 +25081,8 @@ "type": "string" }, "url": { - "type": "string" + "type": "string", + "description": "API URL of the recording. Exception: in the `type=Door` (external-link)\nprojection, `url` is the door's **external destination address** (e.g. the\nFigma/Dropbox URL) and `app_url` is the Basecamp redirector — see the\ndoor-specific `service`/`description` fields below." }, "app_url": { "type": "string" @@ -24237,6 +25117,18 @@ "subscription_url": { "type": "string" }, + "position": { + "type": "integer", + "description": "Ordinal position within the project's External links section. Present on\n`Door` (external-link) recordings.", + "format": "int32" + }, + "description": { + "type": "string", + "description": "Rich-text (HTML) description shown beneath an external link. Present only\non `Door` recordings returned by the `type=Door` recordings query (the only\nendpoint that returns the full door shape). The external destination\naddress is `url` (not this field); `app_url` is the Basecamp redirector.\nSee `spec/api-gaps/external-links-doors.md`." + }, + "service": { + "$ref": "#/components/schemas/DoorService" + }, "parent": { "$ref": "#/components/schemas/RecordingParent" }, @@ -24254,7 +25146,6 @@ "creator", "id", "inherits_status", - "parent", "status", "title", "type", diff --git a/typescript/src/generated/path-mapping.ts b/typescript/src/generated/path-mapping.ts index 723b74331..3a5c16956 100644 --- a/typescript/src/generated/path-mapping.ts +++ b/typescript/src/generated/path-mapping.ts @@ -11,6 +11,7 @@ export const PATH_TO_OPERATION: Record = { "DELETE:/{accountId}/account/logo.json": "RemoveAccountLogo", "PUT:/{accountId}/account/logo.json": "UpdateAccountLogo", "PUT:/{accountId}/account/name.json": "UpdateAccountName", + "GET:/{accountId}/boosts.json": "GetEverythingBoosts", "DELETE:/{accountId}/boosts/{boostId}": "DeleteBoost", "GET:/{accountId}/boosts/{boostId}": "GetBoost", "POST:/{accountId}/buckets/{bucketId}/card_tables/{cardTableId}/wormholes.json": "CreateWormhole", @@ -39,6 +40,7 @@ export const PATH_TO_OPERATION: Record = { "GET:/{accountId}/card_tables/steps/{stepId}": "GetCardStep", "PUT:/{accountId}/card_tables/steps/{stepId}": "UpdateCardStep", "PUT:/{accountId}/card_tables/steps/{stepId}/completions.json": "SetCardStepCompletion", + "GET:/{accountId}/cards/overdue.json": "GetEverythingOverdueCards", "GET:/{accountId}/categories.json": "ListMessageTypes", "POST:/{accountId}/categories.json": "CreateMessageType", "DELETE:/{accountId}/categories/{typeId}": "DeleteMessageType", @@ -58,12 +60,14 @@ export const PATH_TO_OPERATION: Record = { "PUT:/{accountId}/chats/{campfireId}/lines/{lineId}": "UpdateCampfireLine", "GET:/{accountId}/chats/{campfireId}/uploads.json": "ListCampfireUploads", "POST:/{accountId}/chats/{campfireId}/uploads.json": "CreateCampfireUpload", + "GET:/{accountId}/checkins.json": "GetEverythingCheckins", "GET:/{accountId}/client/approvals.json": "ListClientApprovals", "GET:/{accountId}/client/approvals/{approvalId}": "GetClientApproval", "GET:/{accountId}/client/correspondences.json": "ListClientCorrespondences", "GET:/{accountId}/client/correspondences/{correspondenceId}": "GetClientCorrespondence", "GET:/{accountId}/client/recordings/{recordingId}/replies.json": "ListClientReplies", "GET:/{accountId}/client/recordings/{recordingId}/replies/{replyId}": "GetClientReply", + "GET:/{accountId}/comments.json": "GetEverythingComments", "GET:/{accountId}/comments/{commentId}": "GetComment", "PUT:/{accountId}/comments/{commentId}": "UpdateComment", "DELETE:/{accountId}/dock/tools/{toolId}": "DeleteTool", @@ -71,6 +75,8 @@ export const PATH_TO_OPERATION: Record = { "PUT:/{accountId}/dock/tools/{toolId}": "UpdateTool", "GET:/{accountId}/documents/{documentId}": "GetDocument", "PUT:/{accountId}/documents/{documentId}": "UpdateDocument", + "GET:/{accountId}/files.json": "GetEverythingFiles", + "GET:/{accountId}/forwards.json": "GetEverythingForwards", "DELETE:/{accountId}/gauge_needles/{needleId}": "DestroyGaugeNeedle", "GET:/{accountId}/gauge_needles/{needleId}": "GetGaugeNeedle", "PUT:/{accountId}/gauge_needles/{needleId}": "UpdateGaugeNeedle", @@ -87,6 +93,7 @@ export const PATH_TO_OPERATION: Record = { "GET:/{accountId}/message_boards/{boardId}": "GetMessageBoard", "GET:/{accountId}/message_boards/{boardId}/messages.json": "ListMessages", "POST:/{accountId}/message_boards/{boardId}/messages.json": "CreateMessage", + "GET:/{accountId}/messages.json": "GetEverythingMessages", "GET:/{accountId}/messages/{messageId}": "GetMessage", "PUT:/{accountId}/messages/{messageId}": "UpdateMessage", "GET:/{accountId}/question_answers/{answerId}": "GetAnswer", @@ -153,6 +160,7 @@ export const PATH_TO_OPERATION: Record = { "DELETE:/{accountId}/todos/{todoId}/completion.json": "UncompleteTodo", "POST:/{accountId}/todos/{todoId}/completion.json": "CompleteTodo", "PUT:/{accountId}/todos/{todoId}/position.json": "RepositionTodo", + "GET:/{accountId}/todos/overdue.json": "GetEverythingOverdueTodos", "GET:/{accountId}/todosets/{todosetId}": "GetTodoset", "GET:/{accountId}/todosets/{todosetId}/hill.json": "GetHillChart", "PUT:/{accountId}/todosets/{todosetId}/hills/settings.json": "UpdateHillChartSettings", diff --git a/typescript/src/generated/schema.d.ts b/typescript/src/generated/schema.d.ts index f85c30618..c8e5f3f97 100644 --- a/typescript/src/generated/schema.d.ts +++ b/typescript/src/generated/schema.d.ts @@ -77,6 +77,26 @@ export interface paths { patch?: never; trace?: never; }; + "/boosts.json": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * @description Get every boost across all accessible projects, newest-first (paginated). + * Each boost carries its `booster` and the `recording` it boosts. + */ + get: operations["GetEverythingBoosts"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/boosts/{boostId}": { parameters: { query?: never; @@ -425,6 +445,26 @@ export interface paths { patch?: never; trace?: never; }; + "/cards/overdue.json": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * @description Get every overdue card across all accessible projects, oldest-due-date-first. + * A complete, unpaginated array; each item embeds its `bucket`. + */ + get: operations["GetEverythingOverdueCards"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/categories.json": { parameters: { query?: never; @@ -621,6 +661,26 @@ export interface paths { patch?: never; trace?: never; }; + "/checkins.json": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * @description Get every automatic check-in answer across all accessible projects, + * newest-first (paginated). Each item embeds its `bucket`. + */ + get: operations["GetEverythingCheckins"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/circles/people.json": { parameters: { query?: never; @@ -760,6 +820,26 @@ export interface paths { patch?: never; trace?: never; }; + "/comments.json": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * @description Get every comment across all accessible projects, newest-first (paginated). + * Each item embeds its `bucket`. + */ + get: operations["GetEverythingComments"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/comments/{commentId}": { parameters: { query?: never; @@ -815,6 +895,50 @@ export interface paths { patch?: never; trace?: never; }; + "/files.json": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * @description Get every file recording across all accessible projects, newest-first + * (paginated). Heterogeneous: uploads and Basecamp documents carry their + * standard recording shapes, while rich-text attachments are wrapped in a + * recording envelope plus an `attachable_sgid` and blob metadata. Modeled as + * an optional-field superset (EverythingFile) so one element type decodes any + * variant. + */ + get: operations["GetEverythingFiles"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/forwards.json": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * @description Get every inbox forward across all accessible projects, newest-first + * (paginated). Each item embeds its `bucket`. + */ + get: operations["GetEverythingForwards"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/gauge_needles/{needleId}": { parameters: { query?: never; @@ -1006,6 +1130,26 @@ export interface paths { patch?: never; trace?: never; }; + "/messages.json": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * @description Get every message across all accessible projects, newest-first (paginated). + * Each item embeds its `bucket` for project context. + */ + get: operations["GetEverythingMessages"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/messages/{messageId}": { parameters: { query?: never; @@ -2317,6 +2461,26 @@ export interface paths { patch?: never; trace?: never; }; + "/todos/overdue.json": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * @description Get every overdue to-do across all accessible projects, oldest-due-date-first. + * A complete, unpaginated array; each item embeds its `bucket`. + */ + get: operations["GetEverythingOverdueTodos"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/todos/{todoId}": { parameters: { query?: never; @@ -3241,6 +3405,19 @@ export interface components { boosts_count?: number; boosts_url?: string; }; + /** + * @description Metadata describing the recognized external service backing an external link + * (`Door` recording): its display name, a canonical example URL, a short code, + * the URL patterns Basecamp recognizes for it, and human supporting text. `code` + * is `other` for a generic link. + */ + DoorService: { + name?: string; + example_url?: string; + code?: string; + valid_patterns?: string[]; + supporting_text?: string; + }; EnableCardColumnOnHoldResponseContent: components["schemas"]["CardColumn"]; EnableOutOfOfficeRequestContent: { out_of_office: components["schemas"]["OutOfOfficePayload"]; @@ -3264,6 +3441,69 @@ export interface components { removed_person_ids?: number[]; notified_recipient_ids?: number[]; }; + /** + * @description A single item in the /files.json feed. An optional-field superset over three + * wire variants — a full Upload recording, a Basecamp Document recording, and a + * rich-text attachment wrapped in a recording envelope (distinguished by + * `attachable_sgid` and blob metadata). Every field is optional; a given + * instance populates only the fields of the variant it represents. Unknown + * fields are ignored by every SDK decoder, so the superset need not enumerate + * every field of the Upload/Document recordings. + */ + EverythingFile: { + /** + * Format: int64 + * @description Recording (Upload/Document) or attachment id. + */ + id?: number; + status?: string; + visible_to_clients?: boolean; + created_at?: string; + updated_at?: string; + title?: string; + inherits_status?: boolean; + /** @description "Upload", "Document", or "Attachment". */ + type?: string; + url?: string; + app_url?: string; + bookmark_url?: string; + subscription_url?: string; + /** Format: int32 */ + comments_count?: number; + comments_url?: string; + /** Format: int32 */ + boosts_count?: number; + boosts_url?: string; + /** Format: int32 */ + position?: number; + parent?: components["schemas"]["RecordingParent"]; + bucket?: components["schemas"]["RecordingBucket"]; + creator?: components["schemas"]["Person"]; + /** + * @description Present on the rich-text attachment variant: signed global id of the + * attachment (uploads/documents omit it). + */ + attachable_sgid?: string; + content_type?: string; + /** Format: int64 */ + byte_size?: number; + filename?: string; + download_url?: string; + app_download_url?: string; + /** + * Format: int32 + * @description Pixel width; null for non-image blobs and may be float-spelled (1024.0). + */ + width?: number | null; + /** + * Format: int32 + * @description Pixel height; null for non-image blobs and may be float-spelled (1024.0). + */ + height?: number | null; + /** @description Rich-text description (upload/document variants). */ + description?: string; + description_attachments?: components["schemas"]["RichTextAttachment"][]; + }; /** @enum {string} */ FirstWeekDay: "Sunday" | "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday" | "Saturday"; ForbiddenErrorResponseContent: { @@ -3416,6 +3656,14 @@ export interface components { GetClientReplyResponseContent: components["schemas"]["ClientReply"]; GetCommentResponseContent: components["schemas"]["Comment"]; GetDocumentResponseContent: components["schemas"]["Document"]; + GetEverythingBoostsResponseContent: components["schemas"]["Boost"][]; + GetEverythingCheckinsResponseContent: components["schemas"]["Recording"][]; + GetEverythingCommentsResponseContent: components["schemas"]["Recording"][]; + GetEverythingFilesResponseContent: components["schemas"]["EverythingFile"][]; + GetEverythingForwardsResponseContent: components["schemas"]["Recording"][]; + GetEverythingMessagesResponseContent: components["schemas"]["Recording"][]; + GetEverythingOverdueCardsResponseContent: components["schemas"]["Card"][]; + GetEverythingOverdueTodosResponseContent: components["schemas"]["Todo"][]; GetForwardReplyResponseContent: components["schemas"]["ForwardReply"]; GetForwardResponseContent: components["schemas"]["Forward"]; GetGaugeNeedleResponseContent: components["schemas"]["GaugeNeedle"]; @@ -4035,6 +4283,12 @@ export interface components { title: string; inherits_status: boolean; type: string; + /** + * @description API URL of the recording. Exception: in the `type=Door` (external-link) + * projection, `url` is the door's **external destination address** (e.g. the + * Figma/Dropbox URL) and `app_url` is the Basecamp redirector — see the + * door-specific `service`/`description` fields below. + */ url: string; app_url: string; bookmark_url?: string; @@ -4055,7 +4309,22 @@ export interface components { comments_count?: number; comments_url?: string; subscription_url?: string; - parent: components["schemas"]["RecordingParent"]; + /** + * Format: int32 + * @description Ordinal position within the project's External links section. Present on + * `Door` (external-link) recordings. + */ + position?: number; + /** + * @description Rich-text (HTML) description shown beneath an external link. Present only + * on `Door` recordings returned by the `type=Door` recordings query (the only + * endpoint that returns the full door shape). The external destination + * address is `url` (not this field); `app_url` is the Basecamp redirector. + * See `spec/api-gaps/external-links-doors.md`. + */ + description?: string; + service?: components["schemas"]["DoorService"]; + parent?: components["schemas"]["RecordingParent"]; bucket: components["schemas"]["RecordingBucket"]; creator: components["schemas"]["Person"]; }; @@ -5317,6 +5586,65 @@ export interface operations { }; }; }; + GetEverythingBoosts: { + parameters: { + query?: { + /** @description Page number for paginating through results. Defaults to 1. */ + page?: number; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description GetEverythingBoosts 200 response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["GetEverythingBoostsResponseContent"]; + }; + }; + /** @description UnauthorizedError 401 response */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["UnauthorizedErrorResponseContent"]; + }; + }; + /** @description ForbiddenError 403 response */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ForbiddenErrorResponseContent"]; + }; + }; + /** @description RateLimitError 429 response */ + 429: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["RateLimitErrorResponseContent"]; + }; + }; + /** @description InternalServerError 500 response */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["InternalServerErrorResponseContent"]; + }; + }; + }; + }; GetBoost: { parameters: { query?: never; @@ -7182,6 +7510,53 @@ export interface operations { }; }; }; + GetEverythingOverdueCards: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description GetEverythingOverdueCards 200 response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["GetEverythingOverdueCardsResponseContent"]; + }; + }; + /** @description UnauthorizedError 401 response */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["UnauthorizedErrorResponseContent"]; + }; + }; + /** @description ForbiddenError 403 response */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ForbiddenErrorResponseContent"]; + }; + }; + /** @description InternalServerError 500 response */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["InternalServerErrorResponseContent"]; + }; + }; + }; + }; ListMessageTypes: { parameters: { query?: never; @@ -8389,6 +8764,65 @@ export interface operations { }; }; }; + GetEverythingCheckins: { + parameters: { + query?: { + /** @description Page number for paginating through results. Defaults to 1. */ + page?: number; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description GetEverythingCheckins 200 response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["GetEverythingCheckinsResponseContent"]; + }; + }; + /** @description UnauthorizedError 401 response */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["UnauthorizedErrorResponseContent"]; + }; + }; + /** @description ForbiddenError 403 response */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ForbiddenErrorResponseContent"]; + }; + }; + /** @description RateLimitError 429 response */ + 429: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["RateLimitErrorResponseContent"]; + }; + }; + /** @description InternalServerError 500 response */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["InternalServerErrorResponseContent"]; + }; + }; + }; + }; ListPingablePeople: { parameters: { query?: never; @@ -8611,7 +9045,65 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["RateLimitErrorResponseContent"]; + "application/json": components["schemas"]["RateLimitErrorResponseContent"]; + }; + }; + /** @description InternalServerError 500 response */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["InternalServerErrorResponseContent"]; + }; + }; + }; + }; + GetClientCorrespondence: { + parameters: { + query?: never; + header?: never; + path: { + correspondenceId: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description GetClientCorrespondence 200 response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["GetClientCorrespondenceResponseContent"]; + }; + }; + /** @description UnauthorizedError 401 response */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["UnauthorizedErrorResponseContent"]; + }; + }; + /** @description ForbiddenError 403 response */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ForbiddenErrorResponseContent"]; + }; + }; + /** @description NotFoundError 404 response */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["NotFoundErrorResponseContent"]; }; }; /** @description InternalServerError 500 response */ @@ -8625,24 +9117,24 @@ export interface operations { }; }; }; - GetClientCorrespondence: { + ListClientReplies: { parameters: { query?: never; header?: never; path: { - correspondenceId: number; + recordingId: number; }; cookie?: never; }; requestBody?: never; responses: { - /** @description GetClientCorrespondence 200 response */ + /** @description ListClientReplies 200 response */ 200: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["GetClientCorrespondenceResponseContent"]; + "application/json": components["schemas"]["ListClientRepliesResponseContent"]; }; }; /** @description UnauthorizedError 401 response */ @@ -8663,13 +9155,13 @@ export interface operations { "application/json": components["schemas"]["ForbiddenErrorResponseContent"]; }; }; - /** @description NotFoundError 404 response */ - 404: { + /** @description RateLimitError 429 response */ + 429: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["NotFoundErrorResponseContent"]; + "application/json": components["schemas"]["RateLimitErrorResponseContent"]; }; }; /** @description InternalServerError 500 response */ @@ -8683,24 +9175,25 @@ export interface operations { }; }; }; - ListClientReplies: { + GetClientReply: { parameters: { query?: never; header?: never; path: { recordingId: number; + replyId: number; }; cookie?: never; }; requestBody?: never; responses: { - /** @description ListClientReplies 200 response */ + /** @description GetClientReply 200 response */ 200: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["ListClientRepliesResponseContent"]; + "application/json": components["schemas"]["GetClientReplyResponseContent"]; }; }; /** @description UnauthorizedError 401 response */ @@ -8721,13 +9214,13 @@ export interface operations { "application/json": components["schemas"]["ForbiddenErrorResponseContent"]; }; }; - /** @description RateLimitError 429 response */ - 429: { + /** @description NotFoundError 404 response */ + 404: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["RateLimitErrorResponseContent"]; + "application/json": components["schemas"]["NotFoundErrorResponseContent"]; }; }; /** @description InternalServerError 500 response */ @@ -8741,25 +9234,25 @@ export interface operations { }; }; }; - GetClientReply: { + GetEverythingComments: { parameters: { - query?: never; - header?: never; - path: { - recordingId: number; - replyId: number; + query?: { + /** @description Page number for paginating through results. Defaults to 1. */ + page?: number; }; + header?: never; + path?: never; cookie?: never; }; requestBody?: never; responses: { - /** @description GetClientReply 200 response */ + /** @description GetEverythingComments 200 response */ 200: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["GetClientReplyResponseContent"]; + "application/json": components["schemas"]["GetEverythingCommentsResponseContent"]; }; }; /** @description UnauthorizedError 401 response */ @@ -8780,13 +9273,13 @@ export interface operations { "application/json": components["schemas"]["ForbiddenErrorResponseContent"]; }; }; - /** @description NotFoundError 404 response */ - 404: { + /** @description RateLimitError 429 response */ + 429: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["NotFoundErrorResponseContent"]; + "application/json": components["schemas"]["RateLimitErrorResponseContent"]; }; }; /** @description InternalServerError 500 response */ @@ -9243,6 +9736,128 @@ export interface operations { }; }; }; + GetEverythingFiles: { + parameters: { + query?: { + /** @description Filter by file kind: all (default), images, pdfs, documents, or videos. */ + kind?: string; + /** @description Restrict to files created by the given people (repeatable). */ + "people_ids[]"?: number[]; + /** @description Page number for paginating through results. Defaults to 1. */ + page?: number; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description GetEverythingFiles 200 response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["GetEverythingFilesResponseContent"]; + }; + }; + /** @description UnauthorizedError 401 response */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["UnauthorizedErrorResponseContent"]; + }; + }; + /** @description ForbiddenError 403 response */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ForbiddenErrorResponseContent"]; + }; + }; + /** @description RateLimitError 429 response */ + 429: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["RateLimitErrorResponseContent"]; + }; + }; + /** @description InternalServerError 500 response */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["InternalServerErrorResponseContent"]; + }; + }; + }; + }; + GetEverythingForwards: { + parameters: { + query?: { + /** @description Page number for paginating through results. Defaults to 1. */ + page?: number; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description GetEverythingForwards 200 response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["GetEverythingForwardsResponseContent"]; + }; + }; + /** @description UnauthorizedError 401 response */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["UnauthorizedErrorResponseContent"]; + }; + }; + /** @description ForbiddenError 403 response */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ForbiddenErrorResponseContent"]; + }; + }; + /** @description RateLimitError 429 response */ + 429: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["RateLimitErrorResponseContent"]; + }; + }; + /** @description InternalServerError 500 response */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["InternalServerErrorResponseContent"]; + }; + }; + }; + }; GetGaugeNeedle: { parameters: { query?: never; @@ -10253,6 +10868,65 @@ export interface operations { }; }; }; + GetEverythingMessages: { + parameters: { + query?: { + /** @description Page number for paginating through results. Defaults to 1. */ + page?: number; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description GetEverythingMessages 200 response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["GetEverythingMessagesResponseContent"]; + }; + }; + /** @description UnauthorizedError 401 response */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["UnauthorizedErrorResponseContent"]; + }; + }; + /** @description ForbiddenError 403 response */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ForbiddenErrorResponseContent"]; + }; + }; + /** @description RateLimitError 429 response */ + 429: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["RateLimitErrorResponseContent"]; + }; + }; + /** @description InternalServerError 500 response */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["InternalServerErrorResponseContent"]; + }; + }; + }; + }; GetMessage: { parameters: { query?: never; @@ -11428,7 +12102,7 @@ export interface operations { ListRecordings: { parameters: { query: { - /** @description Comment|Document|Kanban::Card|Kanban::Step|Message|Question::Answer|Schedule::Entry|Todo|Todolist|Upload|Vault */ + /** @description Comment|Document|Door|Kanban::Card|Kanban::Step|Message|Question::Answer|Schedule::Entry|Todo|Todolist|Upload|Vault */ type: string; bucket?: string; /** @description active|archived|trashed */ @@ -16627,6 +17301,53 @@ export interface operations { }; }; }; + GetEverythingOverdueTodos: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description GetEverythingOverdueTodos 200 response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["GetEverythingOverdueTodosResponseContent"]; + }; + }; + /** @description UnauthorizedError 401 response */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["UnauthorizedErrorResponseContent"]; + }; + }; + /** @description ForbiddenError 403 response */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ForbiddenErrorResponseContent"]; + }; + }; + /** @description InternalServerError 500 response */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["InternalServerErrorResponseContent"]; + }; + }; + }; + }; GetTodo: { parameters: { query?: never; diff --git a/typescript/src/generated/services/boosts.ts b/typescript/src/generated/services/boosts.ts index 58b42fe5c..11bba775c 100644 --- a/typescript/src/generated/services/boosts.ts +++ b/typescript/src/generated/services/boosts.ts @@ -14,6 +14,8 @@ import { Errors } from "../../errors.js"; // Types // ============================================================================= +/** Boost entity from the Basecamp API. */ +export type Boost = components["schemas"]["Boost"]; /** * Options for listForRecording. @@ -56,7 +58,7 @@ export class BoostsService extends BaseService { /** * Get a single boost * @param boostId - The boost ID - * @returns The boost + * @returns The Boost * @throws {BasecampError} If the resource is not found * * @example @@ -64,7 +66,7 @@ export class BoostsService extends BaseService { * const result = await client.boosts.get(123); * ``` */ - async get(boostId: number): Promise { + async get(boostId: number): Promise { const response = await this.request( { service: "Boosts", @@ -116,14 +118,14 @@ export class BoostsService extends BaseService { * List boosts on a recording * @param recordingId - The recording ID * @param options - Optional query parameters - * @returns All results across all pages, with .meta.totalCount + * @returns All Boost across all pages, with .meta.totalCount * * @example * ```ts * const result = await client.boosts.listForRecording(123); * ``` */ - async listForRecording(recordingId: number, options?: ListForRecordingBoostOptions): Promise { + async listForRecording(recordingId: number, options?: ListForRecordingBoostOptions): Promise> { return this.requestPaginated( { service: "Boosts", @@ -146,7 +148,7 @@ export class BoostsService extends BaseService { * Create a boost on a recording * @param recordingId - The recording ID * @param req - Recording_boost creation parameters - * @returns The recording_boost + * @returns The Boost * @throws {BasecampError} If required fields are missing or invalid * * @example @@ -154,7 +156,7 @@ export class BoostsService extends BaseService { * const result = await client.boosts.createForRecording(123, { content: "Hello world" }); * ``` */ - async createForRecording(recordingId: number, req: CreateForRecordingBoostRequest): Promise { + async createForRecording(recordingId: number, req: CreateForRecordingBoostRequest): Promise { if (!req.content) { throw Errors.validation("Content is required"); } @@ -184,14 +186,14 @@ export class BoostsService extends BaseService { * @param recordingId - The recording ID * @param eventId - The event ID * @param options - Optional query parameters - * @returns All results across all pages, with .meta.totalCount + * @returns All Boost across all pages, with .meta.totalCount * * @example * ```ts * const result = await client.boosts.listForEvent(123, 123); * ``` */ - async listForEvent(recordingId: number, eventId: number, options?: ListForEventBoostOptions): Promise { + async listForEvent(recordingId: number, eventId: number, options?: ListForEventBoostOptions): Promise> { return this.requestPaginated( { service: "Boosts", @@ -215,7 +217,7 @@ export class BoostsService extends BaseService { * @param recordingId - The recording ID * @param eventId - The event ID * @param req - Event_boost creation parameters - * @returns The event_boost + * @returns The Boost * @throws {BasecampError} If required fields are missing or invalid * * @example @@ -223,7 +225,7 @@ export class BoostsService extends BaseService { * const result = await client.boosts.createForEvent(123, 123, { content: "Hello world" }); * ``` */ - async createForEvent(recordingId: number, eventId: number, req: CreateForEventBoostRequest): Promise { + async createForEvent(recordingId: number, eventId: number, req: CreateForEventBoostRequest): Promise { if (!req.content) { throw Errors.validation("Content is required"); } diff --git a/typescript/src/generated/services/everything.ts b/typescript/src/generated/services/everything.ts new file mode 100644 index 000000000..48754cd68 --- /dev/null +++ b/typescript/src/generated/services/everything.ts @@ -0,0 +1,304 @@ +/** + * Everything service for the Basecamp API. + * + * @generated from OpenAPI spec - do not edit directly + */ + +import { BaseService } from "../../services/base.js"; +import type { components } from "../schema.js"; +import { ListResult } from "../../pagination.js"; +import type { PaginationOptions } from "../../pagination.js"; + +// ============================================================================= +// Types +// ============================================================================= + +/** Boost entity from the Basecamp API. */ +export type Boost = components["schemas"]["Boost"]; +/** Card entity from the Basecamp API. */ +export type Card = components["schemas"]["Card"]; +/** Recording entity from the Basecamp API. */ +export type Recording = components["schemas"]["Recording"]; +/** EverythingFile entity from the Basecamp API. */ +export type EverythingFile = components["schemas"]["EverythingFile"]; +/** Todo entity from the Basecamp API. */ +export type Todo = components["schemas"]["Todo"]; + +/** + * Options for everythingBoosts. + */ +export interface EverythingBoostsEverythingOptions extends PaginationOptions { + /** Page number for paginating through results. Defaults to 1. */ + page?: number; +} + +/** + * Options for everythingCheckins. + */ +export interface EverythingCheckinsEverythingOptions extends PaginationOptions { + /** Page number for paginating through results. Defaults to 1. */ + page?: number; +} + +/** + * Options for everythingComments. + */ +export interface EverythingCommentsEverythingOptions extends PaginationOptions { + /** Page number for paginating through results. Defaults to 1. */ + page?: number; +} + +/** + * Options for everythingFiles. + */ +export interface EverythingFilesEverythingOptions extends PaginationOptions { + /** Filter by file kind: all (default), images, pdfs, documents, or videos. */ + kind?: string; + /** Restrict to files created by the given people (repeatable). */ + peopleIds?: number[]; + /** Page number for paginating through results. Defaults to 1. */ + page?: number; +} + +/** + * Options for everythingForwards. + */ +export interface EverythingForwardsEverythingOptions extends PaginationOptions { + /** Page number for paginating through results. Defaults to 1. */ + page?: number; +} + +/** + * Options for everythingMessages. + */ +export interface EverythingMessagesEverythingOptions extends PaginationOptions { + /** Page number for paginating through results. Defaults to 1. */ + page?: number; +} + + +// ============================================================================= +// Service +// ============================================================================= + +/** + * Service for Everything operations. + */ +export class EverythingService extends BaseService { + + /** + * Get every boost across all accessible projects, newest-first (paginated). + * @param options - Optional query parameters + * @returns All Boost across all pages, with .meta.totalCount + * + * @example + * ```ts + * const result = await client.everything.everythingBoosts(); + * ``` + */ + async everythingBoosts(options?: EverythingBoostsEverythingOptions): Promise> { + return this.requestPaginated( + { + service: "Everything", + operation: "GetEverythingBoosts", + resourceType: "everything_boost", + isMutation: false, + }, + () => + this.client.GET("/boosts.json", { + params: { + query: { page: options?.page }, + }, + }) + , options + ); + } + + /** + * Get every overdue card across all accessible projects, oldest-due-date-first. + * @returns Array of Card + * + * @example + * ```ts + * const result = await client.everything.everythingOverdueCards(); + * ``` + */ + async everythingOverdueCards(): Promise { + const response = await this.request( + { + service: "Everything", + operation: "GetEverythingOverdueCards", + resourceType: "everything_overdue_card", + isMutation: false, + }, + () => + this.client.GET("/cards/overdue.json", { + }) + ); + return response ?? []; + } + + /** + * Get every automatic check-in answer across all accessible projects, + * @param options - Optional query parameters + * @returns All Recording across all pages, with .meta.totalCount + * + * @example + * ```ts + * const result = await client.everything.everythingCheckins(); + * ``` + */ + async everythingCheckins(options?: EverythingCheckinsEverythingOptions): Promise> { + return this.requestPaginated( + { + service: "Everything", + operation: "GetEverythingCheckins", + resourceType: "everything_checkin", + isMutation: false, + }, + () => + this.client.GET("/checkins.json", { + params: { + query: { page: options?.page }, + }, + }) + , options + ); + } + + /** + * Get every comment across all accessible projects, newest-first (paginated). + * @param options - Optional query parameters + * @returns All Recording across all pages, with .meta.totalCount + * + * @example + * ```ts + * const result = await client.everything.everythingComments(); + * ``` + */ + async everythingComments(options?: EverythingCommentsEverythingOptions): Promise> { + return this.requestPaginated( + { + service: "Everything", + operation: "GetEverythingComments", + resourceType: "everything_comment", + isMutation: false, + }, + () => + this.client.GET("/comments.json", { + params: { + query: { page: options?.page }, + }, + }) + , options + ); + } + + /** + * Get every file recording across all accessible projects, newest-first + * @param options - Optional query parameters + * @returns All EverythingFile across all pages, with .meta.totalCount + * + * @example + * ```ts + * const result = await client.everything.everythingFiles(); + * ``` + */ + async everythingFiles(options?: EverythingFilesEverythingOptions): Promise> { + return this.requestPaginated( + { + service: "Everything", + operation: "GetEverythingFiles", + resourceType: "everything_file", + isMutation: false, + }, + () => + this.client.GET("/files.json", { + params: { + query: { kind: options?.kind, "people_ids[]": options?.peopleIds, page: options?.page }, + }, + }) + , options + ); + } + + /** + * Get every inbox forward across all accessible projects, newest-first + * @param options - Optional query parameters + * @returns All Recording across all pages, with .meta.totalCount + * + * @example + * ```ts + * const result = await client.everything.everythingForwards(); + * ``` + */ + async everythingForwards(options?: EverythingForwardsEverythingOptions): Promise> { + return this.requestPaginated( + { + service: "Everything", + operation: "GetEverythingForwards", + resourceType: "everything_forward", + isMutation: false, + }, + () => + this.client.GET("/forwards.json", { + params: { + query: { page: options?.page }, + }, + }) + , options + ); + } + + /** + * Get every message across all accessible projects, newest-first (paginated). + * @param options - Optional query parameters + * @returns All Recording across all pages, with .meta.totalCount + * + * @example + * ```ts + * const result = await client.everything.everythingMessages(); + * ``` + */ + async everythingMessages(options?: EverythingMessagesEverythingOptions): Promise> { + return this.requestPaginated( + { + service: "Everything", + operation: "GetEverythingMessages", + resourceType: "everything_message", + isMutation: false, + }, + () => + this.client.GET("/messages.json", { + params: { + query: { page: options?.page }, + }, + }) + , options + ); + } + + /** + * Get every overdue to-do across all accessible projects, oldest-due-date-first. + * @returns Array of Todo + * + * @example + * ```ts + * const result = await client.everything.everythingOverdueTodos(); + * ``` + */ + async everythingOverdueTodos(): Promise { + const response = await this.request( + { + service: "Everything", + operation: "GetEverythingOverdueTodos", + resourceType: "everything_overdue_todo", + isMutation: false, + }, + () => + this.client.GET("/todos/overdue.json", { + }) + ); + return response ?? []; + } +} \ No newline at end of file diff --git a/typescript/src/generated/services/index.ts b/typescript/src/generated/services/index.ts index 781347443..00ff817dc 100644 --- a/typescript/src/generated/services/index.ts +++ b/typescript/src/generated/services/index.ts @@ -1,5 +1,6 @@ export { AccountService } from "./account.js"; export { AttachmentsService } from "./attachments.js"; +export { EverythingService } from "./everything.js"; export { BoostsService } from "./boosts.js"; export { CardColumnsService } from "./card-columns.js"; export { WormholesService } from "./wormholes.js"; diff --git a/typescript/src/generated/services/recordings.ts b/typescript/src/generated/services/recordings.ts index a2407cb32..07a240a12 100644 --- a/typescript/src/generated/services/recordings.ts +++ b/typescript/src/generated/services/recordings.ts @@ -42,7 +42,7 @@ export class RecordingsService extends BaseService { /** * List recordings of a given type across projects - * @param type - Comment|Document|Kanban::Card|Kanban::Step|Message|Question::Answer|Schedule::Entry|Todo|Todolist|Upload|Vault + * @param type - Comment|Document|Door|Kanban::Card|Kanban::Step|Message|Question::Answer|Schedule::Entry|Todo|Todolist|Upload|Vault * @param options - Optional query parameters * @returns All Recording across all pages, with .meta.totalCount * @@ -54,7 +54,7 @@ export class RecordingsService extends BaseService { * const filtered = await client.recordings.list("type", { bucket: [123] }); * ``` */ - async list(type: "Comment" | "Document" | "Kanban::Card" | "Kanban::Step" | "Message" | "Question::Answer" | "Schedule::Entry" | "Todo" | "Todolist" | "Upload" | "Vault", options?: ListRecordingOptions): Promise> { + async list(type: "Comment" | "Document" | "Door" | "Kanban::Card" | "Kanban::Step" | "Message" | "Question::Answer" | "Schedule::Entry" | "Todo" | "Todolist" | "Upload" | "Vault", options?: ListRecordingOptions): Promise> { return this.requestPaginated( { service: "Recordings", diff --git a/typescript/src/index.ts b/typescript/src/index.ts index 78ab02beb..6b07fbcec 100644 --- a/typescript/src/index.ts +++ b/typescript/src/index.ts @@ -347,6 +347,11 @@ export { TimelineService, } from "./generated/services/timeline.js"; +// Everything aggregates service - generated +export { + EverythingService, +} from "./generated/services/everything.js"; + // Search & Reports services - generated export { SearchService, diff --git a/typescript/tests/services/everything.test.ts b/typescript/tests/services/everything.test.ts new file mode 100644 index 000000000..32a6e7752 --- /dev/null +++ b/typescript/tests/services/everything.test.ts @@ -0,0 +1,356 @@ +/** + * Tests for the EverythingService (generated from OpenAPI spec) + */ +import { describe, it, expect, beforeEach } from "vitest"; +import { http, HttpResponse } from "msw"; +import { server } from "../setup.js"; +import { createBasecampClient } from "../../src/client.js"; +import { BasecampError } from "../../src/errors.js"; +import type { BasecampClient } from "../../src/client.js"; + +const BASE_URL = "https://3.basecampapi.com/12345"; + +describe("EverythingService", () => { + let client: BasecampClient; + + beforeEach(() => { + client = createBasecampClient({ + accountId: "12345", + accessToken: "test-token", + enableRetry: false, + }); + }); + + describe("everythingFiles", () => { + it("should decode the heterogeneous /files.json feed: Upload, Document, and Attachment variants in one array", async () => { + const fixture = [ + { + id: 900, + type: "Upload", + status: "active", + visible_to_clients: false, + title: "logo.png", + inherits_status: true, + filename: "logo.png", + content_type: "image/png", + byte_size: 1281, + width: 1024.0, + height: 768.0, + url: "https://3.basecampapi.com/1/buckets/2/uploads/900.json", + app_url: "https://3.basecamp.com/1/buckets/2/uploads/900", + download_url: "https://3.basecampapi.com/1/buckets/2/uploads/900/download/logo.png", + app_download_url: "https://storage.3.basecamp.com/1/buckets/2/uploads/900/download/logo.png", + bucket: { id: 2, name: "The Leto Laptop", type: "Project" }, + creator: { id: 1, name: "Victor Cooper" }, + }, + { + id: 901, + type: "Document", + status: "active", + visible_to_clients: false, + title: "Spec", + inherits_status: true, + content_type: "text/html", + url: "https://3.basecampapi.com/1/buckets/2/documents/901.json", + app_url: "https://3.basecamp.com/1/buckets/2/documents/901", + bucket: { id: 2, name: "The Leto Laptop", type: "Project" }, + creator: { id: 1, name: "Victor Cooper" }, + }, + { + id: 902, + type: "Attachment", + attachable_sgid: "sgid-902", + filename: "chart.avif", + content_type: "image/avif", + byte_size: 4096, + width: null, + height: null, + download_url: "https://storage.3.basecamp.com/1/blobs/902/download/chart.avif", + parent: { id: 800, title: "A message", type: "Message" }, + }, + ]; + + server.use( + http.get(`${BASE_URL}/files.json`, () => { + return HttpResponse.json(fixture); + }) + ); + + const result = (await client.everything.everythingFiles()) as any[]; + expect(result).toHaveLength(3); + + // variant 1: full Upload recording + expect(result[0].type).toBe("Upload"); + expect(result[0].filename).toBe("logo.png"); + expect(result[0].app_download_url).toBe( + "https://storage.3.basecamp.com/1/buckets/2/uploads/900/download/logo.png" + ); + expect(result[0].width).toBe(1024); + + // variant 2: Basecamp Document recording + expect(result[1].type).toBe("Document"); + expect(result[1].title).toBe("Spec"); + + // variant 3: rich-text Attachment envelope + expect(result[2].type).toBe("Attachment"); + expect(result[2].attachable_sgid).toBe("sgid-902"); + expect(result[2].parent).toBeDefined(); + expect(result[2].width).toBeNull(); + }); + }); + + describe("everythingMessages", () => { + it("should decode the paginated /messages.json Recording feed with embedded buckets", async () => { + const fixture = [ + { + id: 1001, + type: "Message", + title: "Kickoff", + bucket: { id: 2, name: "The Leto Laptop", type: "Project" }, + creator: { id: 1, name: "Victor Cooper" }, + }, + { + id: 1002, + type: "Message", + title: "Status update", + bucket: { id: 3, name: "Honcho Rollout", type: "Project" }, + creator: { id: 1, name: "Victor Cooper" }, + }, + ]; + + server.use( + http.get(`${BASE_URL}/messages.json`, () => { + return HttpResponse.json(fixture); + }) + ); + + const result = (await client.everything.everythingMessages()) as any[]; + expect(result).toHaveLength(2); + expect(result[0].id).toBe(1001); + expect(result[0].bucket).toEqual({ id: 2, name: "The Leto Laptop", type: "Project" }); + expect(result[1].id).toBe(1002); + expect(result[1].bucket.id).toBe(3); + }); + }); + + describe("everythingComments", () => { + it("should decode the /comments.json Recording feed with embedded buckets", async () => { + const fixture = [ + { + id: 2001, + type: "Comment", + bucket: { id: 2, name: "The Leto Laptop", type: "Project" }, + creator: { id: 1, name: "Victor Cooper" }, + }, + { + id: 2002, + type: "Comment", + bucket: { id: 2, name: "The Leto Laptop", type: "Project" }, + creator: { id: 5, name: "Annie Bryan" }, + }, + ]; + + server.use( + http.get(`${BASE_URL}/comments.json`, () => { + return HttpResponse.json(fixture); + }) + ); + + const result = (await client.everything.everythingComments()) as any[]; + expect(result).toHaveLength(2); + expect(result[0].id).toBe(2001); + expect(result[0].bucket).toEqual({ id: 2, name: "The Leto Laptop", type: "Project" }); + expect(result[1].id).toBe(2002); + }); + }); + + describe("everythingCheckins", () => { + it("should decode the /checkins.json Recording feed with embedded buckets", async () => { + const fixture = [ + { + id: 3001, + type: "Question::Answer", + bucket: { id: 2, name: "The Leto Laptop", type: "Project" }, + creator: { id: 1, name: "Victor Cooper" }, + }, + { + id: 3002, + type: "Question::Answer", + bucket: { id: 4, name: "Marketing Site", type: "Project" }, + creator: { id: 5, name: "Annie Bryan" }, + }, + ]; + + server.use( + http.get(`${BASE_URL}/checkins.json`, () => { + return HttpResponse.json(fixture); + }) + ); + + const result = (await client.everything.everythingCheckins()) as any[]; + expect(result).toHaveLength(2); + expect(result[0].id).toBe(3001); + expect(result[0].bucket).toEqual({ id: 2, name: "The Leto Laptop", type: "Project" }); + expect(result[1].bucket.id).toBe(4); + }); + }); + + describe("everythingForwards", () => { + it("should decode the /forwards.json Recording feed with embedded buckets", async () => { + const fixture = [ + { + id: 4001, + type: "Inbox::Forward", + bucket: { id: 2, name: "The Leto Laptop", type: "Project" }, + creator: { id: 1, name: "Victor Cooper" }, + }, + { + id: 4002, + type: "Inbox::Forward", + bucket: { id: 2, name: "The Leto Laptop", type: "Project" }, + creator: { id: 5, name: "Annie Bryan" }, + }, + ]; + + server.use( + http.get(`${BASE_URL}/forwards.json`, () => { + return HttpResponse.json(fixture); + }) + ); + + const result = (await client.everything.everythingForwards()) as any[]; + expect(result).toHaveLength(2); + expect(result[0].id).toBe(4001); + expect(result[0].bucket).toEqual({ id: 2, name: "The Leto Laptop", type: "Project" }); + expect(result[1].id).toBe(4002); + }); + }); + + describe("everythingBoosts", () => { + it("should decode the /boosts.json feed with booster and nested recording", async () => { + const fixture = [ + { + id: 5001, + content: "👏", + created_at: "2024-01-15T10:00:00Z", + booster: { id: 1, name: "Victor Cooper" }, + recording: { id: 800, title: "A message", type: "Message" }, + }, + { + id: 5002, + content: "🔥", + created_at: "2024-01-15T11:00:00Z", + booster: { id: 5, name: "Annie Bryan" }, + recording: { id: 801, title: "A comment", type: "Comment" }, + }, + ]; + + server.use( + http.get(`${BASE_URL}/boosts.json`, () => { + return HttpResponse.json(fixture); + }) + ); + + const result = (await client.everything.everythingBoosts()) as any[]; + expect(result).toHaveLength(2); + expect(result[0].id).toBe(5001); + expect(result[0].booster).toEqual({ id: 1, name: "Victor Cooper" }); + expect(result[0].recording).toEqual({ id: 800, title: "A message", type: "Message" }); + expect(result[1].id).toBe(5002); + expect(result[1].recording.id).toBe(801); + }); + }); + + describe("everythingOverdueTodos", () => { + it("should decode the unpaginated /todos/overdue.json array, oldest-first", async () => { + const fixture = [ + { + id: 6001, + type: "Todo", + title: "Ship the thing", + due_on: "2024-01-10", + bucket: { id: 2, name: "The Leto Laptop", type: "Project" }, + }, + { + id: 6002, + type: "Todo", + title: "Review the docs", + due_on: "2024-01-20", + bucket: { id: 2, name: "The Leto Laptop", type: "Project" }, + }, + ]; + + server.use( + http.get(`${BASE_URL}/todos/overdue.json`, () => { + return HttpResponse.json(fixture); + }) + ); + + const result = (await client.everything.everythingOverdueTodos()) as any[]; + expect(result).toHaveLength(2); + expect(result[0].id).toBe(6001); + expect(result[1].id).toBe(6002); + // oldest-first ordering by due_on + expect(result[0].due_on).toBe("2024-01-10"); + expect(result[1].due_on).toBe("2024-01-20"); + expect(result[0].due_on! < result[1].due_on!).toBe(true); + }); + }); + + describe("everythingOverdueCards", () => { + it("should decode the unpaginated /cards/overdue.json array, oldest-first", async () => { + const fixture = [ + { + id: 7001, + type: "Kanban::Card", + title: "Draft proposal", + due_on: "2024-02-01", + bucket: { id: 2, name: "The Leto Laptop", type: "Project" }, + }, + { + id: 7002, + type: "Kanban::Card", + title: "Send invoice", + due_on: "2024-02-14", + bucket: { id: 2, name: "The Leto Laptop", type: "Project" }, + }, + ]; + + server.use( + http.get(`${BASE_URL}/cards/overdue.json`, () => { + return HttpResponse.json(fixture); + }) + ); + + const result = (await client.everything.everythingOverdueCards()) as any[]; + expect(result).toHaveLength(2); + expect(result[0].id).toBe(7001); + expect(result[1].id).toBe(7002); + // oldest-first ordering by due_on + expect(result[0].due_on).toBe("2024-02-01"); + expect(result[1].due_on).toBe("2024-02-14"); + expect(result[0].due_on! < result[1].due_on!).toBe(true); + }); + }); + + describe("error propagation", () => { + const cases: Array<[string, string, () => Promise]> = [ + ["everythingMessages", "/messages.json", () => client.everything.everythingMessages()], + ["everythingComments", "/comments.json", () => client.everything.everythingComments()], + ["everythingCheckins", "/checkins.json", () => client.everything.everythingCheckins()], + ["everythingForwards", "/forwards.json", () => client.everything.everythingForwards()], + ["everythingBoosts", "/boosts.json", () => client.everything.everythingBoosts()], + ["everythingFiles", "/files.json", () => client.everything.everythingFiles()], + ["everythingOverdueTodos", "/todos/overdue.json", () => client.everything.everythingOverdueTodos()], + ["everythingOverdueCards", "/cards/overdue.json", () => client.everything.everythingOverdueCards()], + ]; + + it.each(cases)("%s propagates a 4xx as a BasecampError", async (_name, path, call) => { + server.use( + http.get(`${BASE_URL}${path}`, () => HttpResponse.json({ error: "Not found" }, { status: 404 })) + ); + + await expect(call()).rejects.toThrow(BasecampError); + }); + }); +});