feat(google-appsheet): add Google AppSheet integration#5376
Conversation
|
@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. |
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 |
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
|
@greptile review |
|
@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 parsing
d3c5e10 to
5a46dab
Compare
|
@greptile review |
|
@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.
|
@greptile review |
|
@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