Skip to content

Caspio - new components#19724

Merged
michelle0927 merged 4 commits intomasterfrom
issue-13397
Jan 26, 2026
Merged

Caspio - new components#19724
michelle0927 merged 4 commits intomasterfrom
issue-13397

Conversation

@michelle0927
Copy link
Collaborator

@michelle0927 michelle0927 commented Jan 16, 2026

Resolves #13397

Summary by CodeRabbit

  • New Features
    • Added ability to create records in Caspio tables with automatic field mapping
    • Added instant webhook triggers for Caspio record events: new records created, records updated, and records deleted
    • Enhanced Caspio integration with improved authentication and API support

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link

vercel bot commented Jan 16, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Review Updated (UTC)
pipedream-docs-redirect-do-not-edit Ignored Ignored Jan 23, 2026 4:07pm

Request Review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 16, 2026

Walkthrough

A new Caspio integration component is introduced with authenticated API helpers, a create-record action that dynamically maps table fields to input properties, and three webhook sources (new/updated/deleted records) that share a common base class for webhook lifecycle management and event emission.

Changes

Cohort / File(s) Summary
Core App & Dependencies
components/caspio/caspio.app.mjs, components/caspio/package.json
Adds propDefinition for table selection, HTTP helpers (_baseUrl, _makeRequest), and API methods (listTables, listTableFields, createRecord, createWebhook, createWebhookEvent, deleteWebhook). Package bumped to v0.1.0 with new @pipedream/platform dependency.
Create Record Action
components/caspio/actions/create-record/create-record.mjs
New action that dynamically builds input fields from table columns, validates non-empty data, casts numeric fields to numbers, and calls createRecord API with Caspio field-to-prop-type mapping (YES/NO → boolean).
Webhook Base Class
components/caspio/sources/common/base-webhook.mjs
Reusable webhook source foundation with lifecycle hooks (activate creates webhook and event, deactivate deletes webhook), persistent webhookId storage/retrieval, and abstract methods (getEventType, generateMeta) for subclass implementation.
New Record Webhook Source
components/caspio/sources/new-record-created/new-record-created.mjs, components/caspio/sources/new-record-created/test-event.mjs
Webhook source for table.recordInsert events that extends base class, extracts PK_ID and eventId for meta, includes sample payload.
Updated Record Webhook Source
components/caspio/sources/record-updated/record-updated.mjs, components/caspio/sources/record-updated/test-event.mjs
Webhook source for table.recordUpdate events extending base class with meta generation from eventId and PK_ID, includes sample payload.
Deleted Record Webhook Source
components/caspio/sources/record-deleted/record-deleted.mjs, components/caspio/sources/record-deleted/test-event.mjs
Webhook source for table.recordDelete events extending base class, constructs meta from eventId and deleted record's PK_ID, includes sample payload.

Sequence Diagrams

sequenceDiagram
    participant Action as Create Record<br/>Action
    participant App as Caspio App
    participant API as Caspio API

    Action->>App: listTableFields({ table })
    App->>API: GET /v2/tables/{table}/fields
    API-->>App: field definitions
    App-->>Action: fields with types
    
    Action->>Action: Cast numeric fields,<br/>validate input data
    
    Action->>App: createRecord({ table, ...data })
    App->>API: POST /v2/tables/{table}/records
    API-->>App: record created
    App-->>Action: { ID, ... }
