fix(grain): update to stable version of API#3556
Conversation
PR SummaryMedium Risk Overview Adds a pending webhook verification flow for providers that probe URL reachability before DB rows exist. Deployment registers short-lived “pending verification” entries (Redis-backed with in-memory fallback) and the webhook trigger route returns a temporary 200 for matching GET/HEAD/empty-POST probes on those paths; generic webhooks can opt in via a new Written by Cursor Bugbot for commit cac054a. Configure here. |
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Greptile SummaryThis PR migrates the Grain integration from an unstable versioned API ( Key changes:
Confidence Score: 4/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant User as User / UI
participant SimApp as Sim App (deploy.ts)
participant PVStore as PendingVerification Store (Redis / in-memory)
participant GrainAPI as Grain API
participant RouteHandler as Webhook Route Handler
User->>SimApp: Deploy workflow with Grain trigger (viewId)
SimApp->>PVStore: register(path, provider='grain', TTL=120s)
SimApp->>GrainAPI: POST /_/public-api/hooks {version:2, hook_url, view_id, actions?}
GrainAPI->>RouteHandler: GET /api/webhooks/trigger/{path} (verification probe)
RouteHandler->>PVStore: getPendingWebhookVerification(path)
PVStore-->>RouteHandler: entry (not expired)
RouteHandler->>RouteHandler: matchesPendingWebhookVerificationProbe(entry, {method:'GET'})
RouteHandler-->>GrainAPI: 200 OK { status: 'ok', message: 'Webhook endpoint verified' }
GrainAPI-->>SimApp: { id: 'webhook-uuid', hook_url, view_id, ... }
SimApp->>SimApp: Validate responseBody.id exists
SimApp->>SimApp: db.transaction — insert webhook record (externalId = id)
SimApp->>PVStore: clearAll()
SimApp-->>User: Deploy success
|
|
bugbot run |
|
@greptile |
|
bugbot run |
|
@greptile |
| return { | ||
| success: true, | ||
| output: { | ||
| views: data.views || data || [], |
There was a problem hiding this comment.
views may silently receive a non-array value
data.views || data || [] looks safe on the happy path, but if the Grain stable API ever returns a plain array (no wrapping object), data.views is undefined (falsy) and data — an array — is returned directly. That part is fine. However, if data is a truthy non-array object (e.g., { message: "ok" } from an unexpected 200 body), it gets assigned to views, violating the GrainView[] contract at runtime without any error being thrown.
Prefer an explicit check to guard against this:
| views: data.views || data || [], | |
| views: Array.isArray(data.views) ? data.views : Array.isArray(data) ? data : [], |
| } | ||
| }) | ||
|
|
||
| await pendingVerificationTracker.clearAll() |
There was a problem hiding this comment.
clearAll() called before configurePollingIfNeeded return paths
pendingVerificationTracker.clearAll() is called at line 680 (inside the try block, after the DB transaction but before the polling loop). If configurePollingIfNeeded triggers the early-return path (return { success: false, error: pollingError }), the function exits without a finally-guarded cleanup — but since clearAll() already ran at this point, the pending entries are correctly cleared.
The concern is the catch branch lower in the function also calls clearAll() as a separate statement. If a new early-return path is ever introduced between line 680 and the end of the try block (e.g., a new pollingError cleanup that throws instead of returning), the double-call is harmless but the absence of a finally block means the intent isn't self-documenting. Consider wrapping the tracker cleanup in a finally block for robustness:
try {
await db.transaction(...)
for (const sub of createdSubscriptions) { ... }
} catch (error: any) {
// cleanup
} finally {
await pendingVerificationTracker.clearAll()
}Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Summary
Update to stable version of grain api as in (https://grainhq.notion.site/Grain-Personal-API-877184aa82b54c77a875083c1b560de9)
Type of Change
Testing
Tested manually
Checklist