-
Notifications
You must be signed in to change notification settings - Fork 5.5k
trustpilot fixes #18152
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
trustpilot fixes #18152
Conversation
The latest updates on your projects. Learn more about Vercel for GitHub. 2 Skipped Deployments
|
WalkthroughIntroduce an auth-aware makeRequest client, reorganize endpoints toward private/service flows, replace bespoke HTTP/auth logic with unified requests, refactor parsers, add conversation retrieval and multi-step reply flows, convert polling to a lastReviewTime model, remove several legacy sources, and bump package versions. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant Action as GetConversationFromProductReview
participant API as Trustpilot API
Note right of Action #B3E5FC: validate reviewId
User->>Action: run({ reviewId })
Action->>API: GET /private/product-reviews/{reviewId}
API-->>Action: product review (may include conversationId)
alt conversationId exists
Action->>API: GET /private/conversations/{conversationId}
API-->>Action: conversation
Action-->>User: { success: true, conversation, metadata }
else no conversationId
Action-->>User: { success: false, message, review:{ hasConversation:false }, metadata }
end
sequenceDiagram
autonumber
actor User
participant Action as ReplyToProductReview
participant API as Trustpilot API
Note right of Action #FFE0B2: validate inputs (reviewId, content, businessUserId)
User->>Action: run({ reviewId, content, businessUserId, integrationId? })
Action->>API: GET /private/product-reviews/{reviewId}
API-->>Action: review (may include conversationId)
alt conversation exists
Action->>API: POST /private/conversations/{conversationId}/comments
Note right of Action #C8E6C9: header x-business-user-id
API-->>Action: posted comment
else create conversation then comment
Action->>API: POST /private/product-reviews/{reviewId}/create-conversation
API-->>Action: { conversationId }
Action->>API: POST /private/conversations/{conversationId}/comments
Note right of Action #C8E6C9: header x-business-user-id
API-->>Action: posted comment
end
Action-->>User: { comment, metadata:{ wasConversationCreated, ... } }
sequenceDiagram
autonumber
actor User
participant Action as ReplyToServiceReview
participant API as Trustpilot API
Note right of Action #FFCDD2: validate inputs (reviewId, authorBusinessUserId, message)
User->>Action: run({ reviewId, authorBusinessUserId, message })
Action->>API: POST /private/reviews/{reviewId}/reply
Note right of Action #E1BEE7: body { authorBusinessUserId, message }
API-->>Action: reply result
Action-->>User: { reply, metadata:{ httpStatus, messageLength, ... } }
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120+ minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (2 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (2)
✅ Files skipped from review due to trivial changes (2)
⏰ 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)
Tip 👮 Agentic pre-merge checks are now available in preview!Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.
Please see the documentation for more information. Example: reviews:
pre_merge_checks:
custom_checks:
- name: "Undocumented Breaking Changes"
mode: "warning"
instructions: |
Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal). Please share your feedback with us on this Discord post. 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 |
Thank you so much for submitting this! We've added it to our backlog to review, and our team has been notified. |
Thanks for submitting this PR! When we review PRs, we follow the Pipedream component guidelines. If you're not familiar, here's a quick checklist:
|
Major improvements and API updates across all actions: - Enhanced private API support with proper authentication - Improved parameter handling and validation - Better error handling and response structures - Added new conversation flow for product reviews - Fixed endpoint URLs to match latest API documentation - Streamlined request/response processing 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
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: 15
🧹 Nitpick comments (37)
components/trustpilot/package.json (1)
3-3
: Confirm semver intent for removed sourcesMultiple sources were removed across this PR. Under 0.x, minor bumps may include breaking changes, but please confirm no active workflows depend on removed keys and add a CHANGELOG note calling this out.
components/trustpilot/sources/new-product-reviews/new-product-reviews.mjs (2)
51-54
: Order by newest first (if supported by endpoint)Prevents missing new items on page 1 when mixing statuses.
Apply if supported:
const fetchParams = { businessUnitId: this.businessUnitId, perPage: 100, - page: 1, + page: 1, + orderBy: "createdat.desc", };To verify support, check the app’s product review fetch or docs for orderBy on private product reviews.
56-60
: Paginate until reviews are older than last poll to avoid gapsIf > perPage reviews arrive between polls, page 1 isn’t enough.
Apply pattern:
- const result = await this.trustpilot.fetchProductReviews(fetchParams); - const reviews = result.reviews || []; + const toMs = (d) => new Date(d).getTime(); + const lastTs = Number(this._getLastReviewTime()) || 0; + let page = 1; + let reviews = []; + while (true) { + const { reviews: batch = [] } = await this.trustpilot.fetchProductReviews({ ...fetchParams, page }); + if (!batch.length) break; + reviews.push(...batch); + const oldest = Math.min(...batch.map((r) => toMs(r.createdAt))); + if (oldest <= lastTs) break; + page += 1; + }components/trustpilot/actions/fetch-product-review-by-id/fetch-product-review-by-id.mjs (3)
47-49
: Be robust to response shape (review vs root object)Some endpoints return
{ review: {...} }
. Safely support both.Apply:
- const review = parseProductReview(response); + const raw = response?.review ?? response; + const review = parseProductReview(raw);
59-61
: Improve error messaging for 404Provide a clearer “not found” for missing IDs; keep other errors wrapped.
Apply:
- } catch (error) { - throw new Error(`Failed to fetch product review: ${error.message}`); - } + } catch (error) { + if (error.response?.status === 404) { + throw new Error(`Review ${this.reviewId} not found (404)`); + } + throw new Error(`Failed to fetch product review: ${error.message}`); + }
42-46
: Optional: set an explicit timeoutAvoid long hangs on degraded endpoints.
Apply:
const response = await makeRequest(this.trustpilot, { endpoint, + timeout: 20000, });
components/trustpilot/actions/get-conversation-from-product-review/get-conversation-from-product-review.mjs (2)
50-63
: Add a final $summary when no conversation exists.This improves UX in runs where the action exits early without fetching a conversation.
if (!conversationId) { + $.export("$summary", `No conversation found for product review ${reviewId}`); return { success: false, message: "No conversation found for this product review",
1-8
: Return richer API error details to aid troubleshooting.Surface parsed status/code/message so users can act on 401/403/404/429, etc.
-import { - buildUrl, - validateReviewId, -} from "../../common/utils.mjs"; +import { + buildUrl, + validateReviewId, + parseApiError, +} from "../../common/utils.mjs";- } catch (error) { - throw new ConfigurationError(`Failed to get conversation from product review: ${error.message}`); - } + } catch (error) { + const apiErr = parseApiError(error); + throw new ConfigurationError( + `Failed to get conversation from product review: ${apiErr.message} (status: ${apiErr.status}, code: ${apiErr.code})`, + ); + }Also applies to: 90-92
components/trustpilot/actions/reply-to-product-review/reply-to-product-review.mjs (2)
79-97
: Handle “conversation already exists” (HTTP 409) to avoid failing valid runs.Races can occur between Step 1 and Step 2. Gracefully recover by refetching the review (or using the ID from the error body if present). Also, include x-business-user-id on conversation creation if the API requires it.
- // Step 2: Create conversation if it doesn't exist - if (!conversationId) { - $.export("$summary", "Creating conversation for review..."); - - const createConversationEndpoint = buildUrl(ENDPOINTS.CREATE_CONVERSATION_FOR_REVIEW, { - reviewId, - }); - - const createConversationResponse = await makeRequest(this.trustpilot, { - endpoint: createConversationEndpoint, - method: "POST", - }); - - conversationId = createConversationResponse.conversationId; - - if (!conversationId) { - throw new Error("Failed to create conversation - no conversationId returned"); - } - } + // Step 2: Create conversation if it doesn't exist + if (!conversationId) { + $.export("$summary", "Creating conversation for review..."); + const createConversationEndpoint = buildUrl(ENDPOINTS.CREATE_CONVERSATION_FOR_REVIEW, { reviewId }); + try { + const createConversationResponse = await makeRequest(this.trustpilot, { + endpoint: createConversationEndpoint, + method: "POST", + additionalHeaders: { + "x-business-user-id": businessUserId, // required by some endpoints + }, + }); + conversationId = createConversationResponse.conversationId; + } catch (err) { + // If the conversation already exists, recover by re-fetching it + if (err?.response?.status === 409) { + $.export("$summary", "Conversation already exists. Fetching conversationId..."); + const refreshed = await makeRequest(this.trustpilot, { endpoint: getReviewEndpoint }); + conversationId = refreshed.conversationId || err?.response?.data?.conversationId; + } else { + throw err; + } + } + if (!conversationId) { + throw new Error("Failed to create or locate conversationId"); + } + }
63-64
: Sanitize and truncate reply content
Trustpilot’s Conversations API doesn’t document a strict limit; community testing indicates a nominal cap of ~10 000 chars and practical limits closer to ~8 000. To prevent server-side rejections, import and apply a sanitizer/truncator:-import { - buildUrl, - validateReviewId, -} from "../../common/utils.mjs"; +import { + buildUrl, + validateReviewId, + sanitizeInput, +} from "../../common/utils.mjs";- const trimmedContent = content.trim(); + const trimmedContent = sanitizeInput(content, 8000); // truncate/sanitize to 8 000 charsAlso ensure the
x-business-user-id
header is sent on Create Conversation calls when using a client_credentials token.components/trustpilot/common/utils.mjs (2)
107-149
: PII exposure: consumer email is passed through. Verify intent and compliance.parseProductReview returns consumer.email verbatim. Ensure this is expected and compliant with your data handling policy. If not strictly required, consider masking or omitting it by default.
197-203
: Normalize boolean default for countsTowardsLocationTrustScore.For consistency with countsTowardsTrustScore, default this to false when undefined.
- countsTowardsLocationTrustScore: review.countsTowardsLocationTrustScore, + countsTowardsLocationTrustScore: review.countsTowardsLocationTrustScore || false,components/trustpilot/actions/fetch-product-reviews/fetch-product-reviews.mjs (2)
77-77
: Guard summary against unexpected API shapes.Avoid runtime errors if reviews is missing/not an array.
- $.export("$summary", `Successfully fetched ${result.reviews.length} product review(s) for business unit ${businessUnitId}`); + const count = Array.isArray(result?.reviews) ? result.reviews.length : Array.isArray(result) ? result.length : 0; + $.export("$summary", `Successfully fetched ${count} product review(s) for business unit ${businessUnitId}`);
35-40
: Language vs. locale: confirm intent and API mapping.Both language and locale are exposed and forwarded. If the API expects only one (or mutually exclusive values), this can lead to ignored params or ambiguous results. Consider a single prop or validation to ensure they’re not conflicting.
Example constraint:
async run({ $ }) { const { businessUnitId, page, perPage, sku, language, state, locale, } = this; + if (language && locale) { + throw new Error("Specify either language or locale, not both."); + }Also applies to: 47-52, 55-63
components/trustpilot/actions/fetch-service-review-by-id/fetch-service-review-by-id.mjs (2)
26-34
: Review ID validation is too strict for hex case.validateReviewId only allows lowercase a–f; ObjectIDs/hex IDs are case-insensitive. Allow uppercase to avoid false negatives.
Update the validator in components/trustpilot/common/utils.mjs:
-export function validateReviewId(reviewId) { - // Trustpilot Review IDs are 24-character hexadecimal strings (MongoDB ObjectID format) - return ( - typeof reviewId === "string" && - /^[a-f0-9]{24}$/.test(reviewId) - ); -} +export function validateReviewId(reviewId) { + // Trustpilot Review IDs are 24-character hexadecimal strings (MongoDB ObjectID format) + return typeof reviewId === "string" && /^[A-Fa-f0-9]{24}$/.test(reviewId); +}
13-14
: Double-check documentation link matches the private-by-id endpoint.The link references “business-units-api#get-private-review-by-id” while the code hits
/private/reviews/{reviewId}
. Ensure the doc URL matches the exact endpoint to avoid confusing users.components/trustpilot/common/api-client.mjs (3)
37-44
: Prevent accidental override of auth headers via args.headers.Spreading
...args
afterheaders
lets callers overwrite Authorization/apikey. Merge only non-header args, or explicitly merge safe header keys.- const config = { + const { headers: _ignoredHeaders, ...restArgs } = args; + const config = { method, url, headers, params: formatQueryParams(params), timeout, - ...args, + ...restArgs, };
54-69
: Retry policy could be more robust.Consider retrying on transient errors like 408/429/500/502/503/504 and network timeouts, with jittered exponential backoff.
Example:
- if (retries > 0 && error.response?.status === HTTP_STATUS.TOO_MANY_REQUESTS) { + const status = error.response?.status; + const transient = [HTTP_STATUS.TOO_MANY_REQUESTS, 408, 500, 502, 503, 504]; + if (retries > 0 && transient.includes(status)) { - const delay = Math.min( - RETRY_CONFIG.INITIAL_DELAY * (RETRY_CONFIG.MAX_RETRIES - retries + 1), - RETRY_CONFIG.MAX_DELAY, - ); + const attempt = RETRY_CONFIG.MAX_RETRIES - retries + 1; + const base = Math.min(RETRY_CONFIG.INITIAL_DELAY * 2 ** (attempt - 1), RETRY_CONFIG.MAX_DELAY); + const jitter = Math.random() * base * 0.2; + const delay = Math.min(base + jitter, RETRY_CONFIG.MAX_DELAY);
116-135
: Heuristic private URL detection.Using url.includes("private") works with current constants, but is brittle. Prefer a flag or derive from endpoint groups to avoid misclassification if paths change.
Minimal change:
-export async function makeRequest(trustpilotApp, { +export async function makeRequest(trustpilotApp, { endpoint, method = "GET", params = {}, data = null, timeout = 30000, additionalHeaders = {}, ...args }, retries = RETRY_CONFIG.MAX_RETRIES) { const url = `${BASE_URL}${endpoint}`; - const headers = { - ...getAuthHeaders(trustpilotApp, url), + const headers = { + ...getAuthHeaders(trustpilotApp, endpoint), ...additionalHeaders, };And switch getAuthHeaders to check the endpoint path (startsWith("/private")) instead of the full URL string.
components/trustpilot/actions/search-business-units-test/search-business-units-test.mjs (1)
39-48
: Consider null safety for businessUnit properties.The destructuring assumes
id
anddisplayName
exist on each business unit. Add defensive checks to prevent runtime errors.const options = businessUnits.map((businessUnit) => { const { id, displayName, } = businessUnit; + + if (!id || !displayName) { + console.warn("Business unit missing required fields:", businessUnit); + return null; + } return { label: displayName, value: id, }; - }); + }).filter(Boolean); // Remove null entriescomponents/trustpilot/sources/new-service-reviews/new-service-reviews.mjs (1)
6-6
: Description is too verbose for a component description.The description is extremely long and contains implementation details that belong in documentation rather than the component description field.
- description: "Emit new event when a customer posts a new service review on Trustpilot. This source periodically polls the Trustpilot API to detect new service reviews using the private reviews API for comprehensive coverage. Each event contains the complete review data including star rating, review text, consumer details, business unit info, customer email, and timestamps. Ideal for monitoring overall business reputation, tracking customer satisfaction metrics, and triggering workflows based on review ratings or content.", + description: "Emit new event when a customer posts a new service review on Trustpilot using the private reviews API. [See the documentation](https://developers.trustpilot.com/private-reviews-api)",components/trustpilot/common/constants.mjs (3)
47-54
: De-duplicate sort strings across actionsSince SORT_OPTIONS are defined here, import and reuse them in actions (e.g., fetch-service-reviews) to avoid drift.
Apply in the action file:
- { - label: "Created At (Ascending)", - value: "createdat.asc", - }, + { label: "Created At (Ascending)", value: SORT_OPTIONS.CREATED_AT_ASC },(Repeat for the other three options after importing SORT_OPTIONS.)
3-10
: Stale webhook events?WEBHOOK_EVENTS remain, but webhook endpoints were dropped in this PR. If they’re unused, remove them or add a comment noting they’re for inbound-only use to reduce confusion.
91-97
: Align SOURCE_TYPES with the current sourcesSome source types (e.g., new_conversations) were removed per the PR summary. Either prune them or annotate deprecated values to prevent accidental use.
components/trustpilot/actions/fetch-service-reviews/fetch-service-reviews.mjs (5)
6-8
: Description overpromises vs single-page fetchThe action fetches a single page, but the description claims up to 100,000 records. Either implement pagination within the action or adjust the copy.
Apply to fix the copy:
- description: "Get private reviews for a business unit, limited to 100,000 records. Response includes customer email and order ID. [See the documentation](https://developers.trustpilot.com/business-units-api#get-private-reviews-for-business-unit)", + description: "Get a page of private reviews for a business unit. Response includes customer email and order ID. [See the documentation](https://developers.trustpilot.com/business-units-api#get-private-reviews-for-business-unit)",If you want pagination here, I can propose a minimal loop that respects perPage/max pages and returns aggregated results.
31-36
: Validate/sanitize stars inputThe API expects 1–5 comma-separated. Consider normalizing (e.g., strip spaces, validate digits) upstream to prevent 400s.
I suggest adding validation in the app’s fetchServiceReviews (see app comment with diff).
52-76
: Use constants for orderBy valuesAvoid hardcoding the same strings in multiple files.
Apply:
-import trustpilot from "../../trustpilot.app.mjs"; +import trustpilot from "../../trustpilot.app.mjs"; +import { SORT_OPTIONS } from "../../common/constants.mjs"; ... - value: "createdat.asc", + value: SORT_OPTIONS.CREATED_AT_ASC, ... - value: "createdat.desc", + value: SORT_OPTIONS.CREATED_AT_DESC, ... - value: "stars.asc", + value: SORT_OPTIONS.STARS_ASC, ... - value: "stars.desc", + value: SORT_OPTIONS.STARS_DESC,
120-131
: Grammar nit and add example format for datetimesSmall copy fix and an example improves UX.
- description: "Filter reviews by datetime range. If no time is specified than time is implicit 00:00:00. Format: 2013-09-07T13:37:00", + description: "Filter reviews by datetime range. If no time is specified then time is implicit 00:00:00. Example format: 2013-09-07T13:37:00", ... - description: "Filter reviews by datetime range. If no time is specified than time is implicit 00:00:00. Format: 2013-09-07T13:37:00", + description: "Filter reviews by datetime range. If no time is specified then time is implicit 00:00:00. Example format: 2013-09-07T13:37:00",
205-207
: Preserve original error as causeKeeps stack/metadata for debugging while improving the message.
- } catch (error) { - throw new Error(`Failed to fetch service reviews: ${error.message}`); + } catch (error) { + throw new Error(`Failed to fetch service reviews: ${error.message}`, { cause: error }); }components/trustpilot/trustpilot.app.mjs (8)
112-121
: Return empty options on error is safe, but log is minimalIf you see repeated failures, consider including response status/body in the debug log to aid triage.
147-154
: Business unit ID format validation may be too strictvalidateBusinessUnitId enforces lowercase hex. ObjectIDs are case-insensitive; rejecting uppercase can block valid input pasted from external tools.
If you want to relax validation without changing all callers, update utils:
// components/trustpilot/common/utils.mjs export function validateBusinessUnitId(businessUnitId) { return typeof businessUnitId === "string" && /^[a-f0-9]{24}$/i.test(businessUnitId); }
160-182
: Consider normalizing and validating stars before requestPrevents 400s and keeps API surface tidy.
Apply within fetchServiceReviews:
- if (stars) queryParams.stars = stars; + if (stars) { + const normalized = String(stars).replace(/\s+/g, ""); + if (!/^(?:[1-5](?:,[1-5])*)$/.test(normalized)) { + throw new Error('Invalid "stars" format. Use comma-separated integers 1–5, e.g. "1,3,5".'); + } + queryParams.stars = normalized; + }
189-196
: Service reviews pagination shape assumptionYou map response.pagination.{total,page,perPage,hasMore}. Please confirm the v1 private reviews endpoint returns this shape (some Trustpilot endpoints use links.next/prev). If not guaranteed, add a fallback like the product flow.
221-226
: Add business unit ID validation for product reviews for parityService reviews validate ID format; product reviews should do the same to catch obvious errors early.
if (!businessUnitId) { throw new Error("Business Unit ID is required"); } + if (!validateBusinessUnitId(businessUnitId)) { + throw new Error("Invalid business unit ID format"); + }
248-257
: Product reviews pagination shapeYou rely on links.total and links.next. Please confirm this is stable and documented. If both shapes occur (pagination vs links), consider a defensive merge to standardize the returned pagination object.
210-269
: Optional: clamp perPage to MAX_LIMIT at the app layerActions set max=100, but sources or external callers could bypass. Clamp to constants.MAX_LIMIT defensively.
-import { ENDPOINTS } from "./common/constants.mjs"; +import { ENDPOINTS, MAX_LIMIT } from "./common/constants.mjs"; ... - if (perPage) queryParams.perPage = perPage; + if (perPage) queryParams.perPage = Math.max(1, Math.min(perPage, MAX_LIMIT));(Repeat for fetchProductReviews.)
108-121
: Add concise JSDoc for public methodsHelps users and aligns with the guidelines to document methods.
/** * Search business units by query. * @param {{query?: string, page?: number}} params * @returns {Promise<Array<{id: string, displayName: string}>>} */(Apply similarly to fetchServiceReviews and fetchProductReviews.)
Also applies to: 123-207, 209-269
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (22)
components/trustpilot/actions/fetch-product-review-by-id/fetch-product-review-by-id.mjs
(2 hunks)components/trustpilot/actions/fetch-product-reviews/fetch-product-reviews.mjs
(2 hunks)components/trustpilot/actions/fetch-service-review-by-id/fetch-service-review-by-id.mjs
(2 hunks)components/trustpilot/actions/fetch-service-reviews/fetch-service-reviews.mjs
(2 hunks)components/trustpilot/actions/get-conversation-from-product-review/get-conversation-from-product-review.mjs
(1 hunks)components/trustpilot/actions/reply-to-product-review/reply-to-product-review.mjs
(2 hunks)components/trustpilot/actions/reply-to-service-review/reply-to-service-review.mjs
(2 hunks)components/trustpilot/actions/search-business-units-test/search-business-units-test.mjs
(1 hunks)components/trustpilot/common/api-client.mjs
(1 hunks)components/trustpilot/common/constants.mjs
(1 hunks)components/trustpilot/common/utils.mjs
(1 hunks)components/trustpilot/package.json
(1 hunks)components/trustpilot/sources/common/polling.mjs
(0 hunks)components/trustpilot/sources/new-conversations/new-conversations.mjs
(0 hunks)components/trustpilot/sources/new-product-review-replies/new-product-review-replies.mjs
(0 hunks)components/trustpilot/sources/new-product-reviews/new-product-reviews.mjs
(1 hunks)components/trustpilot/sources/new-service-review-replies/new-service-review-replies.mjs
(0 hunks)components/trustpilot/sources/new-service-reviews/new-service-reviews.mjs
(1 hunks)components/trustpilot/sources/updated-conversations/updated-conversations.mjs
(0 hunks)components/trustpilot/sources/updated-product-reviews/updated-product-reviews.mjs
(0 hunks)components/trustpilot/sources/updated-service-reviews/updated-service-reviews.mjs
(0 hunks)components/trustpilot/trustpilot.app.mjs
(4 hunks)
💤 Files with no reviewable changes (7)
- components/trustpilot/sources/common/polling.mjs
- components/trustpilot/sources/new-product-review-replies/new-product-review-replies.mjs
- components/trustpilot/sources/updated-product-reviews/updated-product-reviews.mjs
- components/trustpilot/sources/new-conversations/new-conversations.mjs
- components/trustpilot/sources/updated-conversations/updated-conversations.mjs
- components/trustpilot/sources/new-service-review-replies/new-service-review-replies.mjs
- components/trustpilot/sources/updated-service-reviews/updated-service-reviews.mjs
🧰 Additional context used
🧬 Code graph analysis (13)
components/trustpilot/actions/get-conversation-from-product-review/get-conversation-from-product-review.mjs (3)
components/trustpilot/common/utils.mjs (3)
validateReviewId
(231-237)buildUrl
(51-65)error
(266-268)components/trustpilot/common/constants.mjs (2)
ENDPOINTS
(12-35)ENDPOINTS
(12-35)components/trustpilot/common/api-client.mjs (1)
makeRequest
(22-71)
components/trustpilot/actions/fetch-service-review-by-id/fetch-service-review-by-id.mjs (3)
components/trustpilot/common/utils.mjs (3)
validateReviewId
(231-237)buildUrl
(51-65)parseServiceReview
(157-211)components/trustpilot/common/constants.mjs (2)
ENDPOINTS
(12-35)ENDPOINTS
(12-35)components/trustpilot/common/api-client.mjs (2)
response
(51-51)makeRequest
(22-71)
components/trustpilot/actions/reply-to-product-review/reply-to-product-review.mjs (3)
components/trustpilot/common/utils.mjs (3)
validateReviewId
(231-237)buildUrl
(51-65)error
(266-268)components/trustpilot/common/constants.mjs (2)
ENDPOINTS
(12-35)ENDPOINTS
(12-35)components/trustpilot/common/api-client.mjs (1)
makeRequest
(22-71)
components/trustpilot/common/api-client.mjs (6)
components/trustpilot/common/constants.mjs (6)
RETRY_CONFIG
(79-83)RETRY_CONFIG
(79-83)BASE_URL
(1-1)BASE_URL
(1-1)HTTP_STATUS
(67-77)HTTP_STATUS
(67-77)components/trustpilot/common/utils.mjs (2)
formatQueryParams
(244-257)sleep
(290-292)components/trustpilot/trustpilot.app.mjs (7)
endpoint
(156-158)endpoint
(227-229)params
(125-145)params
(211-219)response
(112-118)response
(184-187)response
(243-246)components/trustpilot/actions/fetch-product-review-by-id/fetch-product-review-by-id.mjs (2)
endpoint
(38-40)response
(43-45)components/trustpilot/actions/fetch-service-review-by-id/fetch-service-review-by-id.mjs (2)
endpoint
(38-40)response
(43-45)components/trustpilot/actions/reply-to-service-review/reply-to-service-review.mjs (1)
endpoint
(60-62)
components/trustpilot/actions/reply-to-service-review/reply-to-service-review.mjs (3)
components/trustpilot/common/utils.mjs (3)
validateReviewId
(231-237)buildUrl
(51-65)error
(266-268)components/trustpilot/common/constants.mjs (2)
ENDPOINTS
(12-35)ENDPOINTS
(12-35)components/trustpilot/common/api-client.mjs (1)
makeRequest
(22-71)
components/trustpilot/common/utils.mjs (2)
components/trustpilot/actions/fetch-product-review-by-id/fetch-product-review-by-id.mjs (1)
review
(48-48)components/trustpilot/actions/fetch-service-review-by-id/fetch-service-review-by-id.mjs (1)
review
(48-48)
components/trustpilot/actions/search-business-units-test/search-business-units-test.mjs (2)
components/trustpilot/trustpilot.app.mjs (2)
businessUnits
(28-33)businessUnit
(36-38)components/trustpilot/common/utils.mjs (1)
error
(266-268)
components/trustpilot/actions/fetch-product-review-by-id/fetch-product-review-by-id.mjs (3)
components/trustpilot/common/utils.mjs (3)
validateReviewId
(231-237)buildUrl
(51-65)parseProductReview
(107-150)components/trustpilot/common/constants.mjs (2)
ENDPOINTS
(12-35)ENDPOINTS
(12-35)components/trustpilot/common/api-client.mjs (2)
response
(51-51)makeRequest
(22-71)
components/trustpilot/actions/fetch-product-reviews/fetch-product-reviews.mjs (3)
components/trustpilot/actions/fetch-service-reviews/fetch-service-reviews.mjs (1)
result
(180-200)components/trustpilot/sources/new-product-reviews/new-product-reviews.mjs (1)
result
(57-57)components/trustpilot/sources/new-service-reviews/new-service-reviews.mjs (1)
result
(59-59)
components/trustpilot/sources/new-product-reviews/new-product-reviews.mjs (3)
components/trustpilot/sources/new-service-reviews/new-service-reviews.mjs (8)
stars
(34-34)consumerName
(35-35)businessUnit
(36-36)lastReviewTime
(44-44)fetchParams
(47-51)result
(59-59)reviews
(61-61)latestReviewTime
(69-69)components/trustpilot/trustpilot.app.mjs (3)
businessUnit
(36-38)reviews
(190-190)reviews
(249-249)components/trustpilot/actions/fetch-product-reviews/fetch-product-reviews.mjs (1)
result
(67-75)
components/trustpilot/actions/fetch-service-reviews/fetch-service-reviews.mjs (3)
components/trustpilot/actions/fetch-product-reviews/fetch-product-reviews.mjs (1)
result
(67-75)components/trustpilot/sources/new-product-reviews/new-product-reviews.mjs (1)
result
(57-57)components/trustpilot/sources/new-service-reviews/new-service-reviews.mjs (1)
result
(59-59)
components/trustpilot/sources/new-service-reviews/new-service-reviews.mjs (3)
components/trustpilot/sources/new-product-reviews/new-product-reviews.mjs (8)
stars
(34-34)consumerName
(35-35)businessUnit
(37-37)lastReviewTime
(45-45)fetchParams
(50-54)result
(57-57)reviews
(59-59)latestReviewTime
(79-79)components/trustpilot/trustpilot.app.mjs (3)
businessUnit
(36-38)reviews
(190-190)reviews
(249-249)components/trustpilot/actions/fetch-service-reviews/fetch-service-reviews.mjs (1)
result
(180-200)
components/trustpilot/trustpilot.app.mjs (3)
components/trustpilot/common/api-client.mjs (2)
response
(51-51)makeRequest
(22-71)components/trustpilot/common/constants.mjs (2)
ENDPOINTS
(12-35)ENDPOINTS
(12-35)components/trustpilot/common/utils.mjs (4)
validateBusinessUnitId
(218-224)buildUrl
(51-65)parseServiceReview
(157-211)parseProductReview
(107-150)
⏰ 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 (19)
components/trustpilot/sources/new-product-reviews/new-product-reviews.mjs (1)
1-26
: Props/timer/db usage looks goodKey, dedupe, timer default, and state storage align with Pipedream sources.
components/trustpilot/actions/fetch-product-review-by-id/fetch-product-review-by-id.mjs (1)
28-35
: Good input validationPresence + format checks for reviewId are clear and fail fast.
components/trustpilot/actions/get-conversation-from-product-review/get-conversation-from-product-review.mjs (1)
36-89
: Flow and error handling look solid.Validation is tight, retry behavior is delegated to the shared client, and the two-step fetch is correct for private product reviews → conversations.
components/trustpilot/actions/reply-to-product-review/reply-to-product-review.mjs (2)
41-139
: Overall LGTM — correct multi-step flow with clear validation and summaries.Good use of shared client, endpoints, and input validation; header use for comment creation is correct.
87-91
: Verify header requirements for create-conversation.Some private endpoints require x-business-user-id; you add it for comments but not for conversation creation. The diff above includes it defensively; please confirm with docs.
components/trustpilot/common/utils.mjs (1)
157-210
: Service review parsing looks comprehensive and consistent.Good escaping, sensible defaults (e.g., numberOfLikes), and structure.
components/trustpilot/actions/fetch-product-reviews/fetch-product-reviews.mjs (1)
7-7
: No internal dependencies on raw API response shape Onlyresult.reviews
andresult.pagination
are consumed (e.g. innew-product-reviews
); no code references raw fields—safe to proceed.components/trustpilot/sources/new-service-reviews/new-service-reviews.mjs (1)
78-82
: Add deduplication logic to prevent duplicate events.Since the source uses
dedupe: "unique"
with the review ID, this should work correctly. However, consider adding explicit deduplication if reviews can be updated after creation.The event emission correctly uses the review ID for deduplication and includes appropriate metadata.
components/trustpilot/common/constants.mjs (5)
14-16
: BUSINESS_UNITS endpoint change looks goodThe switch to the search endpoint matches expected usage for query-backed lookups.
19-20
: Verify PUBLIC_REVIEW_BY_ID pathYou changed the path to "/reviews/{reviewId}". Please confirm Trustpilot’s v1 public API supports fetching a review by ID without the business unit path to avoid 404s.
21-25
: Service review endpoints: solid additionNew private service review endpoints align with the refactor and naming is consistent.
27-30
: Product review + create-conversation endpoints look consistentNaming and paths align with the new flows.
34-35
: Confirm reply-to-conversation path semanticsChanging to "/private/conversations/{conversationId}/comments" typically implies a comments collection (often POST). Make sure all callers switched HTTP method / payload accordingly.
components/trustpilot/actions/fetch-service-reviews/fetch-service-reviews.mjs (4)
21-22
: Reusing app propDefinition for language is correctKeeps the surface consistent across actions.
23-30
: Page prop contract is clearMin, default, and empty-page behavior are documented. Looks good.
43-51
: Per-page bounds are goodmin/max set to 1/100 matches API.
178-201
: Good: routes through shared app methodKeeps logic centralized and returns the unified shape.
components/trustpilot/trustpilot.app.mjs (2)
18-27
: Nice UX: empty query default and useQueryDefaulting to "a" avoids Trustpilot’s requirement pitfalls and improves searchability.
29-33
: Correct page index translationClear comment and correct +1 transform.
components/trustpilot/actions/fetch-product-reviews/fetch-product-reviews.mjs
Outdated
Show resolved
Hide resolved
components/trustpilot/actions/fetch-service-review-by-id/fetch-service-review-by-id.mjs
Show resolved
Hide resolved
components/trustpilot/actions/reply-to-service-review/reply-to-service-review.mjs
Outdated
Show resolved
Hide resolved
components/trustpilot/actions/reply-to-service-review/reply-to-service-review.mjs
Show resolved
Hide resolved
components/trustpilot/actions/reply-to-service-review/reply-to-service-review.mjs
Show resolved
Hide resolved
components/trustpilot/sources/new-product-reviews/new-product-reviews.mjs
Outdated
Show resolved
Hide resolved
components/trustpilot/sources/new-product-reviews/new-product-reviews.mjs
Outdated
Show resolved
Hide resolved
components/trustpilot/sources/new-product-reviews/new-product-reviews.mjs
Outdated
Show resolved
Hide resolved
components/trustpilot/sources/new-service-reviews/new-service-reviews.mjs
Show resolved
Hide resolved
components/trustpilot/sources/new-service-reviews/new-service-reviews.mjs
Outdated
Show resolved
Hide resolved
…reviews.mjs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
…reviews.mjs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
…reviews.mjs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
…reviews.mjs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
components/trustpilot/actions/fetch-product-reviews/fetch-product-reviews.mjs
Outdated
Show resolved
Hide resolved
components/trustpilot/actions/fetch-product-reviews/fetch-product-reviews.mjs
Outdated
Show resolved
Hide resolved
components/trustpilot/actions/fetch-service-reviews/fetch-service-reviews.mjs
Outdated
Show resolved
Hide resolved
components/trustpilot/actions/fetch-service-reviews/fetch-service-reviews.mjs
Outdated
Show resolved
Hide resolved
components/trustpilot/actions/fetch-service-reviews/fetch-service-reviews.mjs
Outdated
Show resolved
Hide resolved
components/trustpilot/sources/new-product-reviews/new-product-reviews.mjs
Show resolved
Hide resolved
components/trustpilot/sources/new-service-reviews/new-service-reviews.mjs
Outdated
Show resolved
Hide resolved
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: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
components/trustpilot/actions/fetch-service-review-by-id/fetch-service-review-by-id.mjs (1)
36-62
: Preserve rich Axios errors: remove try/catch wrapper.Catching and rethrowing with
new Error(...)
discards Pipedream Axios context (status, data). Let errors bubble so users get full diagnostics.- try { - // Build the endpoint URL for private service review - const endpoint = buildUrl(ENDPOINTS.SERVICE_REVIEW_BY_ID, { - reviewId, - }); - - // Make the API request - const response = await makeRequest($, this.trustpilot, { - endpoint, - }); - - // Parse the service review with the correct parser - const review = parseServiceReview(response); - - $.export("$summary", `Successfully fetched service review ${reviewId}`); - - return { - review, - metadata: { - reviewId, - requestTime: new Date().toISOString(), - }, - }; - } catch (error) { - throw new Error(`Failed to fetch service review: ${error.message}`); - } + // Build the endpoint URL for private service review + const endpoint = buildUrl(ENDPOINTS.SERVICE_REVIEW_BY_ID, { reviewId }); + + // Make the API request + const response = await makeRequest($, this.trustpilot, { endpoint }); + + // Parse the service review with the correct parser + const review = parseServiceReview(response); + + $.export("$summary", `Successfully fetched service review ${reviewId}`); + + return { + review, + metadata: { + reviewId, + requestTime: new Date().toISOString(), + }, + };components/trustpilot/actions/fetch-product-reviews/fetch-product-reviews.mjs (1)
65-83
: Preserve Axios error details: drop try/catch.Let Axios throw to keep
status
,data
, etc., rather than wrapping withnew Error(...)
.- try { - // Use the shared method from the app - const result = await this.trustpilot.fetchProductReviews($, { - businessUnitId, - page, - perPage, - sku, - language, - state, - locale, - }); - - $.export("$summary", `Successfully fetched ${result.reviews.length} product review(s) for business unit ${businessUnitId}`); - - return result; - } catch (error) { - throw new Error(`Failed to fetch product reviews: ${error.message}`); - } + // Use the shared method from the app + const result = await this.trustpilot.fetchProductReviews($, { + businessUnitId, + page, + perPage, + sku, + language, + state, + locale, + }); + + $.export("$summary", `Successfully fetched ${result.reviews?.length ?? 0} product review(s) for business unit ${businessUnitId}`); + + return result;
♻️ Duplicate comments (1)
components/trustpilot/actions/fetch-service-reviews/fetch-service-reviews.mjs (1)
23-30
: Good: page prop UX and guardrailsClear description, min=1, sensible default. Matches prior review guidance.
🧹 Nitpick comments (19)
components/trustpilot/actions/fetch-product-review-by-id/fetch-product-review-by-id.mjs (1)
28-35
: Use ConfigurationError for input/flow errors (align with other actions).Consistent error types improve UX and surface configuration issues clearly.
+import { ConfigurationError } from "@pipedream/platform"; import { makeRequest } from "../../common/api-client.mjs"; import { ENDPOINTS } from "../../common/constants.mjs"; import { buildUrl, parseProductReview, validateReviewId, } from "../../common/utils.mjs"; // Validate required parameters if (!reviewId) { - throw new Error("Review ID is required"); + throw new ConfigurationError("Review ID is required"); } if (!validateReviewId(reviewId)) { - throw new Error("Invalid review ID format"); + throw new ConfigurationError("Invalid review ID format"); } ... } catch (error) { - throw new Error(`Failed to fetch product review: ${error.message}`); + throw new ConfigurationError(`Failed to fetch product review: ${error.message}`); }Also applies to: 59-61, 2-4
components/trustpilot/actions/reply-to-product-review/reply-to-product-review.mjs (2)
63-64
: Sanitize reply content without imposing a length cap.Avoid control chars while respecting prior decision to not enforce arbitrary limits.
import { makeRequest } from "../../common/api-client.mjs"; import { ENDPOINTS } from "../../common/constants.mjs"; import { buildUrl, validateReviewId, + sanitizeInput, } from "../../common/utils.mjs"; -const trimmedContent = content.trim(); +const sanitizedContent = sanitizeInput(content, Infinity); // Prepare request data const requestData = { - content: trimmedContent, + content: sanitizedContent, }; // Add integrationId if provided if (integrationId) { requestData.integrationId = integrationId; } ... return { success: true, comment: replyResponse, metadata: { reviewId, conversationId, businessUserId, - contentLength: trimmedContent.length, + contentLength: sanitizedContent.length, integrationId: integrationId || null, wasConversationCreated: !review.conversationId, requestTime: new Date().toISOString(), }, };Also applies to: 106-115, 127-139, 5-8
67-68
: Intermediate $summary calls are overwritten.Keep a single final summary or switch these to logs if you need progress notes.
-$.export("$summary", "Fetching product review details..."); ... -$.export("$summary", "Creating conversation for review..."); ... -$.export("$summary", "Posting reply comment...");Also applies to: 81-82, 100-101
components/trustpilot/common/utils.mjs (3)
218-224
: Accept uppercase hex and stray whitespace for IDs.ObjectIDs can be uppercase; trimming avoids false negatives.
export function validateBusinessUnitId(businessUnitId) { // Trustpilot Business Unit IDs are 24-character hexadecimal strings (MongoDB ObjectID format) return ( - typeof businessUnitId === "string" && - /^[a-f0-9]{24}$/.test(businessUnitId) + typeof businessUnitId === "string" && + /^[a-f0-9]{24}$/i.test(businessUnitId.trim()) ); }
231-236
: Same normalization for review IDs.export function validateReviewId(reviewId) { // Trustpilot Review IDs are 24-character hexadecimal strings (MongoDB ObjectID format) return ( - typeof reviewId === "string" && - /^[a-f0-9]{24}$/.test(reviewId) + typeof reviewId === "string" && + /^[a-f0-9]{24}$/i.test(reviewId.trim()) ); }
33-36
: Comment/code mismatch on sanitizeInput.The code uses explicit ranges, not Unicode property escapes. Update the comment for clarity.
-// Using Unicode property escapes for safer regex -// eslint-disable-next-line no-control-regex +// Using explicit control-char ranges for broad runtime support +// eslint-disable-next-line no-control-regex sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");components/trustpilot/actions/reply-to-service-review/reply-to-service-review.mjs (2)
13-14
: Add helpful docs link to description.-description: "Reply to a service review on Trustpilot.", +description: "Reply to a service review on Trustpilot. [See the documentation](https://developers.trustpilot.com/service-reviews-api#reply-to-a-review)",
56-57
: Sanitize message without adding a size limit.Removes control chars but keeps your “no arbitrary cutoff” stance.
import { makeRequest } from "../../common/api-client.mjs"; import { ENDPOINTS } from "../../common/constants.mjs"; import { buildUrl, validateReviewId, + sanitizeInput, } from "../../common/utils.mjs"; -const trimmedMessage = message.trim(); +const sanitizedMessage = sanitizeInput(message, Infinity); // Prepare request data according to API specification const requestData = { authorBusinessUserId, - message: trimmedMessage, + message: sanitizedMessage, }; ... return { success: true, reply: { - message: trimmedMessage, + message: sanitizedMessage, authorBusinessUserId, reviewId, status: "created", statusCode: 201, postedAt: new Date().toISOString(), }, metadata: { reviewId, authorBusinessUserId, - messageLength: trimmedMessage.length, + messageLength: sanitizedMessage.length, requestTime: new Date().toISOString(), httpStatus: "201 Created", }, };Also applies to: 65-69, 83-95, 5-8
components/trustpilot/actions/fetch-service-review-by-id/fetch-service-review-by-id.mjs (1)
26-34
: Optional: be lenient on reviewId input (trim/case).End users may paste uppercase/space-padded IDs. Normalize before validation to reduce false negatives.
- const { reviewId } = this; + const { reviewId } = this; + const normalizedReviewId = String(reviewId).trim(); // Validate required parameters - if (!reviewId) { + if (!normalizedReviewId) { throw new Error("Review ID is required"); } - if (!validateReviewId(reviewId)) { + if (!validateReviewId(normalizedReviewId)) { throw new Error("Invalid review ID format"); }Outside this file (utils.mjs), consider accepting uppercase hex as well:
- /^[a-f0-9]{24}$/.test(reviewId) + /^[a-f0-9]{24}$/i.test(reviewId)components/trustpilot/actions/get-conversation-from-product-review/get-conversation-from-product-review.mjs (1)
49-61
: Optional: add a summary on the no-conversation path.Improves UX in the run log when exiting early.
if (!conversationId) { + $.export("$summary", `No conversation found for product review ${reviewId}`); return { success: false, message: "No conversation found for this product review",
components/trustpilot/common/api-client.mjs (3)
5-16
: JSDoc parameter order is outdated.Document the
$
parameter and reflect the actual signaturemakeRequest($, trustpilotApp, options)
.- * @param {object} trustpilotApp - The Trustpilot app instance with auth credentials - * @param {object} options - Request options + * @param {object} $ - Pipedream context (for axios/error handling) + * @param {object} trustpilotApp - The Trustpilot app instance with auth credentials + * @param {object} options - Request options
92-111
: Optional: include Accept header.Some APIs vary payloads by
Accept
. AddingAccept: application/json
is harmless and can prevent content-negotiation surprises.const headers = { "Content-Type": "application/json", + "Accept": "application/json", "User-Agent": "Pipedream/1.0", };
54-56
: Optional: make private detection more explicit.
url.includes("private")
is workable but brittle. Consider checking theendpoint
prefix (e.g.,endpoint.startsWith("/private/")
) or allowing an explicitauthMode
option.components/trustpilot/sources/new-service-reviews/new-service-reviews.mjs (1)
53-56
: Optional: ensurestartDateTime
is ISO string.If
lastReviewTime
was stored as a number, coerce to ISO before using it in query params.- if (lastReviewTime) { - fetchParams.startDateTime = lastReviewTime; - } + if (lastReviewTime) { + fetchParams.startDateTime = new Date(lastReviewTime).toISOString(); + }components/trustpilot/common/constants.mjs (1)
29-30
: Rename CREATE_CONVERSATION_FOR_REVIEW to CREATE_CONVERSATION_FOR_PRODUCT_REVIEWIn components/trustpilot/common/constants.mjs, update:
- CREATE_CONVERSATION_FOR_REVIEW: "/private/product-reviews/{reviewId}/create-conversation", + CREATE_CONVERSATION_FOR_PRODUCT_REVIEW: "/private/product-reviews/{reviewId}/create-conversation",Then update its usage in components/trustpilot/actions/reply-to-product-review/reply-to-product-review.mjs (change ENDPOINTS.CREATE_CONVERSATION_FOR_REVIEW to ENDPOINTS.CREATE_CONVERSATION_FOR_PRODUCT_REVIEW) to match the naming of other product-review constants.
components/trustpilot/actions/fetch-service-reviews/fetch-service-reviews.mjs (2)
31-36
: Constrain or validate star rating inputFree-form "1-5, separated by commas" is error-prone. Either present selectable options or validate with a regex to avoid invalid input (e.g., "6", "a").
- stars: { - type: "string", + stars: { + type: "string", label: "Star Rating", - description: "Filter by reviews with a specific star rating. 1-5, separated by commas.", + description: "Filter by reviews with a specific star rating. Accepts comma-separated values from 1–5 (e.g., 5 or 4,5).", + pattern: "^(?:[1-5])(?:,(?:[1-5]))*$", optional: true, },
120-131
: Date-time props: great detail; consider timezone noteDescriptions are solid (ISO format, implicit 00:00:00). Add a brief note on timezone expectation (UTC vs local) if the API assumes UTC.
components/trustpilot/trustpilot.app.mjs (2)
18-33
: Harden async options for businessUnitId (page default + fallback labels)Guard against undefined page and missing displayName to improve UX.
- async options({ - page, query, - }) { + async options({ + page = 0, query, + } = {}) { try { if (query === "") { // Trustpilot requires a query to be passed in, default to "a" if empty query = "a"; } const businessUnits = await this.searchBusinessUnits({ // Trustpilot requires the page to be 1-indexed // whereas pipedream is 0-indexed page: page + 1, query, }); - return businessUnits.map((businessUnit) => { - const { - id, displayName, - } = businessUnit; - - return { - label: displayName, - value: id, - }; - }); + return businessUnits.map(({ id, displayName, identifyingName }) => ({ + label: displayName || identifyingName || id, + value: id, + }));
175-191
: Add consistent BU ID validation for product reviewsService flow validates businessUnitId format; apply the same check here for parity.
async fetchProductReviews($, params = {}) { const { businessUnitId, page, perPage, sku, language, state, locale, } = params; // Validate required parameters if (!businessUnitId) { throw new Error("Business Unit ID is required"); } + if (!validateBusinessUnitId(businessUnitId)) { + throw new Error("Invalid business unit ID format"); + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (13)
components/trustpilot/actions/fetch-product-review-by-id/fetch-product-review-by-id.mjs
(2 hunks)components/trustpilot/actions/fetch-product-reviews/fetch-product-reviews.mjs
(2 hunks)components/trustpilot/actions/fetch-service-review-by-id/fetch-service-review-by-id.mjs
(2 hunks)components/trustpilot/actions/fetch-service-reviews/fetch-service-reviews.mjs
(2 hunks)components/trustpilot/actions/get-conversation-from-product-review/get-conversation-from-product-review.mjs
(1 hunks)components/trustpilot/actions/reply-to-product-review/reply-to-product-review.mjs
(2 hunks)components/trustpilot/actions/reply-to-service-review/reply-to-service-review.mjs
(2 hunks)components/trustpilot/common/api-client.mjs
(1 hunks)components/trustpilot/common/constants.mjs
(1 hunks)components/trustpilot/common/utils.mjs
(1 hunks)components/trustpilot/sources/new-product-reviews/new-product-reviews.mjs
(1 hunks)components/trustpilot/sources/new-service-reviews/new-service-reviews.mjs
(1 hunks)components/trustpilot/trustpilot.app.mjs
(4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- components/trustpilot/sources/new-product-reviews/new-product-reviews.mjs
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-03T14:43:27.512Z
Learnt from: Afstkla
PR: PipedreamHQ/pipedream#18152
File: components/trustpilot/actions/fetch-service-review-by-id/fetch-service-review-by-id.mjs:42-49
Timestamp: 2025-09-03T14:43:27.512Z
Learning: The Trustpilot SERVICE_REVIEW_BY_ID endpoint returns the review data directly as the response body, not wrapped in a container object like `{ review: {...} }`.
Applied to files:
components/trustpilot/actions/fetch-service-review-by-id/fetch-service-review-by-id.mjs
🧬 Code graph analysis (11)
components/trustpilot/actions/get-conversation-from-product-review/get-conversation-from-product-review.mjs (3)
components/trustpilot/common/utils.mjs (2)
validateReviewId
(231-237)buildUrl
(51-65)components/trustpilot/common/constants.mjs (2)
ENDPOINTS
(12-35)ENDPOINTS
(12-35)components/trustpilot/common/api-client.mjs (1)
makeRequest
(17-47)
components/trustpilot/actions/reply-to-product-review/reply-to-product-review.mjs (3)
components/trustpilot/common/utils.mjs (2)
validateReviewId
(231-237)buildUrl
(51-65)components/trustpilot/common/constants.mjs (2)
ENDPOINTS
(12-35)ENDPOINTS
(12-35)components/trustpilot/common/api-client.mjs (1)
makeRequest
(17-47)
components/trustpilot/actions/fetch-service-review-by-id/fetch-service-review-by-id.mjs (3)
components/trustpilot/common/utils.mjs (3)
validateReviewId
(231-237)buildUrl
(51-65)parseServiceReview
(157-211)components/trustpilot/common/constants.mjs (2)
ENDPOINTS
(12-35)ENDPOINTS
(12-35)components/trustpilot/common/api-client.mjs (2)
response
(45-45)makeRequest
(17-47)
components/trustpilot/actions/reply-to-service-review/reply-to-service-review.mjs (3)
components/trustpilot/common/utils.mjs (2)
validateReviewId
(231-237)buildUrl
(51-65)components/trustpilot/common/constants.mjs (2)
ENDPOINTS
(12-35)ENDPOINTS
(12-35)components/trustpilot/common/api-client.mjs (1)
makeRequest
(17-47)
components/trustpilot/common/api-client.mjs (6)
components/trustpilot/common/utils.mjs (1)
formatQueryParams
(244-257)components/trustpilot/common/constants.mjs (2)
BASE_URL
(1-1)BASE_URL
(1-1)components/trustpilot/actions/fetch-product-review-by-id/fetch-product-review-by-id.mjs (2)
endpoint
(38-40)response
(43-45)components/trustpilot/actions/fetch-service-review-by-id/fetch-service-review-by-id.mjs (2)
endpoint
(38-40)response
(43-45)components/trustpilot/actions/reply-to-service-review/reply-to-service-review.mjs (1)
endpoint
(60-62)components/trustpilot/trustpilot.app.mjs (7)
endpoint
(136-138)endpoint
(193-195)params
(125-125)params
(177-185)response
(112-118)response
(150-153)response
(209-212)
components/trustpilot/actions/fetch-product-review-by-id/fetch-product-review-by-id.mjs (3)
components/trustpilot/common/utils.mjs (3)
validateReviewId
(231-237)buildUrl
(51-65)parseProductReview
(107-150)components/trustpilot/common/constants.mjs (2)
ENDPOINTS
(12-35)ENDPOINTS
(12-35)components/trustpilot/common/api-client.mjs (2)
response
(45-45)makeRequest
(17-47)
components/trustpilot/common/utils.mjs (2)
components/trustpilot/actions/fetch-product-review-by-id/fetch-product-review-by-id.mjs (1)
review
(48-48)components/trustpilot/actions/fetch-service-review-by-id/fetch-service-review-by-id.mjs (1)
review
(48-48)
components/trustpilot/actions/fetch-product-reviews/fetch-product-reviews.mjs (3)
components/trustpilot/actions/fetch-service-reviews/fetch-service-reviews.mjs (1)
result
(180-200)components/trustpilot/sources/new-product-reviews/new-product-reviews.mjs (1)
result
(57-57)components/trustpilot/sources/new-service-reviews/new-service-reviews.mjs (1)
result
(59-59)
components/trustpilot/actions/fetch-service-reviews/fetch-service-reviews.mjs (3)
components/trustpilot/actions/fetch-product-reviews/fetch-product-reviews.mjs (1)
result
(67-75)components/trustpilot/sources/new-product-reviews/new-product-reviews.mjs (1)
result
(57-57)components/trustpilot/sources/new-service-reviews/new-service-reviews.mjs (1)
result
(59-59)
components/trustpilot/sources/new-service-reviews/new-service-reviews.mjs (3)
components/trustpilot/sources/new-product-reviews/new-product-reviews.mjs (8)
stars
(34-34)consumerName
(35-35)businessUnit
(37-37)lastReviewTime
(45-45)fetchParams
(50-54)result
(57-57)reviews
(59-59)latestReviewTime
(80-80)components/trustpilot/trustpilot.app.mjs (3)
businessUnit
(36-38)reviews
(156-156)reviews
(215-215)components/trustpilot/actions/fetch-service-reviews/fetch-service-reviews.mjs (1)
result
(180-200)
components/trustpilot/trustpilot.app.mjs (3)
components/trustpilot/common/api-client.mjs (2)
response
(45-45)makeRequest
(17-47)components/trustpilot/common/constants.mjs (2)
ENDPOINTS
(12-35)ENDPOINTS
(12-35)components/trustpilot/common/utils.mjs (4)
validateBusinessUnitId
(218-224)buildUrl
(51-65)parseServiceReview
(157-211)parseProductReview
(107-150)
⏰ 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 (16)
components/trustpilot/actions/fetch-product-review-by-id/fetch-product-review-by-id.mjs (1)
37-49
: Endpoint construction + parsing look solid.Good use of ENDPOINTS + buildUrl + parseProductReview with makeRequest.
components/trustpilot/actions/reply-to-product-review/reply-to-product-review.mjs (1)
87-123
: Conversation creation flow and x-business-user-id header are correct.Nice multi-step orchestration with clear error on missing conversationId.
components/trustpilot/common/utils.mjs (1)
102-150
: Richer, escaped review models look good.Good field coverage and systematic escapeHtml usage across nested objects.
Also applies to: 152-211
components/trustpilot/actions/reply-to-service-review/reply-to-service-review.mjs (1)
42-76
: Validation and endpoint usage are correct.Input checks, URL build, and payload shape match the private reply endpoint.
components/trustpilot/actions/fetch-service-review-by-id/fetch-service-review-by-id.mjs (1)
37-49
: Endpoint build + parsing look correct.Building
SERVICE_REVIEW_BY_ID
withbuildUrl
and feeding the raw payload toparseServiceReview
matches the endpoint that returns the review body directly. LGTM.components/trustpilot/actions/fetch-product-reviews/fetch-product-reviews.mjs (1)
7-7
: Major version bump for prop renames is correct.Prop surface changed; bumping to
1.0.0
is appropriate for breaking changes.components/trustpilot/actions/get-conversation-from-product-review/get-conversation-from-product-review.mjs (2)
28-35
: Good validation UX with ConfigurationError.Clear, user-facing errors for missing/invalid
reviewId
. Nicely done.
36-89
: Solid two-step retrieval flow.Fetching the review first, early-returning when no
conversationId
, then fetching conversation details is clean and predictable. Using$summary
for progress/success is helpful.components/trustpilot/common/api-client.mjs (1)
17-47
: Good:$
is first param and passed to axios with fallback.This aligns with platform best practices and preserves richer error context.
components/trustpilot/common/constants.mjs (4)
21-25
: Ensure REPLY_TO_SERVICE_REVIEW uses POST
Confirm that incomponents/trustpilot/actions/reply-to-service-review/reply-to-service-review.mjs
the HTTP method is explicitly set toPOST
and that the request body is sent as expected.
14-16
: No hard-coded/business-units
calls remain in Trustpilot
OnlyENDPOINTS.BUSINESS_UNITS
is referenced incomponents/trustpilot/trustpilot.app.mjs
; no raw/business-units
paths found.
19-20
: PUBLIC_REVIEW_BY_ID callers updated — no businessUnitId references found
Our searches didn’t locate anybuildUrl
calls passingbusinessUnitId
intoPUBLIC_REVIEW_BY_ID
. Please manually verify all callers to ensure no businessUnitId path-param remains.
34-35
: Conversation reply configuration OK
Method POST and request body schema (content
, optionalintegrationId
) align with Trustpilot’s API for creating comments.components/trustpilot/actions/fetch-service-reviews/fetch-service-reviews.mjs (2)
156-176
: Destructured defaults are fine, but currently overridden downstreamThese defaults are sensible; however, the app method overwrites page/perPage/orderBy/ignoreTagValueCase. After fixing the app, these will apply correctly.
202-205
: Summary and return shape look goodReturning the full result and summarizing by reviews.length is consistent with the app method contract.
components/trustpilot/trustpilot.app.mjs (1)
110-121
: Search call through makeRequest looks goodQuery and page pass-through is clear; returning [] on failure keeps the UI resilient.
components/trustpilot/actions/fetch-service-reviews/fetch-service-reviews.mjs
Outdated
Show resolved
Hide resolved
components/trustpilot/actions/fetch-service-reviews/fetch-service-reviews.mjs
Show resolved
Hide resolved
components/trustpilot/sources/new-service-reviews/new-service-reviews.mjs
Outdated
Show resolved
Hide resolved
components/trustpilot/sources/new-service-reviews/new-service-reviews.mjs
Outdated
Show resolved
Hide resolved
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: 0
♻️ Duplicate comments (2)
components/trustpilot/sources/new-service-reviews/new-service-reviews.mjs (1)
34-46
: Fix pagination: condition checks total accumulated length, so you fetch at most one extra page.Use the size of the last fetched page to continue, and avoid mutating the input
params
.- // Handle pagination for service reviews - while (result.reviews && result.reviews.length === 100) { - params.page = (params.page || 1) + 1; - const nextResult = await this.trustpilot.fetchServiceReviews($, params); - result.reviews = result.reviews.concat(nextResult.reviews || []); - } + // Handle pagination for service reviews + const perPage = params.perPage ?? 100; + let page = params.page ?? 1; + let all = result.reviews || []; + let lastChunkSize = all.length; + while (lastChunkSize === perPage) { + page += 1; + const next = await this.trustpilot.fetchServiceReviews($, { ...params, page }); + const chunk = next.reviews || []; + all = all.concat(chunk); + lastChunkSize = chunk.length; + } + result = { ...result, reviews: all };components/trustpilot/sources/common/polling.mjs (1)
76-127
: Let Axios errors propagate; remove the try/catch wrapper.Catching and rethrowing loses response metadata (status, data). Allow the framework to surface the original error.
- async run({ $ }) { - try { + async run({ $ }) { // Get the last review time for filtering new reviews const lastReviewTime = this._getLastReviewTime(); @@ - // Update the last review time for next poll - if (latestReviewTime && latestReviewTime !== lastReviewTime) { - this._setLastReviewTime(latestReviewTime); - } - - } catch (error) { - throw new Error(`Failed to fetch reviews: ${error.message}`); - } + // Update the last review time for next poll + if (latestReviewTime && latestReviewTime !== lastReviewTime) { + this._setLastReviewTime(latestReviewTime); + } },
🧹 Nitpick comments (4)
components/trustpilot/sources/new-service-reviews/new-service-reviews.mjs (2)
7-8
: Tighten description and verify data fields claimed.The description reads like marketing copy and asserts inclusion of “customer email” and use of the “private reviews API.” Please confirm both are true for all events; if unsure, tone down the claim.
- description: "Emit new event when a customer posts a new service review on Trustpilot. This source periodically polls the Trustpilot API to detect new service reviews using the private reviews API for comprehensive coverage. Each event contains the complete review data including star rating, review text, consumer details, business unit info, customer email, and timestamps. Ideal for monitoring overall business reputation, tracking customer satisfaction metrics, and triggering workflows based on review ratings or content.", + description: "Emits an event when a new Trustpilot service review is posted. Polls the Trustpilot API and includes key review fields (stars, text, consumer, business unit, timestamps).",
13-19
: Align consumerName fallback to displayName then name
Useconsumer?.displayName ?? consumer?.name ?? "Anonymous"
to match new-product-reviews and avoid dropping an existing name.- const consumerName = review.consumer?.displayName || "Anonymous"; + const consumerName = review.consumer?.displayName ?? review.consumer?.name ?? "Anonymous";components/trustpilot/sources/common/polling.mjs (2)
57-63
: Set a default page to complement perPage.Minor: establish
page: 1
in the default params to standardize child behavior unless overridden.getFetchParams(_lastReviewTime) { return { businessUnitId: this.businessUnitId, - perPage: 100, + perPage: 100, + page: 1, }; },
101-116
: Compare numeric timestamps to avoid redundant Date/ISO conversions.Reduces parsing overhead and avoids edge cases with string comparisons.
- // Track the latest review time - let latestReviewTime = lastReviewTime; + // Track the latest review time (numeric) + const lastTs = lastReviewTime ? Date.parse(lastReviewTime) : 0; + let latestTs = lastTs; @@ - // Track the latest review time - const reviewTime = new Date(review.createdAt).toISOString(); - if (!latestReviewTime || new Date(reviewTime) > new Date(latestReviewTime)) { - latestReviewTime = reviewTime; - } + // Track the latest review time + const reviewTs = Date.parse(review.createdAt); + if (Number.isFinite(reviewTs) && reviewTs > latestTs) { + latestTs = reviewTs; + } @@ - this.$emit(review, { + this.$emit(review, { id: review.id, summary: this.generateSummary(review), - ts: new Date(review.createdAt).getTime(), + ts: Number.isFinite(reviewTs) ? reviewTs : Date.now(), }); } @@ - if (latestReviewTime && latestReviewTime !== lastReviewTime) { - this._setLastReviewTime(latestReviewTime); - } + if (latestTs > lastTs) { + this._setLastReviewTime(new Date(latestTs).toISOString()); + }Also applies to: 119-122
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
components/trustpilot/sources/common/polling.mjs
(2 hunks)components/trustpilot/sources/new-product-reviews/new-product-reviews.mjs
(1 hunks)components/trustpilot/sources/new-service-reviews/new-service-reviews.mjs
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- components/trustpilot/sources/new-product-reviews/new-product-reviews.mjs
🧰 Additional context used
🧬 Code graph analysis (2)
components/trustpilot/sources/new-service-reviews/new-service-reviews.mjs (4)
components/trustpilot/sources/new-product-reviews/new-product-reviews.mjs (3)
stars
(14-14)consumerName
(15-15)businessUnit
(17-17)components/trustpilot/trustpilot.app.mjs (3)
businessUnit
(36-38)params
(125-125)params
(177-185)components/trustpilot/sources/common/polling.mjs (2)
lastReviewTime
(79-79)result
(85-85)components/trustpilot/actions/fetch-service-reviews/fetch-service-reviews.mjs (1)
result
(180-200)
components/trustpilot/sources/common/polling.mjs (6)
components/trustpilot/trustpilot.app.mjs (2)
reviews
(156-156)reviews
(215-215)components/trustpilot/sources/new-service-reviews/new-service-reviews.mjs (1)
result
(36-36)components/trustpilot/actions/fetch-service-reviews/fetch-service-reviews.mjs (1)
result
(180-200)components/trustpilot/actions/fetch-product-reviews/fetch-product-reviews.mjs (1)
result
(67-75)components/trustpilot/actions/fetch-service-review-by-id/fetch-service-review-by-id.mjs (1)
review
(48-48)components/trustpilot/actions/fetch-product-review-by-id/fetch-product-review-by-id.mjs (1)
review
(48-48)
⏰ 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: Publish TypeScript components
- GitHub Check: Verify TypeScript components
- GitHub Check: pnpm publish
- GitHub Check: Lint Code Base
🔇 Additional comments (4)
components/trustpilot/sources/new-service-reviews/new-service-reviews.mjs (1)
20-33
: Droppage
addition; defaults already applied
Downstream intrustpilot.app.mjs
the spread ofparams
is immediately followed bypage: 1
,perPage: 20
andorderBy: "createdat.desc"
, so addingpage
here is redundant and theorderBy
format is already correct.Likely an incorrect or invalid review comment.
components/trustpilot/sources/common/polling.mjs (3)
27-32
: State handling LGTM.Persisting a single ISO timestamp keeps the poller simple and robust.
33-41
: Clear contract for child summary.The explicit error helps enforce implementation. Good.
42-51
: Fetch hook contract LGTM.The enforced override and clear return shape are appropriate.
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: 5
♻️ Duplicate comments (5)
components/trustpilot/sources/new-service-reviews/new-service-reviews.mjs (1)
42-54
: Pagination logic fix looks good vs prior “total-length” bug.You now gate continuation on the last page size instead of the accumulated length. This resolves the earlier issue where only one extra page could be fetched.
components/trustpilot/actions/fetch-service-reviews/fetch-service-reviews.mjs (2)
23-30
: Page docs text looks goodThe page description matches current Trustpilot docs behavior (empty array when exceeding available pages). (developers.trustpilot.com)
178-200
: Good: orderBy/pagination are now respectedPassing page/perPage/orderBy through (without being overridden in the app) addresses the earlier issue.
components/trustpilot/trustpilot.app.mjs (2)
125-133
: LGTM: preserve user pagination/sort and exclude path param from queryDestructuring with defaults and spreading filters (excluding businessUnitId) fixes the prior override/leak.
221-230
: Fix product-reviews pagination: use total and detect next via links arrayThe private product reviews list returns productReviews, total, and a links array. Reading links.total and links.next is incorrect; compute hasMore by checking for a link with rel === "next".
- const pagination = { - total: response.links?.total || 0, - page: queryParams.page || 1, - perPage: queryParams.perPage || 20, - hasMore: response.links?.next - ? true - : false, - }; + const pagination = { + total: typeof response.total === "number" ? response.total : null, + page: queryParams.page || 1, + perPage: queryParams.perPage || 20, + hasMore: Array.isArray(response.links) + ? response.links.some((l) => l?.rel === "next") + : false, + };
🧹 Nitpick comments (8)
components/trustpilot/common/constants.mjs (1)
7-11
: Normalize endpoint key naming for consistency.Mixing SERVICE_* with PRIVATE_PRODUCT_* invites mistakes. Consider aligning both families (either drop PRIVATE_ or add it for service).
Example:
- SERVICE_REVIEWS: "/private/business-units/{businessUnitId}/reviews", - SERVICE_REVIEW_BY_ID: "/private/reviews/{reviewId}", - REPLY_TO_SERVICE_REVIEW: "/private/reviews/{reviewId}/reply", - PRIVATE_PRODUCT_REVIEWS: "/private/product-reviews/business-units/{businessUnitId}/reviews", - PRIVATE_PRODUCT_REVIEW_BY_ID: "/private/product-reviews/{reviewId}", + PRODUCT_REVIEWS: "/private/product-reviews/business-units/{businessUnitId}/reviews", + PRODUCT_REVIEW_BY_ID: "/private/product-reviews/{reviewId}",Apply corresponding symbol renames in the client.
Also applies to: 13-16
components/trustpilot/sources/new-service-reviews/new-service-reviews.mjs (1)
17-23
: Tweak summary grammar for singular “star”.- return `New ${stars}-star service review by ${consumerName} for ${businessUnit}`; + const starLabel = Number(stars) === 1 ? "star" : "stars"; + return `New ${stars}-${starLabel} service review by ${consumerName} for ${businessUnit}`;components/trustpilot/sources/new-product-reviews/new-product-reviews.mjs (3)
14-21
: Tweak summary grammar for singular “star”.- return `New ${stars}-star product review by ${consumerName} for "${productName}" (${businessUnit})`; + const starLabel = Number(stars) === 1 ? "star" : "stars"; + return `New ${stars}-${starLabel} product review by ${consumerName} for "${productName}" (${businessUnit})`;
49-55
: Harden lastReviewTime persistence against NaN.Guard against invalid inputs before writing to DB.
- const timeMs = typeof time === "string" - ? new Date(time).getTime() - : time; - this.db.set("lastReviewTime", timeMs); + const timeMs = typeof time === "string" ? new Date(time).getTime() : Number(time); + if (Number.isFinite(timeMs) && timeMs > 0) { + this.db.set("lastReviewTime", timeMs); + }
35-44
: Skip reviews with invalid createdAt to avoid NaN compare pitfalls.- const lastTs = Number(lastReviewTime) || 0; - const toMs = (d) => new Date(d).getTime(); - return lastTs - ? reviews.filter((r) => toMs(r.createdAt) > lastTs) - : reviews; + const lastTs = Number(lastReviewTime) || 0; + const toMs = (d) => { + const t = new Date(d).getTime(); + return Number.isFinite(t) ? t : -Infinity; + }; + return lastTs ? reviews.filter((r) => toMs(r.createdAt) > lastTs) : reviews;components/trustpilot/actions/fetch-service-reviews/fetch-service-reviews.mjs (2)
6-7
: Clarify description to reflect private fields and paginationNote that private Business Unit reviews include consumer email, referenceId, and referralEmail; docs also emphasize pagination via page/perPage. Consider expanding the description for accuracy and user expectation. (developers.trustpilot.com)
- description: "Get private reviews for a business unit. Response includes customer email and order ID. [See the documentation](https://developers.trustpilot.com/business-units-api#get-private-reviews-for-business-unit)", + description: "Get private reviews for a business unit (private endpoint). Response includes consumer email, referenceId, and referralEmail. Supports pagination via page/perPage. [Docs](https://developers.trustpilot.com/business-units-api#get-private-reviews-for-business-unit)",
31-36
: Normalize/validate stars to avoid bad inputsUsers may input spaces or invalid values; normalize to a comma-separated list of 1–5 before sending.
async run({ $ }) { const { businessUnitId, - stars, + stars, language, page = 1, internalLocationId, perPage = 20, orderBy = "createdat.desc", tagGroup, @@ - // Use the shared method from the app + // Normalize stars: keep only digits 1–5, comma-separated + const starsNormalized = typeof stars === "string" + ? stars + .split(",") + .map((s) => s.trim()) + .filter((v) => /^[1-5]$/.test(v)) + .join(",") + : stars; + + // Use the shared method from the app const result = await this.trustpilot.fetchServiceReviews($, { businessUnitId, - stars, + stars: starsNormalized, language, page, internalLocationId, perPage, orderBy,components/trustpilot/trustpilot.app.mjs (1)
195-197
: Optional: validate Business Unit ID here tooMirror the validation used in fetchServiceReviews to fail fast on malformed IDs.
if (!businessUnitId) { throw new Error("Business Unit ID is required"); } + if (!validateBusinessUnitId(businessUnitId)) { + throw new Error("Invalid business unit ID format"); + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (5)
components/trustpilot/actions/fetch-service-reviews/fetch-service-reviews.mjs
(2 hunks)components/trustpilot/common/constants.mjs
(1 hunks)components/trustpilot/sources/new-product-reviews/new-product-reviews.mjs
(1 hunks)components/trustpilot/sources/new-service-reviews/new-service-reviews.mjs
(1 hunks)components/trustpilot/trustpilot.app.mjs
(4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
components/trustpilot/trustpilot.app.mjs (2)
components/trustpilot/common/api-client.mjs (2)
response
(45-45)makeRequest
(17-47)components/trustpilot/common/utils.mjs (4)
validateBusinessUnitId
(218-224)buildUrl
(51-65)parseServiceReview
(157-211)parseProductReview
(107-150)
⏰ 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: Lint Code Base
- GitHub Check: Publish TypeScript components
- GitHub Check: Verify TypeScript components
🔇 Additional comments (2)
components/trustpilot/common/constants.mjs (1)
5-5
: No lingering endpoint references
Confirmed no callers reference dropped constants (BUSINESS_UNIT_BY_ID, public review endpoints, EMAIL_INVITATIONS, WEBHOOKS, CONVERSATIONS). The only usage ofENDPOINTS.BUSINESS_UNITS
is incomponents/trustpilot/trustpilot.app.mjs
with the expectedquery
andpage
params for/business-units/search
.components/trustpilot/trustpilot.app.mjs (1)
184-192
: Include productUrl or remove the prop (private product reviews)You define a productUrl prop but don’t pass it to fetchProductReviews. Also, Trustpilot’s private product reviews docs list sku, state, language, locale, page, perPage — productUrl isn’t documented there (it is for some public endpoints). Confirm support; if supported, include it; if not, remove the prop to avoid confusion.
- const { + const { businessUnitId, - page, - perPage, - sku, + page, + perPage, + sku, + productUrl, language, state, locale, } = params;If supported, add to query:
- const queryParams = { - sku, + const queryParams = { + sku, + productUrl, state, locale, perPage, page, - includeReportedReviews: false, language, };Docs references: private product reviews parameters (no productUrl listed), and private BU reviews response shape for context. (documentation-apidocumentation.trustpilot.com)
components/trustpilot/sources/new-product-reviews/new-product-reviews.mjs
Show resolved
Hide resolved
components/trustpilot/sources/new-service-reviews/new-service-reviews.mjs
Outdated
Show resolved
Hide resolved
components/trustpilot/sources/new-service-reviews/new-service-reviews.mjs
Show resolved
Hide resolved
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: 8
♻️ Duplicate comments (2)
components/trustpilot/trustpilot.app.mjs (2)
162-169
: Service-reviews pagination assumes a non-existent pagination blockUse links-based paging; total may be absent. Current logic can stall paging.
- const pagination = { - total: response.pagination?.total || 0, - page: response.pagination?.page || queryParams.page, - perPage: response.pagination?.perPage || queryParams.perPage, - hasMore: response.pagination?.hasMore || false, - }; + const pagination = { + total: typeof response.total === "number" ? response.total : null, + page: queryParams.page, + perPage: queryParams.perPage, + hasMore: Array.isArray(response.links) + ? response.links.some((l) => l?.rel === "next") + : false, + };
205-213
: Remove unsupported includeReportedReviews from product reviewsNot listed for the private product reviews list; can trigger 400s.
const queryParams = { sku, state, locale, perPage, page, - includeReportedReviews: false, language, };
🧹 Nitpick comments (6)
components/trustpilot/trustpilot.app.mjs (4)
19-33
: Harden options() defaults: trim/seed query and guard page to avoid NaNPrevents empty/whitespace queries and undefined page from producing invalid requests.
- async options({ - page, query, - }) { + async options({ page = 0, query } = {}) { try { - if (query === "") { - // Trustpilot requires a query to be passed in, default to "a" if empty - query = "a"; - } + // Trustpilot requires a non-empty query; seed with "a" if blank + query = (typeof query === "string" ? query.trim() : ""); + if (!query) query = "a"; const businessUnits = await this.searchBusinessUnits({ - // Trustpilot requires the page to be 1-indexed - // whereas pipedream is 0-indexed - page: page + 1, + // Trustpilot is 1-indexed; PD is 0-indexed + page: Number.isInteger(page) && page >= 0 ? page + 1 : 1, query, });
91-100
: State says default is Published but no default is setSet the default explicitly for consistency with the description.
state: { type: "string", label: "State", description: "Which reviews to retrieve according to their review state. Default is Published.", options: [ "published", "unpublished", ], + default: "published", optional: true, },
183-193
: Default page/perPage in fetchProductReviews to robust valuesEnsures stable behavior when callers don’t pass these props.
- const { - businessUnitId, - page, - perPage, + const { + businessUnitId, + page = 1, + perPage = 20, sku, language, state, locale, } = params;
221-228
: Avoid misleading 0 total; prefer null when total is absentMatches docs and prevents consumers from misinterpreting “0” as authoritative.
- const pagination = { - total: response.total || 0, + const pagination = { + total: typeof response.total === "number" ? response.total : null, page: queryParams.page || 1, perPage: queryParams.perPage || 20, hasMore: response.links?.some((l) => l.rel === "next") || false, };components/trustpilot/sources/common/polling.mjs (2)
6-7
: Doc says “deduplication” but base doesn’t dedupe by itself. Clarify to time-based semantics.- * Provides common functionality for polling Trustpilot API endpoints - * and emitting new events with deduplication. + * Provides common functionality for polling Trustpilot API endpoints + * and emitting new events using time-based deduplication via `lastReviewTime`. + * Child classes must either: + * - pass a time filter in `getFetchParams(lastReviewTime)`, or + * - implement `filterNewReviews(reviews, lastReviewTime)`.
87-90
: Use Pipedream logger instead of console for consistency.- console.log("No reviews found"); + ($.logger?.info ?? console.log)("No reviews found"); ... - console.log("No new reviews since last poll"); + ($.logger?.info ?? console.log)("No new reviews since last poll");Also applies to: 95-98
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (2)
components/trustpilot/sources/common/polling.mjs
(2 hunks)components/trustpilot/trustpilot.app.mjs
(4 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
components/trustpilot/trustpilot.app.mjs (4)
components/trustpilot/common/api-client.mjs (2)
response
(45-45)makeRequest
(17-47)components/trustpilot/common/constants.mjs (2)
ENDPOINTS
(3-20)ENDPOINTS
(3-20)components/trustpilot/common/utils.mjs (4)
validateBusinessUnitId
(218-224)buildUrl
(51-65)parseServiceReview
(157-211)parseProductReview
(107-150)components/trustpilot/sources/common/polling.mjs (1)
reviews
(85-85)
components/trustpilot/sources/common/polling.mjs (4)
components/trustpilot/trustpilot.app.mjs (2)
reviews
(163-163)reviews
(222-222)components/trustpilot/actions/fetch-service-reviews/fetch-service-reviews.mjs (1)
result
(180-200)components/trustpilot/sources/new-service-reviews/new-service-reviews.mjs (1)
result
(40-40)components/trustpilot/actions/fetch-product-reviews/fetch-product-reviews.mjs (1)
result
(67-75)
⏰ 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 (2)
components/trustpilot/trustpilot.app.mjs (1)
123-154
: Nice: user pagination/sort preserved and businessUnitId omitted from queryThis fixes prior overwriting/leakage and aligns with best practices.
components/trustpilot/sources/common/polling.mjs (1)
118-121
: LGTM: state update after emission ensures at-least-once delivery.
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: 3
♻️ Duplicate comments (6)
components/trustpilot/sources/new-service-reviews/new-service-reviews.mjs (1)
38-57
: Refactor pagination: don’t mutateparams
, base loop on last page size, and cap by MAX_LIMIT.Current loop mutates
params
and useswhile(true)
. Align with the robust pattern used in product reviews.- // Use the shared method from the app directly with pagination support - let result = await this.trustpilot.fetchServiceReviews($, params); - - // Handle pagination for service reviews - if (result.reviews && result.reviews.length === DEFAULT_LIMIT) { - while (true) { - params.page = (params.page || 1) + 1; - const nextResult = await this.trustpilot.fetchServiceReviews($, params); - result.reviews = result.reviews.concat(nextResult.reviews || []); - - if (!nextResult.reviews || - nextResult.reviews.length < DEFAULT_LIMIT || - result.reviews.length >= MAX_LIMIT) { - break; - } - } - } - - return result; + const perPage = params.perPage ?? DEFAULT_LIMIT; + let page = params.page ?? 1; + let result = await this.trustpilot.fetchServiceReviews($, { ...params, page }); + let all = Array.isArray(result.reviews) ? result.reviews : []; + let lastPageSize = all.length; + while (lastPageSize === perPage && all.length < MAX_LIMIT) { + page += 1; + const next = await this.trustpilot.fetchServiceReviews($, { ...params, page }); + const chunk = Array.isArray(next.reviews) ? next.reviews : []; + if (chunk.length === 0) break; + all = all.concat(chunk); + lastPageSize = chunk.length; + result = next; // preserve latest metadata if needed + } + result.reviews = all.slice(0, MAX_LIMIT); + return result;components/trustpilot/sources/common/polling.mjs (1)
128-133
: Add missingcreatedAt
inparseServiceReview
return
parseServiceReview (components/trustpilot/common/utils.mjs) currently returns only{ links, id }
, omittingcreatedAt
and causingcreatedMs
in polling.mjs to be undefined. AddcreatedAt: review.createdAt
to its returned object.components/trustpilot/trustpilot.app.mjs (4)
119-127
: Good: preserves caller pagination/sort and omits businessUnitId from queryDestructuring with defaults and filtering out
businessUnitId
addresses earlier bugs. Looks correct.Also applies to: 141-148
194-200
: Good: validates businessUnitId format for product reviewsFast-fail guard mirrors service reviews. Nice.
223-230
: Product reviews pagination logic looks correctUses top-level
total
and checkslinks
forrel === "next"
. This aligns with the documented response.
156-169
: Fix hasMore detection: rel should be "next", not "next-page"Trustpilot links use
rel: "next"
. Using"next-page"
will stall pagination.- hasMore: Array.isArray(response.links) - ? response.links.some((l) => l?.rel === "next-page") - : false, + hasMore: Array.isArray(response.links) + ? response.links.some((l) => l?.rel === "next") + : false,
🧹 Nitpick comments (7)
components/trustpilot/sources/new-service-reviews/new-service-reviews.mjs (2)
17-23
: Harden summary fallbacks (support both displayName and name).Service and product parsers may emit either
displayName
orname
. Use both before defaulting.- const consumerName = review.consumer?.displayName || "Anonymous"; + const consumerName = review.consumer?.displayName + || review.consumer?.name + || "Anonymous";
24-36
: Initializepage
in params for deterministic paging.Explicitly set
page: 1
to avoid relying on server defaults.const params = { businessUnitId: this.businessUnitId, perPage: DEFAULT_LIMIT, orderBy: "createdat.desc", + page: 1, };
components/trustpilot/sources/new-product-reviews/new-product-reviews.mjs (1)
17-24
: Stars fallback: supportrating
ifstars
absent.Some parsers/flows emit
rating
. Add a nullish-coalescing fallback.- const stars = review.stars || "N/A"; + const stars = review.stars ?? review.rating ?? "N/A";components/trustpilot/sources/common/polling.mjs (2)
56-66
: Doc: acceptstring | number
for time.Children (e.g., product reviews) use numeric ms. Reflect this in JSDoc.
- * @param {string} _lastReviewTime - ISO timestamp of last review + * @param {string|number} _lastReviewTime - Last review time (ISO string or ms)
97-99
: Use $.logger for consistency.See diff in the previous comment replacing
console.log
with$.logger?.info
.Also applies to: 105-107
components/trustpilot/trustpilot.app.mjs (2)
19-33
: Harden async options: default page and avoid mutating queryGuard against undefined/negative page and sanitize query locally. Also pass $ through so downstream requests can use it.
Apply:
- async options({ - page, query, - }) { + async options({ page = 0, query, $ }) { try { - if (query === "") { - // Trustpilot requires a query to be passed in, default to "a" if empty - query = "a"; - } + const q = (query || "").trim() || "a"; // Trustpilot requires non-empty query const businessUnits = await this.searchBusinessUnits( - { - // Trustpilot requires the page to be 1-indexed - // whereas pipedream is 0-indexed - page: page + 1, - query, - }); + $, { + // Trustpilot pages are 1-indexed; Pipedream is 0-indexed + page: Math.max(1, Number(page) + 1), + query: q, + }, + );Also applies to: 35-44
85-94
: Set a default to match the description (“Default is Published.”)The prop says default is Published but no default is set.
state: { type: "string", label: "State", description: "Which reviews to retrieve according to their review state. Default is Published.", options: [ "published", "unpublished", ], + default: "published", optional: true, },
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (4)
components/trustpilot/sources/common/polling.mjs
(2 hunks)components/trustpilot/sources/new-product-reviews/new-product-reviews.mjs
(1 hunks)components/trustpilot/sources/new-service-reviews/new-service-reviews.mjs
(1 hunks)components/trustpilot/trustpilot.app.mjs
(4 hunks)
⏰ 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: Verify TypeScript components
- GitHub Check: Publish TypeScript components
- GitHub Check: Lint Code Base
- GitHub Check: pnpm publish
🔇 Additional comments (5)
components/trustpilot/sources/new-product-reviews/new-product-reviews.mjs (3)
25-33
: LGTM on fetch params.Clear defaults with explicit paging. Matches the contract.
34-68
: LGTM on robust pagination.Good use of
perPage
, last-page-size loop, and MAX_LIMIT cap.
69-78
: Client-side filter looks good.Numeric comparison avoids string/timezone pitfalls.
components/trustpilot/sources/common/polling.mjs (2)
28-33
: LGTM: BU-namespaced state.Prevents cross-configuration data mixups.
69-83
: Default filter is sensible.Numeric parse with strict
>
avoids duplicate emissions.
async run({ $ }) { | ||
// Get the last review time for filtering new reviews | ||
const lastReviewTime = this._getLastReviewTime(); | ||
|
||
// Get fetch parameters from child class | ||
const fetchParams = this.getFetchParams(lastReviewTime); | ||
|
||
// Fetch reviews using child class method | ||
const result = await this.fetchReviews($, fetchParams); | ||
const reviews = result.reviews || []; | ||
|
||
if (!reviews.length) { | ||
console.log("No reviews found"); | ||
return; | ||
} | ||
|
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: mixed string/number handling can stall lastReviewTime updates.
Date.parse(latestReviewTime)
breaks when latestReviewTime
is a number (product flow), preventing advancement. Normalize to numbers for comparison, then persist ISO.
async run({ $ }) {
// Get the last review time for filtering new reviews
- const lastReviewTime = this._getLastReviewTime();
+ const lastReviewTime = this._getLastReviewTime();
+ const lastMsRaw = typeof lastReviewTime === "number"
+ ? lastReviewTime
+ : Date.parse(lastReviewTime);
+ const lastMs = Number.isFinite(lastMsRaw) ? lastMsRaw : 0;
@@
- if (!reviews.length) {
- console.log("No reviews found");
+ if (!reviews.length) {
+ ($.logger?.info ?? console.log)("No reviews found");
return;
}
@@
- if (!newReviews.length) {
- console.log("No new reviews since last poll");
+ if (!newReviews.length) {
+ ($.logger?.info ?? console.log)("No new reviews since last poll");
return;
}
// Track the latest review time
- let latestReviewTime = lastReviewTime;
+ let latestMs = lastMs;
for (const review of newReviews) {
// Track the latest review time
const createdMs = Date.parse(review?.createdAt);
if (!Number.isFinite(createdMs)) {
($.logger?.warn ?? console.warn)("Skipping review with invalid createdAt", {
id: review?.id,
createdAt: review?.createdAt,
});
continue;
}
- const reviewTime = new Date(createdMs).toISOString();
- if (!latestReviewTime || createdMs > Date.parse(latestReviewTime)) {
- latestReviewTime = reviewTime;
- }
+ if (createdMs > latestMs) latestMs = createdMs;
// Emit the review with unique ID and summary
this.$emit(review, {
id: review.id,
summary: this.generateSummary(review),
ts: createdMs,
});
}
// Update the last review time for next poll
- if (latestReviewTime && latestReviewTime !== lastReviewTime) {
- this._setLastReviewTime(latestReviewTime);
+ if (latestMs && latestMs !== lastMs) {
+ this._setLastReviewTime(new Date(latestMs).toISOString());
}
},
Also applies to: 105-109, 110-139
_getLastReviewTime() { | ||
// Product reviews store timestamp as number (ms), others store as ISO string | ||
return this.db.get("lastReviewTime"); | ||
}, | ||
_setLastReviewTime(time) { | ||
// Store as number for product reviews to match existing behavior | ||
const timeMs = typeof time === "string" | ||
? new Date(time).getTime() | ||
: time; | ||
this.db.set("lastReviewTime", timeMs); |
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.
Bug: lastReviewTime not namespaced by Business Unit (cross-BU bleed).
Overriding the base methods drops the BU namespace, causing data loss when users switch BUs.
- _getLastReviewTime() {
- // Product reviews store timestamp as number (ms), others store as ISO string
- return this.db.get("lastReviewTime");
- },
- _setLastReviewTime(time) {
- // Store as number for product reviews to match existing behavior
- const timeMs = typeof time === "string"
- ? new Date(time).getTime()
- : time;
- this.db.set("lastReviewTime", timeMs);
- },
+ _getLastReviewTime() {
+ // Product reviews: store as number (ms), namespaced by BU
+ return this.db.get(`lastReviewTime:${this.businessUnitId}`);
+ },
+ _setLastReviewTime(time) {
+ const timeMs = typeof time === "string" ? new Date(time).getTime() : time;
+ this.db.set(`lastReviewTime:${this.businessUnitId}`, timeMs);
+ },
📝 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.
_getLastReviewTime() { | |
// Product reviews store timestamp as number (ms), others store as ISO string | |
return this.db.get("lastReviewTime"); | |
}, | |
_setLastReviewTime(time) { | |
// Store as number for product reviews to match existing behavior | |
const timeMs = typeof time === "string" | |
? new Date(time).getTime() | |
: time; | |
this.db.set("lastReviewTime", timeMs); | |
_getLastReviewTime() { | |
// Product reviews: store as number (ms), namespaced by BU | |
return this.db.get(`lastReviewTime:${this.businessUnitId}`); | |
}, | |
_setLastReviewTime(time) { | |
const timeMs = typeof time === "string" ? new Date(time).getTime() : time; | |
this.db.set(`lastReviewTime:${this.businessUnitId}`, timeMs); | |
}, |
🤖 Prompt for AI Agents
In components/trustpilot/sources/new-product-reviews/new-product-reviews.mjs
around lines 79–88, the overridden _getLastReviewTime/_setLastReviewTime drop
the Business Unit (BU) namespace causing cross-BU data bleed; change both
methods to read/write the lastReviewTime under a BU-scoped key (e.g. prepend or
otherwise include this.businessUnit or this.businessUnitId in the key used with
this.db.get/this.db.set) and keep the existing timestamp-to-ms conversion logic
in _setLastReviewTime.
async searchBusinessUnits({ | ||
query = "", limit = DEFAULT_LIMIT, offset = 0, | ||
query = "a", page = 1, | ||
} = {}) { | ||
const response = await this._makeRequest({ | ||
const response = await makeRequest(this, this, { | ||
endpoint: ENDPOINTS.BUSINESS_UNITS, | ||
params: { | ||
query, | ||
limit, | ||
offset, | ||
page, | ||
}, | ||
}); | ||
|
||
return response.businessUnits?.map(parseBusinessUnit) || []; | ||
return response.businessUnits || []; | ||
}, |
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.
💡 Verification agent
🧩 Analysis chain
Fix makeRequest call: passing this
as $
is likely wrong
Other methods use makeRequest($, this, …)
. Here it’s makeRequest(this, this, …)
, which can break HTTP calls (missing $.http/axios). Accept $
and pass it through.
- async searchBusinessUnits({
- query = "a", page = 1,
- } = {}) {
- const response = await makeRequest(this, this, {
+ async searchBusinessUnits($, { query = "a", page = 1 } = {}) {
+ const response = await makeRequest($, this, {
endpoint: ENDPOINTS.BUSINESS_UNITS,
params: {
query,
page,
},
});
Run to find any remaining inconsistent invocations:
🏁 Script executed:
#!/bin/bash
rg -nP --type=js -C2 '\bmakeRequest\s*\(' components/trustpilot
Length of output: 65
🏁 Script executed:
#!/bin/bash
# List all async method signatures in trustpilot.app.mjs
rg -nP -n -C3 'async\s+\w+' -g 'components/trustpilot/*.mjs'
# List all makeRequest calls in trustpilot.app.mjs
rg -nP -n -C3 'makeRequest' -g 'components/trustpilot/*.mjs'
Length of output: 3895
Fix makeRequest invocation in searchBusinessUnits
In components/trustpilot/trustpilot.app.mjs, update the searchBusinessUnits method to accept $
and call makeRequest($, this, …)
so the correct HTTP client is used.
- async searchBusinessUnits({ query = "a", page = 1 } = {}) {
- const response = await makeRequest(this, this, {
+ async searchBusinessUnits($, { query = "a", page = 1 } = {}) {
+ const response = await makeRequest($, this, {
📝 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.
async searchBusinessUnits({ | |
query = "", limit = DEFAULT_LIMIT, offset = 0, | |
query = "a", page = 1, | |
} = {}) { | |
const response = await this._makeRequest({ | |
const response = await makeRequest(this, this, { | |
endpoint: ENDPOINTS.BUSINESS_UNITS, | |
params: { | |
query, | |
limit, | |
offset, | |
page, | |
}, | |
}); | |
return response.businessUnits?.map(parseBusinessUnit) || []; | |
return response.businessUnits || []; | |
}, | |
async searchBusinessUnits($, { query = "a", page = 1 } = {}) { | |
const response = await makeRequest($, this, { | |
endpoint: ENDPOINTS.BUSINESS_UNITS, | |
params: { | |
query, | |
page, | |
}, | |
}); | |
return response.businessUnits || []; | |
}, |
🤖 Prompt for AI Agents
In components/trustpilot/trustpilot.app.mjs around lines 103 to 115, update the
searchBusinessUnits method signature to accept the HTTP client parameter $ (e.g.
async searchBusinessUnits($, { query = "a", page = 1 } = {})), and change the
makeRequest call to pass $ as the first argument (makeRequest($, this, {
endpoint: ENDPOINTS.BUSINESS_UNITS, params: { query, page } })). This ensures
the correct HTTP client is used while keeping the default params behavior.
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.
Moving the issue forward to QA. I left one comment that may not need to be acted upon, just checked.
requestTime: new Date().toISOString(), | ||
}, | ||
}; | ||
} catch (error) { |
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.
There's still a few try/catch blocks being added that may not be desirable (it's usually preferable to let platform axios handle the error and display the appropriate info in the UI).
If they are intentional, just reply to this confirming so, no problem
Hi everyone, all test cases are passed! Ready for release! Test report |
* trustpilot fixes * more fixes * update versions * more version updates * fixes * Bump all Trustpilot actions to version 0.1.0 Major improvements and API updates across all actions: - Enhanced private API support with proper authentication - Improved parameter handling and validation - Better error handling and response structures - Added new conversation flow for product reviews - Fixed endpoint URLs to match latest API documentation - Streamlined request/response processing 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * up version and clean up sources * merge * fix business ID * delete temp action * Update components/trustpilot/sources/new-product-reviews/new-product-reviews.mjs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Update components/trustpilot/sources/new-product-reviews/new-product-reviews.mjs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Update components/trustpilot/sources/new-product-reviews/new-product-reviews.mjs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Update components/trustpilot/sources/new-service-reviews/new-service-reviews.mjs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * comments * Pagination * fixes * comments * missed some `$`'s * unduplicated * more fixes * final comments * more comments * . --------- Co-authored-by: Claude <[email protected]> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Leonardo AI components * added unzoom image action * fixing link errors * more lint fixes * Merging pull request #18359 * fix: pagination prop and params struct * fix: no need for paginate here * chore: update version * chore: cleanup * chore: update package * feat: allow raw response * chore: bump package * fix: buffer response instead * Update components/google_drive/actions/download-file/download-file.mjs Co-authored-by: Jorge Cortes <[email protected]> * versions * pnpm-lock.yaml * pnpm-lock.yaml * pnpm-lock.yaml * feat: add content selector * chore: bump package * fix: comments * chore: bump versions * chore: fix versions * fixes: QA fixes * feat: add cursor to req * package.json --------- Co-authored-by: joao <[email protected]> Co-authored-by: joaocoform <[email protected]> Co-authored-by: Jorge Cortes <[email protected]> Co-authored-by: Michelle Bergeron <[email protected]> Co-authored-by: Luan Cazarine <[email protected]> * Merging pull request #18361 * update siteId prop * pnpm-lock.yaml * package.json version * Google Sheets - update row refresh fields (#18369) * change prop order and refresh fields * bump package.json * Pipedrive - fix app name (#18370) * use pipedriveApp instead of app * bump package.json * Pipedrive - pipelineId integer (#18372) * pipelineId - integer * bump versions * Adding app scaffolding for lightspeed_ecom_c_series * Adding app scaffolding for financial_data * Adding app scaffolding for microsoft_authenticator * Merging pull request #18345 * updates * versions * versions * Merging pull request #18368 * updates * remove console.log * versions * Coinbase Developer Platform - New Wallet Event (#18342) * new component * pnpm-lock.yaml * updates * updates * Hubspot - update search-crm (#18360) * update search-crm * limit results to one page * update version * package.json version * Merging pull request #18347 * widget props * fix version * Adding app scaffolding for rundeck * Merging pull request #18378 * Update Taiga component with new actions and sources - Bump version to 0.1.0 in package.json and add dependency on @pipedream/platform. - Introduce new actions for creating, updating, and deleting issues, tasks, and user stories. - Add sources for tracking changes and deletions of issues and tasks. - Implement utility functions for parsing and cleaning objects in common/utils.mjs. - Enhance prop definitions for better integration with Taiga API. * pnpm update * Refactor Taiga actions to utilize parseObject utility - Added parseObject utility for tags, watchers, and points in update-issue, update-task, and update-userstory actions. - Removed the update-project action as it is no longer needed. - Enhanced base source to include secret key validation for webhook security. * Merging pull request #18382 * add testSources prop * pnpm-lock.yaml * fix * Merging pull request #18323 * Added actions * Added actions * Added actions * Merging pull request #18377 * Added actions * Update components/weaviate/actions/create-class/create-class.mjs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --------- Co-authored-by: Luan Cazarine <[email protected]> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Merging pull request #18376 * Adding app scaffolding for etrusted * Adding app scaffolding for intelliflo_office * Adding app scaffolding for thoughtspot * Adding app scaffolding for kordiam * Adding app scaffolding for ticketsauce * trustpilot fixes (#18152) * trustpilot fixes * more fixes * update versions * more version updates * fixes * Bump all Trustpilot actions to version 0.1.0 Major improvements and API updates across all actions: - Enhanced private API support with proper authentication - Improved parameter handling and validation - Better error handling and response structures - Added new conversation flow for product reviews - Fixed endpoint URLs to match latest API documentation - Streamlined request/response processing 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * up version and clean up sources * merge * fix business ID * delete temp action * Update components/trustpilot/sources/new-product-reviews/new-product-reviews.mjs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Update components/trustpilot/sources/new-product-reviews/new-product-reviews.mjs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Update components/trustpilot/sources/new-product-reviews/new-product-reviews.mjs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Update components/trustpilot/sources/new-service-reviews/new-service-reviews.mjs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * comments * Pagination * fixes * comments * missed some `$`'s * unduplicated * more fixes * final comments * more comments * . --------- Co-authored-by: Claude <[email protected]> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Adding app scaffolding for peekalink * 18314 twilio (#18350) * Update Twilio component versions and dependencies - Update Twilio Send Message action adding detailed description for 'from' prop and refactoring phone number validation logic. - Incremented action versions for several Twilio actions. * pnpm update * Updating LinkedIn API version (#18399) * Merging pull request #18394 * Databricks API - Jobs action components (#18371) * Notion property building improvements (#18381) * validate property types * versions * Google Business - add debug log (#18407) * add debug log * bump versions * Notion API Key - update @pipedream/notion version (#18409) * update @pipedream/notion dependency version * pnpm-lock.yaml * Adding app scaffolding for reduct_video * Adding app scaffolding for shopware * Adding app scaffolding for instamojo * Hubspot - bug fix to sources w/ property changes (#18379) * updates * versions * Google sheets type fix (#18411) * Fixing worksheetId prop type from string to integer * Version bumps --------- Co-authored-by: Leo Vu <[email protected]> * Merging pull request #18393 * new components * remove console.log * versions * update * Merging pull request #18408 * 403 error message * versions * update * Merging pull request #18419 * Changes per PR Review * Removes leonardo_ai_actions.mdc not indented for merging * synced lockfile after install * fully lock form-data for leonardo_ai * conflict solving * lint fixes * Chipped down Readme, implemented async options in gen motion --------- Co-authored-by: jocarino <[email protected]> Co-authored-by: joao <[email protected]> Co-authored-by: joaocoform <[email protected]> Co-authored-by: Jorge Cortes <[email protected]> Co-authored-by: Michelle Bergeron <[email protected]> Co-authored-by: Luan Cazarine <[email protected]> Co-authored-by: michelle0927 <[email protected]> Co-authored-by: Andrew Chuang <[email protected]> Co-authored-by: danhsiung <[email protected]> Co-authored-by: Lucas Caresia <[email protected]> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Job <[email protected]> Co-authored-by: Claude <[email protected]> Co-authored-by: Guilherme Falcão <[email protected]> Co-authored-by: Leo Vu <[email protected]>
WHY
Fixes a bunch of issues with the Trustpilot integration
Summary by CodeRabbit
New Features
Enhancements
Refactor
Removed
Chores