Uh oh!
There was an error while loading. Please reload this page.
feat(google-appsheet): add Google AppSheet integration - #5376
Conversation
waleedlatif1
commented
Jul 2, 2026
waleedlatif1
commented
Jul 2, 2026
@cursor review |
The latest updates on your projects. Learn more about Vercel for GitHub.
|
PR SummaryLow Risk Overview The same change set regenerates or expands many other integration docs so published action/trigger tables match the current blocks— notably new or updated surfaces for Ahrefs ( Reviewed by Cursor Bugbot for commit 3d8b8ee. Configure here. |
Uh oh!
There was an error while loading. Please reload this page.
Greptile SummaryThis PR adds a Google AppSheet integration with four operations — Find, Add, Edit, and Delete Rows — backed by the AppSheet Action API using a static Application Access Key. The implementation is well-structured and security issues raised in prior review rounds (region SSRF, appId URL-encoding, non-JSON response bodies) have all been addressed.
Confidence Score: 5/5Safe to merge — this is a self-contained new integration with no changes to existing tool paths. All issues flagged in the prior review rounds have been resolved: region is now validated against an allow-list, both path segments are URL-encoded, and response bodies are read safely as text before JSON parsing. The remaining comment is a minor defensive-validation gap (empty-array rows reaching the AppSheet API) that does not cause data loss or a crash. No files require special attention beyond the optional empty-rows guard in apps/sim/blocks/blocks/google_appsheet.ts. Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant UI as Sim UI (Block)
participant BP as Block params()
participant Tool as ToolConfig
participant Utils as utils.ts
participant API as AppSheet Action API
UI->>BP: "{ operation, appId, tableName, region, rows/selector, apiKey }"
BP->>BP: JSON.parse(rows) + Array.isArray check
BP-->>Tool: resolved params
Tool->>Utils: buildAppsheetActionUrl(appId, tableName, region)
Utils->>Utils: validate region against allow-list
Utils->>Utils: encodeURIComponent(appId) + encodeURIComponent(tableName)
Utils-->>Tool: "https://{region}.appsheet.com/api/v2/apps/{appId}/tables/{tableName}/Action"
Tool->>API: "POST with ApplicationAccessKey header + { Action, Properties, Rows }"
API-->>Tool: HTTP response
Tool->>Utils: readAppsheetResponseBody(response)
Utils->>Utils: "response.text() → JSON.parse (or fallback to { message })"
Utils-->>Tool: parsed body
Tool->>Tool: check response.ok, extract data.Rows ?? data.rows ?? []
Tool-->>UI: "{ success, output: { rows, metadata: { rowCount } } }"
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant UI as Sim UI (Block)
participant BP as Block params()
participant Tool as ToolConfig
participant Utils as utils.ts
participant API as AppSheet Action API
UI->>BP: "{ operation, appId, tableName, region, rows/selector, apiKey }"
BP->>BP: JSON.parse(rows) + Array.isArray check
BP-->>Tool: resolved params
Tool->>Utils: buildAppsheetActionUrl(appId, tableName, region)
Utils->>Utils: validate region against allow-list
Utils->>Utils: encodeURIComponent(appId) + encodeURIComponent(tableName)
Utils-->>Tool: "https://{region}.appsheet.com/api/v2/apps/{appId}/tables/{tableName}/Action"
Tool->>API: "POST with ApplicationAccessKey header + { Action, Properties, Rows }"
API-->>Tool: HTTP response
Tool->>Utils: readAppsheetResponseBody(response)
Utils->>Utils: "response.text() → JSON.parse (or fallback to { message })"
Utils-->>Tool: parsed body
Tool->>Tool: check response.ok, extract data.Rows ?? data.rows ?? []
Tool-->>UI: "{ success, output: { rows, metadata: { rowCount } } }"
Reviews (5): Last reviewed commit: "docs: sync generated integration docs wi..." | Re-trigger Greptile |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Greptile SummaryThis PR adds a Google AppSheet integration with four operations (Find, Add, Edit, Delete rows) against the AppSheet Action API, using a static
Confidence Score: 3/5The URL-builder injects a user-supplied region string directly into the hostname without allowlisting, which can redirect requests carrying the API key to an arbitrary host. The unvalidated
|
| Filename | Overview |
|---|---|
| apps/sim/tools/google_appsheet/utils.ts | URL builder injects the region parameter into the hostname without validating it against the three allowed values, enabling SSRF/credential-leakage; also appId is not URL-encoded unlike tableName. |
| apps/sim/tools/google_appsheet/find_rows.ts | Calls response.json() before checking response.ok, causing a confusing SyntaxError when the API returns a non-JSON error body; same pattern repeated across all four tool files. |
| apps/sim/tools/google_appsheet/add_rows.ts | Correct Add action body and header construction; shares the response.json() before response.ok issue with the other tool files. |
| apps/sim/tools/google_appsheet/edit_rows.ts | Correct Edit action body; shares the response.json() before response.ok issue. |
| apps/sim/tools/google_appsheet/delete_rows.ts | Correct Delete action body; shares the response.json() before response.ok issue. |
| apps/sim/blocks/blocks/google_appsheet.ts | Block config is well-structured with conditional subblocks and shared rows field; wandConfig.generationType is set to 'json-object' but the expected output is a JSON array. |
| apps/sim/tools/google_appsheet/types.ts | Clean type definitions with a shared base params interface and discriminated union response type; no issues. |
| apps/sim/tools/registry.ts | Four new tools correctly registered in alphabetical order alongside existing Google tools. |
| apps/sim/blocks/registry-maps.ts | Block and meta correctly registered in both BLOCK_REGISTRY and BLOCK_META_REGISTRY. |
| apps/sim/lib/integrations/integrations.json | Integration entry correctly added with all required fields including operations, authType, and tags. |
Sequence Diagram
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant User
participant Block as GoogleAppsheetBlock
participant Tool as Tool (find/add/edit/delete)
participant Utils as buildAppsheetActionUrl
participant API as AppSheet API
User->>Block: Select operation + provide params
Block->>Tool: Dispatch to selected tool with params
Tool->>Utils: buildAppsheetActionUrl(appId, tableName, region)
Utils-->>Tool: "https://{region}.appsheet.com/api/v2/apps/{appId}/tables/{tableName}/Action"
Tool->>API: POST with ApplicationAccessKey header + Action body
API-->>Tool: JSON response with Rows[]
Tool->>Tool: transformResponse() — parse Rows, build metadata
Tool-->>Block: "{ rows, metadata: { rowCount } }"
Block-->>User: Output rows + metadata
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant User
participant Block as GoogleAppsheetBlock
participant Tool as Tool (find/add/edit/delete)
participant Utils as buildAppsheetActionUrl
participant API as AppSheet API
User->>Block: Select operation + provide params
Block->>Tool: Dispatch to selected tool with params
Tool->>Utils: buildAppsheetActionUrl(appId, tableName, region)
Utils-->>Tool: "https://{region}.appsheet.com/api/v2/apps/{appId}/tables/{tableName}/Action"
Tool->>API: POST with ApplicationAccessKey header + Action body
API-->>Tool: JSON response with Rows[]
Tool->>Tool: transformResponse() — parse Rows, build metadata
Tool-->>Block: "{ rows, metadata: { rowCount } }"
Block-->>User: Output rows + metadata
Reviews (2): Last reviewed commit: "feat(google-appsheet): add Google AppShe..." | Re-trigger Greptile
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
waleedlatif1
commented
Jul 2, 2026
@greptile review |
waleedlatif1
commented
Jul 2, 2026
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit d3c5e10. Configure here.
- 4 tools (find/add/edit/delete rows) against the AppSheet Action API - API key auth via Application Access Key (no OAuth/scopes needed) - Block with operation dropdown, region selector, and Selector expression support - Generated docs
…g and skills - Guard against empty/non-JSON AppSheet response bodies (Delete may return no body) - Add wandConfig to the Selector field for AI-assisted expression generation - Add 3 skills grounded in attested AppSheet/Zapier automation patterns - Tighten json output descriptions to describe inner shape
…d, validate rows shape
- Reject unrecognized region values instead of interpolating them into the
request host (a caller could otherwise redirect the Application Access
Key to an arbitrary domain)
- URL-encode appId, not just tableName, in the Action endpoint path
- Reject non-array Rows input in tools.config.params instead of forwarding
a single object to the AppSheet Action API
- Drop the mismatched json-object generationType on the rows wand config
(that enricher appends "must start with { and end with }", which
conflicts with the JSON-array shape the field expects)
- Add utils.test.ts covering region validation and response-body parsingd3c5e10 to
5a46dabComparewaleedlatif1
commented
Jul 2, 2026
@greptile review |
waleedlatif1
commented
Jul 2, 2026
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 5a46dab. Configure here.
Match the MANUAL-CONTENT convention used by other integration docs (Airtable, Ahrefs, Google PageSpeed) — an overview of the service, what the Sim integration lets agents do, and how to get an Application Access Key.
Regenerate docs for integrations whose tools/blocks changed upstream without a matching docs regen (ahrefs, algolia, amplitude, brex, clerk, gong, hex, langsmith, loops, onepassword, sendgrid, sharepoint, similarweb, supabase, tailscale, trello, vercel, wordpress), plus the integrations.json catalog.
waleedlatif1
commented
Jul 2, 2026
@greptile review |
waleedlatif1
commented
Jul 2, 2026
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 3d8b8ee. Configure here.
Summary
ApplicationAccessKey), not OAuth — AppSheet has no OAuth scopes for this APIType of Change
Testing
tsc --noEmitcleanbiome checkcleanblocks/blocks.test.ts(83 tests), serializer + subblock-visibility + params-resolver suites (155 tests) all passcheck-bare-iconsclean (multi-color brand logo, no theme-safety issue)Checklist