Files
sim/apps/sim/tools/google_appsheet/utils.ts
T
Waleed 10b6bb33e9 feat(google-appsheet): add Google AppSheet integration (#5376)
* feat(google-appsheet): add Google AppSheet integration

- 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

* improvement(google-appsheet): harden response parsing, add wand config 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

* fix(google-appsheet): validate region against allow-list, encode appId, 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

* docs(google-appsheet): add manual intro/getting-started section

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.

* docs: sync generated integration docs with current source

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.
2026-07-02 11:58:30 -07:00

37 lines
1.5 KiB
TypeScript

const DEFAULT_REGION = 'www'
const ALLOWED_REGIONS = new Set(['www', 'eu', 'asia-southeast'])
/**
* Builds the AppSheet API Action endpoint URL for a given app/table/region.
* Region defaults to the global `www.appsheet.com` domain when unset, and is
* validated against the known AppSheet regions since it is interpolated into
* the request host — an unvalidated value would let a caller redirect the
* Application Access Key to an arbitrary host.
*/
export function buildAppsheetActionUrl(appId: string, tableName: string, region?: string): string {
const trimmedRegion = (region || DEFAULT_REGION).trim()
if (!ALLOWED_REGIONS.has(trimmedRegion)) {
throw new Error(
`Invalid AppSheet region "${trimmedRegion}". Must be one of: ${Array.from(ALLOWED_REGIONS).join(', ')}.`
)
}
const host = `${trimmedRegion}.appsheet.com`
return `https://${host}/api/v2/apps/${encodeURIComponent(appId.trim())}/tables/${encodeURIComponent(tableName.trim())}/Action`
}
/**
* Safely reads an AppSheet API response body. AppSheet does not consistently
* document whether every Action returns a JSON body (e.g. Delete may return an
* empty body on some accounts), so this avoids `response.json()` throwing on
* empty or non-JSON content.
*/
export async function readAppsheetResponseBody(response: Response): Promise<Record<string, any>> {
const text = await response.text()
if (!text) return {}
try {
return JSON.parse(text)
} catch {
return { message: text }
}
}