-
Notifications
You must be signed in to change notification settings - Fork 5.5k
[Components] Xola - new components #18788
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
The latest updates on your projects. Learn more about Vercel for GitHub. 2 Skipped Deployments
|
WalkthroughAdds comprehensive Xola integration with new actions for event and schedule management, polling sources tracking experience/purchase lifecycle changes, instant webhook sources for real-time events, base infrastructure for sources, API client methods, and configuration constants. Changes
Sequence DiagramsequenceDiagram
participant User
participant Action/Source
participant App
participant API[Xola API]
rect rgb(230, 245, 255)
note over Action/Source,API: Action Flow
User->>Action/Source: Trigger action
Action/Source->>App: Call action method (e.g., addEventGuide)
App->>App: Build URL via getUrl()
App->>App: Assemble headers via getHeaders()
App->>API: POST/PATCH/DELETE via makeRequest()
API-->>App: Response
App-->>Action/Source: API response
Action/Source->>Action/Source: Export summary
Action/Source-->>User: Return result
end
rect rgb(240, 250, 240)
note over Action/Source,API: Polling Source Flow
Action/Source->>Action/Source: Timer triggered
Action/Source->>App: Call listExperiences()
App->>API: GET with limit/skip params
API-->>App: Paginated results
App-->>Action/Source: Experience list
Action/Source->>Action/Source: Read lastCreatedAt from db
Action/Source->>Action/Source: Filter by createdAt > lastCreatedAt
Action/Source->>Action/Source: For each new: generateMeta(), emit event
Action/Source->>Action/Source: Store updated lastCreatedAt in db
Action/Source-->>User: Emitted events
end
rect rgb(255, 250, 240)
note over Action/Source,API: Webhook Source Flow
API->>Action/Source: POST /webhook event
Action/Source->>Action/Source: Respond 200
Action/Source->>Action/Source: Extract event.data
Action/Source->>Action/Source: generateMeta() from payload
Action/Source->>Action/Source: Emit with body + meta
Action/Source-->>User: Event emitted
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Rationale: While individual files follow repeating patterns (16+ similar source modules with consistent structure), the PR spans 40+ files across diverse concerns (polling/webhook infrastructure, multiple action types, app authentication redesign) with varying complexity. The core app.mjs requires careful review for HTTP abstraction correctness and API method implementations. Source implementations are pattern-consistent but require spot-checking state management logic (lastCreatedAt tracking, deduplication), webhook lifecycle management, and filtering rules. High file count with moderate pattern homogeneity elevates scope; logic density per file remains moderate. Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 9
♻️ Duplicate comments (1)
components/xola/sources/purchase-updated/purchase-updated.mjs (1)
17-21
: Critical: Pagination not implemented — updates beyond first 100 purchases will be missed.Identical issue to
experience-updated.mjs
: the hardcodedskip: 0
limits monitoring to only the first 100 purchases, causing updates beyond that threshold to be missed.Apply the same pagination solution as suggested for
experience-updated.mjs
.
🧹 Nitpick comments (6)
components/xola/sources/order-updated-instant/order-updated-instant.mjs (1)
4-27
: LGTM with a minor optimization suggestion.The source implementation correctly extends the common webhook base and implements the required methods. The event name matches the test event payload, and the metadata generation follows the established pattern.
Optional micro-optimization:
Date.now()
is called twice (lines 20 and 22). Consider storing the timestamp in a variable:generateMeta(body) { const { data } = body; + const now = Date.now(); return { - id: `${data.id}-${Date.now()}`, + id: `${data.id}-${now}`, summary: `Order Updated: ${data.id}`, - ts: Date.now(), + ts: now, }; },components/xola/sources/experience-deleted-instant/test-event.mjs (1)
1-8
: Consider adding a deletedAt timestamp.While the minimal payload is acceptable, including a
deletedAt
field would make the deletion event more informative and consistent with other timestamped events in the fixture suite.Apply this diff to add the timestamp:
export default { eventName: "experience.delete", data: { id: "507f1f77bcf86cd799439012", name: "City Walking Tour", + deletedAt: "2024-01-17T10:00:00Z", }, audit: {}, };
components/xola/sources/order-deleted-instant/order-deleted-instant.mjs (1)
17-24
: Minor: Consider capturing Date.now() once.The
generateMeta
method callsDate.now()
twice (lines 20 and 22), which could produce slightly different timestamps, though the difference would be negligible in practice.Apply this diff for consistency:
generateMeta(body) { const { data } = body; + const now = Date.now(); return { - id: `${data.id}-deleted-${Date.now()}`, + id: `${data.id}-deleted-${now}`, summary: `Order Deleted: ${data.id}`, - ts: Date.now(), + ts: now, }; },components/xola/actions/create-experience-schedule/create-experience-schedule.mjs (1)
153-168
: Consider conditionally including optional fields in the data payload.Unlike the update action (see
components/xola/actions/update-experience-schedule/update-experience-schedule.mjs
lines 98-120), this action passes all fields directly without checking if they're provided. This may sendundefined
ornull
values to the API.Apply this pattern for optional fields:
data: { - start, - end, - dates, + ...start && { start }, + ...end && { end }, + ...dates && { dates }, type, name, - repeat, - days, - times, - departure, - priceDelta, + ...repeat && { repeat }, + ...days && { days }, + ...times && { times }, + ...departure && { departure }, + ...priceDelta && { priceDelta }, },components/xola/sources/experience-deleted/experience-deleted.mjs (1)
50-53
: Hardcoded placeholder name reduces usefulness of deletion events.The generic "Deleted Experience" name provides no context about what was deleted. While the deleted experience's actual name may not be available (since it's already deleted from the API), consider including the
id
in a more descriptive format.const deletedExperience = { id, - name: "Deleted Experience", + name: `Experience ID: ${id}`, deletedAt: new Date().toISOString(), };components/xola/sources/purchase-updated/purchase-updated.mjs (1)
30-59
: Consider extracting shared update-tracking logic to the base.The
processEvent()
implementation is nearly identical toexperience-updated.mjs
(lines 30-59), differing only in:
- Resource function name (
listPurchases
vslistExperiences
)- Filtered array name (
filteredPurchases
vsfilteredExperiences
)This duplication increases maintenance burden and risk of inconsistent bug fixes.
Extract the common pattern into the base class (
components/xola/sources/common/base.mjs
), parameterized by resource type. Example:// In base.mjs async processUpdatedResources() { const lastUpdatedAt = this._getLastUpdatedAt(); let maxUpdatedAt = lastUpdatedAt; // Fetch with pagination (see earlier comment) const allResources = await this.fetchAllResources(); const filteredResources = allResources.filter((resource) => { const updatedAt = resource.updatedAt; const createdAt = resource.createdAt; return updatedAt !== createdAt && resource.status !== "deleted" && (!lastUpdatedAt || new Date(updatedAt) > new Date(lastUpdatedAt)); }); filteredResources.forEach((resource) => { const updatedAt = resource.updatedAt; if (!maxUpdatedAt || new Date(updatedAt) > new Date(maxUpdatedAt)) { maxUpdatedAt = updatedAt; } const meta = this.generateMeta(resource); this.$emit(resource, meta); }); if (maxUpdatedAt) { this._setLastUpdatedAt(maxUpdatedAt); } }Then each source would simply call
await this.processUpdatedResources()
fromprocessEvent()
.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (37)
components/xola/actions/add-event-guide/add-event-guide.mjs
(1 hunks)components/xola/actions/create-experience-schedule/create-experience-schedule.mjs
(1 hunks)components/xola/actions/delete-experience-schedule/delete-experience-schedule.mjs
(1 hunks)components/xola/actions/patch-event/patch-event.mjs
(1 hunks)components/xola/actions/remove-event-guide/remove-event-guide.mjs
(1 hunks)components/xola/actions/update-experience-schedule/update-experience-schedule.mjs
(1 hunks)components/xola/common/constants.mjs
(1 hunks)components/xola/package.json
(2 hunks)components/xola/sources/common/base.mjs
(1 hunks)components/xola/sources/common/webhook.mjs
(1 hunks)components/xola/sources/experience-created/experience-created.mjs
(1 hunks)components/xola/sources/experience-created/test-event.mjs
(1 hunks)components/xola/sources/experience-deleted-instant/experience-deleted-instant.mjs
(1 hunks)components/xola/sources/experience-deleted-instant/test-event.mjs
(1 hunks)components/xola/sources/experience-deleted/experience-deleted.mjs
(1 hunks)components/xola/sources/experience-deleted/test-event.mjs
(1 hunks)components/xola/sources/experience-updated-instant/experience-updated-instant.mjs
(1 hunks)components/xola/sources/experience-updated-instant/test-event.mjs
(1 hunks)components/xola/sources/experience-updated/experience-updated.mjs
(1 hunks)components/xola/sources/experience-updated/test-event.mjs
(1 hunks)components/xola/sources/new-experience-created-instant/new-experience-created-instant.mjs
(1 hunks)components/xola/sources/new-experience-created-instant/test-event.mjs
(1 hunks)components/xola/sources/new-order-created-instant/new-order-created-instant.mjs
(1 hunks)components/xola/sources/new-order-created-instant/test-event.mjs
(1 hunks)components/xola/sources/new-purchase-created/new-purchase-created.mjs
(1 hunks)components/xola/sources/new-purchase-created/test-event.mjs
(1 hunks)components/xola/sources/order-deleted-instant/order-deleted-instant.mjs
(1 hunks)components/xola/sources/order-deleted-instant/test-event.mjs
(1 hunks)components/xola/sources/order-updated-instant/order-updated-instant.mjs
(1 hunks)components/xola/sources/order-updated-instant/test-event.mjs
(1 hunks)components/xola/sources/purchase-canceled/purchase-canceled.mjs
(1 hunks)components/xola/sources/purchase-canceled/test-event.mjs
(1 hunks)components/xola/sources/purchase-updated/purchase-updated.mjs
(1 hunks)components/xola/sources/purchase-updated/test-event.mjs
(1 hunks)components/xola/sources/user-updated-instant/test-event.mjs
(1 hunks)components/xola/sources/user-updated-instant/user-updated-instant.mjs
(1 hunks)components/xola/xola.app.mjs
(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2024-07-24T02:06:47.016Z
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#12697
File: components/salesforce_rest_api/sources/common-webhook-methods.mjs:1-71
Timestamp: 2024-07-24T02:06:47.016Z
Learning: The `common-webhook-methods.mjs` object is designed to be extended, similar to an abstract class, and intentionally does not implement certain methods like `generateWebhookMeta` and `getEventType` to enforce implementation in subclasses.
Applied to files:
components/xola/sources/common/webhook.mjs
🧬 Code graph analysis (18)
components/xola/actions/create-experience-schedule/create-experience-schedule.mjs (2)
components/xola/actions/delete-experience-schedule/delete-experience-schedule.mjs (1)
response
(36-40)components/xola/actions/update-experience-schedule/update-experience-schedule.mjs (1)
response
(94-121)
components/xola/sources/order-deleted-instant/order-deleted-instant.mjs (6)
components/xola/sources/experience-deleted-instant/experience-deleted-instant.mjs (1)
body
(18-18)components/xola/sources/experience-updated-instant/experience-updated-instant.mjs (1)
body
(18-18)components/xola/sources/new-experience-created-instant/new-experience-created-instant.mjs (1)
body
(18-18)components/xola/sources/new-order-created-instant/new-order-created-instant.mjs (1)
body
(18-18)components/xola/sources/order-updated-instant/order-updated-instant.mjs (1)
body
(18-18)components/xola/sources/user-updated-instant/user-updated-instant.mjs (1)
body
(18-18)
components/xola/sources/experience-created/experience-created.mjs (4)
components/xola/sources/new-purchase-created/new-purchase-created.mjs (6)
lastCreatedAt
(31-31)maxCreatedAt
(32-32)params
(33-33)createdAt
(40-40)createdAt
(45-45)meta
(49-49)components/xola/sources/experience-deleted/experience-deleted.mjs (2)
params
(38-38)meta
(55-55)components/xola/sources/experience-updated/experience-updated.mjs (4)
params
(33-33)filteredExperiences
(39-45)createdAt
(41-41)meta
(52-52)components/xola/sources/common/webhook.mjs (1)
meta
(56-56)
components/xola/sources/order-updated-instant/order-updated-instant.mjs (6)
components/xola/sources/experience-deleted-instant/experience-deleted-instant.mjs (1)
body
(18-18)components/xola/sources/experience-updated-instant/experience-updated-instant.mjs (1)
body
(18-18)components/xola/sources/new-experience-created-instant/new-experience-created-instant.mjs (1)
body
(18-18)components/xola/sources/new-order-created-instant/new-order-created-instant.mjs (1)
body
(18-18)components/xola/sources/order-deleted-instant/order-deleted-instant.mjs (1)
body
(18-18)components/xola/sources/user-updated-instant/user-updated-instant.mjs (1)
body
(18-18)
components/xola/actions/update-experience-schedule/update-experience-schedule.mjs (3)
components/xola/actions/create-experience-schedule/create-experience-schedule.mjs (1)
response
(153-168)components/xola/actions/delete-experience-schedule/delete-experience-schedule.mjs (1)
response
(36-40)components/xola/actions/patch-event/patch-event.mjs (1)
response
(64-84)
components/xola/actions/add-event-guide/add-event-guide.mjs (1)
components/xola/actions/remove-event-guide/remove-event-guide.mjs (1)
response
(36-40)
components/xola/sources/new-order-created-instant/new-order-created-instant.mjs (6)
components/xola/sources/experience-deleted-instant/experience-deleted-instant.mjs (1)
body
(18-18)components/xola/sources/experience-updated-instant/experience-updated-instant.mjs (1)
body
(18-18)components/xola/sources/new-experience-created-instant/new-experience-created-instant.mjs (1)
body
(18-18)components/xola/sources/order-deleted-instant/order-deleted-instant.mjs (1)
body
(18-18)components/xola/sources/order-updated-instant/order-updated-instant.mjs (1)
body
(18-18)components/xola/sources/user-updated-instant/user-updated-instant.mjs (1)
body
(18-18)
components/xola/sources/experience-updated/experience-updated.mjs (2)
components/xola/sources/purchase-updated/purchase-updated.mjs (7)
lastUpdatedAt
(31-31)maxUpdatedAt
(32-32)params
(33-33)updatedAt
(40-40)updatedAt
(48-48)createdAt
(41-41)meta
(52-52)components/xola/sources/experience-created/experience-created.mjs (5)
params
(33-33)filteredExperiences
(39-42)createdAt
(40-40)createdAt
(45-45)meta
(49-49)
components/xola/sources/experience-deleted/experience-deleted.mjs (6)
components/xola/sources/experience-created/experience-created.mjs (2)
params
(33-33)meta
(49-49)components/xola/sources/experience-updated/experience-updated.mjs (2)
params
(33-33)meta
(52-52)components/xola/sources/new-purchase-created/new-purchase-created.mjs (2)
params
(33-33)meta
(49-49)components/xola/sources/purchase-canceled/purchase-canceled.mjs (2)
params
(34-34)meta
(46-46)components/xola/sources/purchase-updated/purchase-updated.mjs (2)
params
(33-33)meta
(52-52)components/xola/sources/common/webhook.mjs (1)
meta
(56-56)
components/xola/actions/patch-event/patch-event.mjs (2)
components/xola/actions/create-experience-schedule/create-experience-schedule.mjs (1)
response
(153-168)components/xola/actions/update-experience-schedule/update-experience-schedule.mjs (1)
response
(94-121)
components/xola/actions/remove-event-guide/remove-event-guide.mjs (2)
components/xola/actions/add-event-guide/add-event-guide.mjs (1)
response
(43-52)components/xola/actions/delete-experience-schedule/delete-experience-schedule.mjs (1)
response
(36-40)
components/xola/sources/purchase-updated/purchase-updated.mjs (2)
components/xola/sources/experience-updated/experience-updated.mjs (7)
lastUpdatedAt
(31-31)maxUpdatedAt
(32-32)params
(33-33)updatedAt
(40-40)updatedAt
(48-48)createdAt
(41-41)meta
(52-52)components/xola/sources/new-purchase-created/new-purchase-created.mjs (5)
params
(33-33)filteredPurchases
(39-42)createdAt
(40-40)createdAt
(45-45)meta
(49-49)
components/xola/sources/purchase-canceled/purchase-canceled.mjs (2)
components/xola/sources/new-purchase-created/new-purchase-created.mjs (3)
params
(33-33)filteredPurchases
(39-42)meta
(49-49)components/xola/sources/purchase-updated/purchase-updated.mjs (3)
params
(33-33)filteredPurchases
(39-45)meta
(52-52)
components/xola/sources/new-purchase-created/new-purchase-created.mjs (1)
components/xola/sources/experience-created/experience-created.mjs (6)
lastCreatedAt
(31-31)maxCreatedAt
(32-32)params
(33-33)createdAt
(40-40)createdAt
(45-45)meta
(49-49)
components/xola/sources/common/base.mjs (4)
components/xola/sources/experience-created/experience-created.mjs (1)
lastCreatedAt
(31-31)components/xola/sources/new-purchase-created/new-purchase-created.mjs (1)
lastCreatedAt
(31-31)components/xola/sources/experience-updated/experience-updated.mjs (1)
lastUpdatedAt
(31-31)components/xola/sources/purchase-updated/purchase-updated.mjs (1)
lastUpdatedAt
(31-31)
components/xola/actions/delete-experience-schedule/delete-experience-schedule.mjs (3)
components/xola/actions/create-experience-schedule/create-experience-schedule.mjs (1)
response
(153-168)components/xola/actions/remove-event-guide/remove-event-guide.mjs (1)
response
(36-40)components/xola/actions/update-experience-schedule/update-experience-schedule.mjs (1)
response
(94-121)
components/xola/xola.app.mjs (1)
components/xola/sources/common/webhook.mjs (2)
userId
(33-33)hookId
(32-32)
components/xola/sources/common/webhook.mjs (13)
components/xola/sources/experience-created/experience-created.mjs (1)
meta
(49-49)components/xola/sources/experience-deleted/experience-deleted.mjs (1)
meta
(55-55)components/xola/sources/experience-updated/experience-updated.mjs (1)
meta
(52-52)components/xola/sources/new-purchase-created/new-purchase-created.mjs (1)
meta
(49-49)components/xola/sources/purchase-canceled/purchase-canceled.mjs (1)
meta
(46-46)components/xola/sources/purchase-updated/purchase-updated.mjs (1)
meta
(52-52)components/xola/sources/experience-deleted-instant/experience-deleted-instant.mjs (1)
body
(18-18)components/xola/sources/experience-updated-instant/experience-updated-instant.mjs (1)
body
(18-18)components/xola/sources/new-experience-created-instant/new-experience-created-instant.mjs (1)
body
(18-18)components/xola/sources/new-order-created-instant/new-order-created-instant.mjs (1)
body
(18-18)components/xola/sources/order-deleted-instant/order-deleted-instant.mjs (1)
body
(18-18)components/xola/sources/order-updated-instant/order-updated-instant.mjs (1)
body
(18-18)components/xola/sources/user-updated-instant/user-updated-instant.mjs (1)
body
(18-18)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: pnpm publish
- GitHub Check: Publish TypeScript components
- GitHub Check: Verify TypeScript components
- GitHub Check: Lint Code Base
🔇 Additional comments (23)
components/xola/sources/order-updated-instant/test-event.mjs (1)
1-26
: LGTM!The test event payload is well-structured with appropriate sample data for an order update webhook event. The structure includes all necessary fields (order details, experience, user info, and audit trail) in the correct format.
components/xola/sources/order-updated-instant/order-updated-instant.mjs (1)
1-2
: LGTM!Imports are correct and follow the established pattern for instant webhook sources.
components/xola/sources/new-order-created-instant/test-event.mjs (1)
1-21
: LGTM!Well-structured test fixture for order creation webhook events. The payload includes appropriate nested objects for experience and user, with realistic sample data.
components/xola/sources/new-purchase-created/test-event.mjs (1)
1-17
: Clarify the distinction between "order" and "purchase".This fixture is structurally identical to the order creation test event, but uses the "purchase" terminology. The PR includes both "new-order-created-instant" and "new-purchase-created" sources with nearly identical payloads.
Consider documenting whether these represent the same entity in Xola's API or if there's a meaningful distinction. This will help prevent confusion when users choose between these sources.
components/xola/sources/order-deleted-instant/test-event.mjs (1)
1-8
: LGTM!Appropriate minimal payload for order deletion events. The "canceled" status correctly reflects the deleted state.
components/xola/sources/experience-updated/test-event.mjs (1)
1-12
: LGTM!Clean test fixture with realistic experience data. The timestamps correctly show updatedAt after createdAt, reflecting an update scenario.
components/xola/sources/user-updated-instant/test-event.mjs (1)
1-17
: LGTM!Excellent test fixture demonstrating the audit trail functionality. The example clearly shows a phone number being added to a user profile, which is helpful for understanding the webhook payload structure.
components/xola/sources/experience-updated-instant/test-event.mjs (1)
1-29
: LGTM!Excellent comprehensive test fixture with a detailed audit trail. The payload demonstrates multiple field changes (description, duration, price) with clear before/after values, making it highly useful for testing and documentation.
components/xola/sources/new-experience-created-instant/test-event.mjs (1)
1-16
: LGTM!Properly structured creation event fixture. The empty audit object is correct since creation events have no prior state to track, and the matching createdAt/updatedAt timestamps appropriately reflect a newly created entity.
components/xola/sources/experience-created/test-event.mjs (1)
1-12
: LGTM!The test event fixture is well-structured and provides appropriate sample data for the experience-created source.
components/xola/sources/purchase-updated/test-event.mjs (1)
1-17
: LGTM!The test event fixture is well-structured with appropriate nested objects for experience and user data.
components/xola/actions/patch-event/patch-event.mjs (1)
64-84
: LGTM!The conditional spread syntax correctly handles optional fields, ensuring only provided values are sent to the API.
components/xola/sources/experience-created/experience-created.mjs (2)
30-56
: LGTM with note on pagination.The
processEvent
implementation correctly filters bycreatedAt
, tracks the maximum timestamp, and persists state. The logic is sound assuming the API returns results in the expected order (newest first or all within limit).
17-22
: Verify that the API returns experiences in descending order by createdAt and that 100 is sufficient for the polling interval.The
getParams
method hardcodesskip: 0
withlimit: 100
consistently across all polling sources (experience-created, experience-updated, experience-deleted, and all purchase sources). This pattern suggests intentional design rather than an oversight, but it relies on assumptions about API behavior:
- The filtering logic depends on the API returning experiences sorted by recency (newest first)
- If the API returns them in ascending order or unsorted, older experiences could be missed
- If more than 100 experiences are created between polling intervals, any beyond position 100 won't be detected
Confirm with the Xola API documentation that
/experiences
returns results sorted bycreatedAt
descending and that 100 is an appropriate limit for your polling frequency.components/xola/actions/delete-experience-schedule/delete-experience-schedule.mjs (2)
24-28
: LGTM!The
destructiveHint: true
annotation correctly indicates this action permanently deletes data.
36-40
: LGTM!The delete operation is implemented correctly with appropriate error handling delegated to the app layer.
components/xola/sources/experience-deleted-instant/experience-deleted-instant.mjs (1)
1-27
: LGTM!The instant webhook source follows the established pattern correctly. The ID generation with timestamp suffix ensures uniqueness for deduplication, and the fallback from
data.name
todata.id
in the summary handles missing names gracefully.components/xola/sources/new-purchase-created/new-purchase-created.mjs (1)
1-59
: LGTM!The polling source implementation correctly:
- Filters purchases by
createdAt
timestamp using state persistence- Updates
maxCreatedAt
while processing to track the latest item- Uses
Date.parse()
for parsing ISO date strings from the API- Handles the case where no new items exist (line 53 check)
components/xola/sources/new-order-created-instant/new-order-created-instant.mjs (1)
17-24
: LGTM!The ID generation using
data.id
directly is appropriate for a "created" event, as newly created entities have unique IDs. Thededupe: "unique"
setting will handle any duplicate webhook deliveries correctly.components/xola/actions/remove-event-guide/remove-event-guide.mjs (1)
1-45
: LGTM!The action correctly removes a guide from an event with appropriate destructive annotations. The summary message clearly indicates the operation performed.
components/xola/sources/user-updated-instant/user-updated-instant.mjs (1)
1-27
: LGTM!The instant webhook source correctly handles user update events with:
- Timestamp-suffixed ID for deduplication across multiple updates
- Proper fallback chain (
data.name || data.email
) for the summary- Consistent structure following the established pattern
components/xola/sources/experience-updated/experience-updated.mjs (1)
39-45
: Filter logic correctly identifies genuine updates.The conditions properly exclude:
- New creations (
updatedAt !== createdAt
)- Soft-deleted experiences (
status !== "deleted"
)- Previously processed updates (
updatedAt > lastUpdatedAt
)components/xola/sources/experience-deleted/experience-deleted.mjs (1)
36-61
: Snapshot comparison logic is sound for detecting deletions.The approach of comparing previous and current ID sets correctly identifies deletions. Note that experiences created and deleted between polling intervals won't be detected, but this is documented and an acceptable limitation of the polling approach.
const response = await app.addEventGuide({ | ||
$, | ||
eventId, | ||
data: { | ||
guide: { | ||
id: guideId, | ||
forceConfirm, | ||
}, | ||
}, | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Verify the payload structure matches the API contract.
The data structure wraps the guide parameters in a nested data.guide
object, which differs from the simpler structure used in the related remove-event-guide
action (which passes eventId
and guideId
directly). Please confirm this nested structure matches the Xola API specification for assigning guides.
🏁 Script executed:
#!/bin/bash
# Description: Check how addEventGuide is implemented in xola.app.mjs to understand the expected payload structure
# Find the addEventGuide method implementation
rg -A 10 'addEventGuide' components/xola/xola.app.mjs
Length of output: 217
🏁 Script executed:
#!/bin/bash
# Check removeEventGuide implementation and compare with addEventGuide
rg -A 5 'removeEventGuide' components/xola/xola.app.mjs
# Also check the post() method to understand how it handles the data parameter
rg -A 15 'post\s*\(\s*\{' components/xola/xola.app.mjs | head -40
Length of output: 1048
🏁 Script executed:
#!/bin/bash
# Check how remove-event-guide action calls removeEventGuide for comparison
cat components/xola/actions/remove-event-guide/remove-event-guide.mjs | grep -A 10 'removeEventGuide'
Length of output: 509
---
🏁 Script executed:
```shell
#!/bin/bash
# Look for any documentation links or comments about Xola API structure
rg -i 'xola.*api|addEventGuide' components/xola/actions/add-event-guide/add-event-guide.mjs
# Check for test files that might show expected payload
fd -type f -name '*.test.*' -o -name '*.spec.*' | xargs rg 'addEventGuide' 2>/dev/null || echo "No test files found with addEventGuide"
# Look at other POST actions in xola to see if nested data structures are common
rg -B 5 'this\.post\(' components/xola/xola.app.mjs | head -60
Length of output: 676
days: { | ||
type: "string[]", | ||
label: "Days", | ||
description: "Days of week", | ||
optional: true, | ||
options: [ | ||
{ | ||
label: "Sunday", | ||
value: "0", | ||
}, | ||
{ | ||
label: "Monday", | ||
value: "1", | ||
}, | ||
{ | ||
label: "Tuesday", | ||
value: "2", | ||
}, | ||
{ | ||
label: "Wednesday", | ||
value: "3", | ||
}, | ||
{ | ||
label: "Thursday", | ||
value: "4", | ||
}, | ||
{ | ||
label: "Friday", | ||
value: "5", | ||
}, | ||
{ | ||
label: "Saturday", | ||
value: "6", | ||
}, | ||
], | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: Inconsistent days representation across schedule actions.
The days
prop uses string[]
with values "0"
(Sunday) through "6"
(Saturday), but the update action at components/xola/actions/update-experience-schedule/update-experience-schedule.mjs
(lines 50-54) uses integer[]
with values 1
(Monday) through 7
(Sunday).
This inconsistency will break workflows where users create a schedule and then attempt to update it, as the day numbering systems are incompatible.
Choose one consistent convention:
- Option 1: Use the create action's Sunday=0 convention (JavaScript Date.getDay() standard)
- Option 2: Use the update action's Monday=1 convention (ISO 8601 standard)
Then update both actions to match.
🤖 Prompt for AI Agents
In
components/xola/actions/create-experience-schedule/create-experience-schedule.mjs
(lines 31-66) and
components/xola/actions/update-experience-schedule/update-experience-schedule.mjs
(around lines 50-54), the days representations are inconsistent: create uses
string[] "0"–"6" (Sunday=0) while update uses integer[] 1–7 (Monday=1). Pick one
convention (recommend Option 1: Sunday=0 as string[] to match JS Date.getDay()
or Option 2: Monday=1 as integer[] for ISO), then make both files use the exact
same type and numeric mapping: update the create action to use the chosen
numeric range and type (or update the update action to match create), adjust the
options array values and types accordingly, and ensure any input
parsing/validation and downstream code expecting days is updated to accept the
unified format.
type: { | ||
type: "string", | ||
label: "Type", | ||
description: "The type of schedule", | ||
options: [ | ||
"available", | ||
"unavailable", | ||
], | ||
optional: true, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Major: Inconsistent type options across schedule actions.
The type
prop offers "available"
and "unavailable"
, but the create action at components/xola/actions/create-experience-schedule/create-experience-schedule.mjs
(lines 22-30) uses "available"
and "blackout"
.
Users won't be able to update a schedule created with type "blackout"
because it's not an option here.
Verify the correct options from the Xola API documentation and align both actions.
🤖 Prompt for AI Agents
In
components/xola/actions/update-experience-schedule/update-experience-schedule.mjs
around lines 29 to 37, the type options list uses "available" and "unavailable"
which is inconsistent with the create action (create-experience-schedule) that
uses "available" and "blackout"; verify the Xola API docs for the correct enum
(likely "available" and "blackout") and update this file to match the canonical
values, then update the create action if needed so both actions use the exact
same allowed type strings across both files.
repeat: { | ||
type: "string", | ||
label: "Repeat", | ||
description: "How often the schedule repeats", | ||
options: [ | ||
"daily", | ||
"weekly", | ||
"monthly", | ||
], | ||
optional: true, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Major: Inconsistent repeat options across schedule actions.
The repeat
prop offers "daily"
, "weekly"
, and "monthly"
, but the create action at components/xola/actions/create-experience-schedule/create-experience-schedule.mjs
(lines 103-111) uses "weekly"
and "custom"
.
This prevents users from updating schedules that were created with the "custom"
repeat option.
Verify the correct options from the Xola API documentation and align both actions.
🤖 Prompt for AI Agents
In
components/xola/actions/update-experience-schedule/update-experience-schedule.mjs
around lines 39-48, the repeat options are inconsistent with the create action
(which uses "weekly" and "custom"), causing inability to update schedules
created with "custom"; update the repeat.options to match the create action
(include "custom" and "weekly" exactly as used in
create-experience-schedule.mjs) and verify against the Xola API docs, ensuring
both create and update actions use the same canonical set of repeat values.
getParams() { | ||
return { | ||
limit: 100, | ||
skip: 0, | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: Pagination not implemented — deletions beyond first 100 experiences will never be detected.
The hardcoded skip: 0
limits tracking to only the first 100 experiences. Any experiences beyond that threshold will never appear in the currentIds
or previousIds
snapshots, making their deletions undetectable. This fundamentally undermines the deletion tracking mechanism.
Implement pagination following the same pattern suggested for experience-updated.mjs
. The snapshot comparison approach requires tracking all experiences, not just the first page.
generateMeta(body) { | ||
const { data } = body; | ||
return { | ||
id: `${data.id}-${Date.now()}`, | ||
summary: `Experience Updated: ${data.name}`, | ||
ts: Date.now(), | ||
}; | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add fallback for missing experience name.
The summary uses data.name
without a fallback, which could display "Experience Updated: undefined" if the name field is missing. Consider adding a fallback similar to other instant sources in this PR (e.g., experience-deleted-instant
uses data.name || data.id
).
Apply this diff to add a fallback:
generateMeta(body) {
const { data } = body;
return {
id: `${data.id}-${Date.now()}`,
- summary: `Experience Updated: ${data.name}`,
+ summary: `Experience Updated: ${data.name || data.id}`,
ts: Date.now(),
};
},
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
generateMeta(body) { | |
const { data } = body; | |
return { | |
id: `${data.id}-${Date.now()}`, | |
summary: `Experience Updated: ${data.name}`, | |
ts: Date.now(), | |
}; | |
}, | |
generateMeta(body) { | |
const { data } = body; | |
return { | |
id: `${data.id}-${Date.now()}`, | |
summary: `Experience Updated: ${data.name || data.id}`, | |
ts: Date.now(), | |
}; | |
}, |
🤖 Prompt for AI Agents
In
components/xola/sources/experience-updated-instant/experience-updated-instant.mjs
around lines 17 to 24, the generateMeta function builds the summary using
data.name which can be undefined; update the summary to use a fallback (e.g.,
data.name || data.id) so it reads `Experience Updated: ${data.name || data.id}`
to avoid "undefined" in the output and keep behavior consistent with other
instant sources.
getParams() { | ||
return { | ||
limit: 100, | ||
skip: 0, | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: Pagination not implemented — updates beyond first 100 experiences will be missed.
The hardcoded skip: 0
means only the first 100 experiences are fetched on each poll. If the Xola account has more than 100 experiences, any updates to experiences beyond that limit will never be detected, leading to missed events and incomplete monitoring.
Implement pagination to fetch all experiences. Example approach:
- getParams() {
- return {
- limit: 100,
- skip: 0,
- };
- },
+ getParams(skip = 0) {
+ return {
+ limit: 100,
+ skip,
+ };
+ },
Then in processEvent()
, add a pagination loop:
async processEvent() {
const lastUpdatedAt = this._getLastUpdatedAt();
let maxUpdatedAt = lastUpdatedAt;
- const params = this.getParams();
+
+ let skip = 0;
+ let hasMore = true;
+ const allExperiences = [];
- const { data } = await this.getResourceFn()({
- params,
- });
+ while (hasMore) {
+ const params = this.getParams(skip);
+ const { data } = await this.getResourceFn()({ params });
+
+ if (data.length === 0) {
+ hasMore = false;
+ } else {
+ allExperiences.push(...data);
+ skip += data.length;
+ if (data.length < 100) hasMore = false;
+ }
+ }
- const filteredExperiences = data.filter((experience) => {
+ const filteredExperiences = allExperiences.filter((experience) => {
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In components/xola/sources/experience-updated/experience-updated.mjs around
lines 17-21, the current getParams() returns a hardcoded skip: 0 causing only
the first 100 experiences to be fetched; implement pagination by making limit
configurable (keep 100) and remove the fixed skip, then update the
polling/processEvent logic to loop requests incrementing skip by limit until a
page returns fewer than limit items (or no items), aggregating results across
pages; ensure each request uses the updated params (limit and skip), respects
any API rate limits or backoff, and stops when no more pages remain so updates
beyond the first 100 experiences are not missed.
generateMeta(body) { | ||
const { data } = body; | ||
return { | ||
id: data.id, | ||
summary: `New Experience Created: ${data.name}`, | ||
ts: Date.now(), | ||
}; | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add fallback for missing experience name.
The summary uses data.name
without a fallback. If the name field is missing or empty, the summary will display "New Experience Created: undefined". Consider adding a fallback to data.id
for consistency with other instant sources.
Apply this diff to add a fallback:
generateMeta(body) {
const { data } = body;
return {
id: data.id,
- summary: `New Experience Created: ${data.name}`,
+ summary: `New Experience Created: ${data.name || data.id}`,
ts: Date.now(),
};
},
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
generateMeta(body) { | |
const { data } = body; | |
return { | |
id: data.id, | |
summary: `New Experience Created: ${data.name}`, | |
ts: Date.now(), | |
}; | |
}, | |
generateMeta(body) { | |
const { data } = body; | |
return { | |
id: data.id, | |
summary: `New Experience Created: ${data.name || data.id}`, | |
ts: Date.now(), | |
}; | |
}, |
🤖 Prompt for AI Agents
In
components/xola/sources/new-experience-created-instant/new-experience-created-instant.mjs
around lines 17 to 24, the summary uses data.name directly which can be
undefined; update the summary to fall back to data.id when name is missing or
empty (e.g., use data.name || data.id or a ternary to check for a non-empty
string) so the summary becomes "New Experience Created: <name or id>" and
remains consistent with other instant sources.
getResourceFn() { | ||
return this.app.listPurchases; | ||
}, | ||
getParams() { | ||
return { | ||
limit: 100, | ||
skip: 0, | ||
status: "canceled", | ||
}; | ||
}, | ||
generateMeta(purchase) { | ||
return { | ||
id: purchase.id, | ||
summary: `Purchase Canceled: ${purchase.id}`, | ||
ts: Date.parse(purchase.updatedAt), | ||
}; | ||
}, | ||
async processEvent() { | ||
const processedIds = this._getProcessedIds(); | ||
const newProcessedIds = []; | ||
const params = this.getParams(); | ||
|
||
const { data } = await this.getResourceFn()({ | ||
params, | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bind the resource function before invoking
Line 15 returns this.app.listPurchases
, and Line 36 calls it without binding. In modules, the function loses its this
context, so inside listPurchases
the call to this.makeRequest
throws at runtime. Wrap or bind the method when you return it so this
still points to the Xola app.
getResourceFn() {
- return this.app.listPurchases;
+ return (args = {}) => this.app.listPurchases(args);
},
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
getResourceFn() { | |
return this.app.listPurchases; | |
}, | |
getParams() { | |
return { | |
limit: 100, | |
skip: 0, | |
status: "canceled", | |
}; | |
}, | |
generateMeta(purchase) { | |
return { | |
id: purchase.id, | |
summary: `Purchase Canceled: ${purchase.id}`, | |
ts: Date.parse(purchase.updatedAt), | |
}; | |
}, | |
async processEvent() { | |
const processedIds = this._getProcessedIds(); | |
const newProcessedIds = []; | |
const params = this.getParams(); | |
const { data } = await this.getResourceFn()({ | |
params, | |
}); | |
getResourceFn() { | |
return (args = {}) => this.app.listPurchases(args); | |
}, | |
getParams() { | |
return { | |
limit: 100, | |
skip: 0, | |
status: "canceled", | |
}; | |
}, | |
generateMeta(purchase) { | |
return { | |
id: purchase.id, | |
summary: `Purchase Canceled: ${purchase.id}`, | |
ts: Date.parse(purchase.updatedAt), | |
}; | |
}, | |
async processEvent() { | |
const processedIds = this._getProcessedIds(); | |
const newProcessedIds = []; | |
const params = this.getParams(); | |
const { data } = await this.getResourceFn()({ | |
params, | |
}); |
🤖 Prompt for AI Agents
In components/xola/sources/purchase-canceled/purchase-canceled.mjs around lines
14 to 38, getResourceFn currently returns this.app.listPurchases which loses its
class context when later invoked; change the return to a bound function so the
Xola app remains the this inside listPurchases (e.g., return
this.app.listPurchases.bind(this.app) or return
(...args)=>this.app.listPurchases(...args)) so subsequent calls to the resource
function don’t throw when using this.makeRequest.
WHY
Resolves #18614
Summary by CodeRabbit
New Features
Chores