Skip to content

Commit 998e8f3

Browse files
committed
fix(coda): retry field-setting PATCH updates on rate limits and server errors
1 parent 2a0463a commit 998e8f3

6 files changed

Lines changed: 22 additions & 11 deletions

File tree

‎apps/docs/content/docs/integrations/coda.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ This integration uses a reusable Coda **API token** connection, not OAuth. In Co
2727
- **Pagination:** List operations return a `nextPageToken`. Pass it as **Page Token** to get the next page. When a Page Token is set, the original filters and limit are reused automatically. Coda can change page sizes at any time, so keep paging until no token comes back.
2828
- **Page content:** **Get Page Content** reads a page as plain-text lines, with element IDs you can target in **Update Page** or **Delete Page Content**. To delete content, enter element IDs, or turn on **Delete All Page Content** to clear the whole page. For the full page as Markdown or HTML, run **Export Page**, then call **Get Page Export Status** until `downloadLink` is present. The link expires shortly after it is issued.
2929
- **Permissions and plans:** Creating docs and pages or renaming a doc requires Doc Maker access in the workspace. Publishing needs a Coda maker profile. Hiding pages and custom domains need a paid Coda plan. Workspace members, role activity, and role changes need a workspace that belongs to an organization, and role changes need Admin access. Page analytics are only available for docs in Enterprise workspaces.
30-
- **Rate limits:** Coda limits requests per user, with tighter limits on writes and on listing docs. Sim automatically retries reads, updates, and deletes when Coda returns HTTP 429. Inserts and creates are not retried, so space out bulk writes.
30+
- **Rate limits:** Coda limits requests per user, with tighter limits on writes and on listing docs. Sim automatically retries reads, updates, and deletes when Coda returns HTTP 429 or a transient server error. Inserts and creates are not retried, so space out bulk writes.
3131
{/* MANUAL-CONTENT-END */}
3232

3333

‎apps/sim/tools/coda/coda.test.ts‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { codaUpdateAclSettingsTool } from '@/tools/coda/update_acl_settings'
1616
import { codaUpdatePageTool } from '@/tools/coda/update_page'
1717
import { codaUpdateRowTool } from '@/tools/coda/update_row'
1818
import { codaUpsertRowsTool } from '@/tools/coda/upsert_rows'
19-
import { buildCodaUrl, CODA_RETRY } from '@/tools/coda/utils'
19+
import { buildCodaUrl, CODA_FIELD_UPDATE_RETRY, CODA_RETRY } from '@/tools/coda/utils'
2020
import { codaWhoamiTool } from '@/tools/coda/whoami'
2121
import { ErrorExtractorId, extractErrorMessageWithId } from '@/tools/error-extractors'
2222

@@ -340,14 +340,16 @@ describe('Coda tool registration', () => {
340340
})
341341

342342
it.each(allTools)(
343-
'%s retries idempotent calls and authenticates with the Coda credential',
343+
'%s retries safely repeatable calls and authenticates with the Coda credential',
344344
(_, tool) => {
345345
const config = tool as {
346-
request: { retry?: unknown }
346+
request: { method: unknown; retry?: unknown }
347347
oauth?: unknown
348348
params: Record<string, unknown>
349349
}
350-
expect(config.request.retry).toBe(CODA_RETRY)
350+
expect(config.request.retry).toBe(
351+
config.request.method === 'PATCH' ? CODA_FIELD_UPDATE_RETRY : CODA_RETRY
352+
)
351353
expect(config.oauth).toEqual({ required: true, provider: 'coda' })
352354
expect(config.params.accessToken).toMatchObject({ required: true, visibility: 'hidden' })
353355
}

‎apps/sim/tools/coda/update_acl_settings.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { CodaAclSettingsParams, CodaAclSettingsResponse } from '@/tools/cod
22
import {
33
ACL_SETTINGS_OUTPUTS,
44
buildCodaUrl,
5-
CODA_RETRY,
5+
CODA_FIELD_UPDATE_RETRY,
66
codaAuthParams,
77
codaDocPath,
88
codaHeaders,
@@ -54,7 +54,7 @@ export const codaUpdateAclSettingsTool: ToolConfig<CodaAclSettingsParams, CodaAc
5454
request: {
5555
url: (params) => buildCodaUrl(codaDocPath(params.docId, 'acl', 'settings')),
5656
method: 'PATCH',
57-
retry: CODA_RETRY,
57+
retry: CODA_FIELD_UPDATE_RETRY,
5858
headers: (params) => codaHeaders(params.accessToken, true),
5959
body: (params) => {
6060
const body: Record<string, boolean> = {}

‎apps/sim/tools/coda/update_doc.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { CodaDocIdResponse, CodaUpdateDocParams } from '@/tools/coda/types'
22
import {
33
buildCodaUrl,
4-
CODA_RETRY,
4+
CODA_FIELD_UPDATE_RETRY,
55
codaAuthParams,
66
codaDocPath,
77
codaHeaders,
@@ -41,7 +41,7 @@ export const codaUpdateDocTool: ToolConfig<CodaUpdateDocParams, CodaDocIdRespons
4141
request: {
4242
url: (params) => buildCodaUrl(codaDocPath(params.docId)),
4343
method: 'PATCH',
44-
retry: CODA_RETRY,
44+
retry: CODA_FIELD_UPDATE_RETRY,
4545
headers: (params) => codaHeaders(params.accessToken, true),
4646
body: (params) => {
4747
const title = optionalTrimmed(params.title)

‎apps/sim/tools/coda/update_folder.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { CodaFolderResponse, CodaUpdateFolderParams } from '@/tools/coda/types'
22
import {
33
buildCodaUrl,
4-
CODA_RETRY,
4+
CODA_FIELD_UPDATE_RETRY,
55
codaAuthParams,
66
codaHeaders,
77
codaOAuth,
@@ -44,7 +44,7 @@ export const codaUpdateFolderTool: ToolConfig<CodaUpdateFolderParams, CodaFolder
4444
request: {
4545
url: (params) => buildCodaUrl(codaPath('folders', [params.folderId, 'folderId'])),
4646
method: 'PATCH',
47-
retry: CODA_RETRY,
47+
retry: CODA_FIELD_UPDATE_RETRY,
4848
headers: (params) => codaHeaders(params.accessToken, true),
4949
body: (params) => {
5050
const name = optionalTrimmed(params.name)

‎apps/sim/tools/coda/utils.ts‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,15 @@ export const CODA_RETRY = {
213213
retryIdempotentOnly: true,
214214
} as const satisfies ToolRetryConfig
215215

216+
/**
217+
* Coda's PATCH endpoints set the supplied fields to fixed values, so repeating one is safe even
218+
* though the executor does not treat PATCH as idempotent.
219+
*/
220+
export const CODA_FIELD_UPDATE_RETRY = {
221+
...CODA_RETRY,
222+
retryIdempotentOnly: false,
223+
} as const satisfies ToolRetryConfig
224+
216225
interface RawReference {
217226
id?: string
218227
name?: string

0 commit comments

Comments
 (0)