Loading
sequenceDiagram
    participant Source as Webhook Source
    participant App as Caspio App
    participant API as Caspio API
    participant DB as Database

    Source->>App: createWebhook()
    App->>API: POST /v2/webhooks
    API-->>App: { id: webhookId }
    
    Source->>DB: _setWebhookId(webhookId)
    DB-->>Source: stored
    
    Source->>App: createWebhookEvent({ webhookId,<br/>eventType, objectName })
    App->>API: POST /v2/webhooks/{webhookId}/events
    API-->>App: event created

    Note over Source: Active - waiting for events

    Source->>Source: Deactivate
    Source->>DB: _getWebhookId()
    DB-->>Source: webhookId
    Source->>App: deleteWebhook(webhookId)
    App->>API: DELETE /v2/webhooks/{webhookId}
    API-->>App: deleted
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • jcortes
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: introducing new Caspio components (webhook sources and action) as specified in the PR objectives.
Description check ✅ Passed The PR description is minimal but appropriately references the linked issue #13397. The template only requires a WHY section, which is not explicitly provided, but the reference to the issue suffices.
Linked Issues check ✅ Passed All requirements from issue #13397 are implemented: three webhook sources (new-record-created, record-updated, record-deleted) and one action (create-record) with proper table selection and field handling.
Out of Scope Changes check ✅ Passed All changes are within scope: new Caspio action and webhook sources as specified in #13397, plus necessary app integration updates and dependency additions.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Fix all issues with AI agents
In `@components/caspio/actions/create-record/create-record.mjs`:
- Around line 49-60: The current empty-input guard and numeric conversion miss
values like 0/"0"; update the validation to ensure at least one provided field
has a non-null/ non-undefined/ non-empty-string value (e.g. replace
Object.keys(data).length check with something like Object.values(data).some(v =>
v !== undefined && v !== null && v !== '')), and change the number conversion in
the loop (inside the code using this.caspio.listTableFields and iterating over
fields) to check existence with hasOwnProperty/explicit null checks (e.g. if
(field.Type === "NUMBER" && data.hasOwnProperty(field.Name) && data[field.Name]
!== undefined && data[field.Name] !== null && data[field.Name] !== '') then set
data[field.Name] = Number(data[field.Name]) so numeric 0/"0" are converted
correctly; keep the thrown ConfigurationError when no valid values are found.

In `@components/caspio/caspio.app.mjs`:
- Around line 38-53: The path construction in listTableFields and createRecord
interpolates the table variable directly into the URL (paths
`/tables/${table}/fields` and `/tables/${table}/records`), which will break for
names with spaces or special characters; update both functions to percent-encode
the table path segment (use encodeURIComponent or equivalent) when building the
path so the Caspio REST API receives a properly encoded path parameter and
requests succeed for any table name.
- Around line 7-15: The options() function for the table property is returning
raw table objects causing the selected value to be an object; change the mapping
in the async options() (the method that calls this.listTables()) to return an
array of { label, value } strings where label uses a human-friendly field (e.g.,
Title or Name) and value is the table's string identifier (e.g., Name), so
downstream uses like `/tables/${table}/fields` and webhook ObjectName receive a
string; update the mapping inside the table config's options() to return
tables?.map(t => ({ label: t.Title || t.Name, value: t.Name })) || [].

In `@components/caspio/sources/common/base-webhook.mjs`:
- Around line 25-42: The activate() flow currently calls
this._setWebhookId(webhookId) immediately after caspio.createWebhook, which
leaves an orphaned webhook if caspio.createWebhookEvent fails; change the logic
so you only persist the webhook ID after both caspio.createWebhook and
caspio.createWebhookEvent succeed (i.e., move the this._setWebhookId(webhookId)
call to after the createWebhookEvent call), and if createWebhookEvent throws,
catch the error and call caspio.deleteWebhook (or the appropriate deletion
method) with the created webhookId to clean up before rethrowing the error;
update activate() to use try/catch around caspio.createWebhookEvent and
reference createWebhook, createWebhookEvent, _setWebhookId, and
caspio.deleteWebhook in the fix.
- Around line 44-50: The deactivate method currently deletes the remote webhook
but leaves the stored webhook id set, causing stale state and repeated deletes;
after successfully awaiting this.caspio.deleteWebhook(webhookId) in
deactivate(), clear the stored id returned by this._getWebhookId() (e.g., set
the backing property or storage key the class uses to store the webhook id to
null/undefined and persist that change if necessary) so subsequent calls to
deactivate() or other methods using _getWebhookId() see no webhook id.
- Around line 66-76: The run method currently responds 200 before any
authentication; instead, first validate the incoming body.secret against the
configured webhook secret using a constant-time comparison (e.g., Node's
crypto.timingSafeEqual): if the configured secret is missing or the secrets
don't match, respond with an unauthorized status (401) and return without
calling this.$emit or generateMeta; only after successful validation call
this.http.respond({ status: 200 }) and then proceed to call
this.generateMeta(body) and this.$emit(body, meta). Ensure you reference the run
function and use a secure constant-time comparison rather than a simple ===
string comparison.

In `@components/caspio/sources/record-updated/record-updated.mjs`:
- Around line 17-22: The generateMeta function assumes body.data[0] exists and
will throw if data is missing or empty; update generateMeta to safely access
PK_ID and eventDate using optional chaining and fallbacks (e.g., use
body.data?.[0]?.PK_ID with a default like 'unknown' for the summary and guard ts
by falling back to a safe value when Date.parse(body.eventDate) returns NaN) so
the function returns a valid meta object even when body.data is absent.
♻️ Duplicate comments (2)
components/caspio/sources/record-deleted/record-deleted.mjs (1)

17-22: Same body.data[0] guard as record-updated.

Line 20 has the same assumption about body.data[0]; please apply the same optional chaining/fallback here.

components/caspio/sources/new-record-created/new-record-created.mjs (1)

17-22: Same body.data[0] guard as record-updated.

Line 20 has the same assumption about body.data[0]; please apply the same optional chaining/fallback here.

lcaresia
lcaresia previously approved these changes Jan 22, 2026
Copy link
Collaborator

@lcaresia lcaresia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@vunguyenhung
Copy link
Contributor

For Integration QA:

@vunguyenhung
Copy link
Contributor

Hello everyone, I have tested this PR and there're some test cases failed or needed improvement.

Please check test reports below for more information:

@vunguyenhung
Copy link
Contributor

Hi everyone, all test cases are passed! Ready for release!

Test reports

@michelle0927
Copy link
Collaborator Author

/approve

Copy link
Collaborator

@lcaresia lcaresia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@michelle0927 michelle0927 merged commit fc61120 into master Jan 26, 2026
8 checks passed
@michelle0927 michelle0927 deleted the issue-13397 branch January 26, 2026 17:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Components] caspio

3 participants