Skip to content

Commit 6bb8e54

Browse files
committed
more comments
1 parent 4ed892e commit 6bb8e54

File tree

4 files changed

+83
-28
lines changed

4 files changed

+83
-28
lines changed

components/trustpilot/sources/common/polling.mjs

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import trustpilot from "../../trustpilot.app.mjs";
2+
import { DEFAULT_LIMIT } from "../../common/constants.mjs";
23

34
/**
45
* Base polling source for Trustpilot integration
@@ -25,10 +26,10 @@ export default {
2526
},
2627
methods: {
2728
_getLastReviewTime() {
28-
return this.db.get("lastReviewTime");
29+
return this.db.get(`lastReviewTime:${this.businessUnitId}`);
2930
},
3031
_setLastReviewTime(time) {
31-
this.db.set("lastReviewTime", time);
32+
this.db.set(`lastReviewTime:${this.businessUnitId}`, time);
3233
},
3334
/**
3435
* Override in child classes to provide review type-specific summary
@@ -40,10 +41,13 @@ export default {
4041
throw new Error("generateSummary must be implemented in child class");
4142
},
4243
/**
43-
* Override in child classes to fetch reviews using appropriate method
44+
* Override in child classes to fetch reviews.
45+
* Requirements:
46+
* - Must return ALL reviews newer than `lastReviewTime` (handle pagination internally), or
47+
* - Return the first page AND expose a pagination cursor so the base can iterate (future).
4448
* @param {Object} _$ - Pipedream step context
45-
* @param {Object} _params - Fetch parameters
46-
* @returns {Object} - API response with reviews array
49+
* @param {Object} _params - Fetch parameters produced by `getFetchParams(lastReviewTime)`
50+
* @returns {{ reviews: Array }} - Array of normalized reviews
4751
*/
4852
// eslint-disable-next-line no-unused-vars
4953
async fetchReviews(_$, _params) {
@@ -58,7 +62,7 @@ export default {
5862
getFetchParams(_lastReviewTime) {
5963
return {
6064
businessUnitId: this.businessUnitId,
61-
perPage: 100,
65+
perPage: DEFAULT_LIMIT,
6266
};
6367
},
6468
/**
@@ -69,8 +73,14 @@ export default {
6973
*/
7074
// eslint-disable-next-line no-unused-vars
7175
filterNewReviews(reviews, _lastReviewTime) {
72-
// Default: return all reviews (for APIs with server-side time filtering)
73-
return reviews;
76+
if (!_lastReviewTime) return reviews;
77+
const lastMs = Date.parse(_lastReviewTime);
78+
if (Number.isNaN(lastMs)) return reviews;
79+
80+
return reviews.filter((r) => {
81+
const ms = Date.parse(r?.createdAt);
82+
return Number.isFinite(ms) && ms > lastMs;
83+
});
7484
},
7585
},
7686
async run({ $ }) {
@@ -102,16 +112,24 @@ export default {
102112

103113
for (const review of newReviews) {
104114
// Track the latest review time
105-
const reviewTime = new Date(review.createdAt).toISOString();
106-
if (!latestReviewTime || new Date(reviewTime) > new Date(latestReviewTime)) {
115+
const createdMs = Date.parse(review?.createdAt);
116+
if (!Number.isFinite(createdMs)) {
117+
($.logger?.warn ?? console.warn)("Skipping review with invalid createdAt", {
118+
id: review?.id,
119+
createdAt: review?.createdAt,
120+
});
121+
continue;
122+
}
123+
const reviewTime = new Date(createdMs).toISOString();
124+
if (!latestReviewTime || createdMs > Date.parse(latestReviewTime)) {
107125
latestReviewTime = reviewTime;
108126
}
109127

110128
// Emit the review with unique ID and summary
111129
this.$emit(review, {
112130
id: review.id,
113131
summary: this.generateSummary(review),
114-
ts: new Date(review.createdAt).getTime(),
132+
ts: createdMs,
115133
});
116134
}
117135

components/trustpilot/sources/new-product-reviews/new-product-reviews.mjs

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import common from "../common/polling.mjs";
2-
import { DEFAULT_LIMIT } from "../../common/constants.mjs";
2+
import {
3+
DEFAULT_LIMIT,
4+
MAX_LIMIT,
5+
} from "../../common/constants.mjs";
36

47
export default {
58
...common,
@@ -29,8 +32,39 @@ export default {
2932
};
3033
},
3134
async fetchReviews($, params) {
32-
// Use the shared method from the app directly
33-
return await this.trustpilot.fetchProductReviews($, params);
35+
const perPage = params.perPage ?? DEFAULT_LIMIT;
36+
let page = params.page ?? 1;
37+
38+
// fetch first page
39+
let result = await this.trustpilot.fetchProductReviews($, {
40+
...params,
41+
page,
42+
});
43+
let all = Array.isArray(result.reviews)
44+
? result.reviews
45+
: [];
46+
let lastPageSize = all.length;
47+
48+
// keep paging while we get a full page and stay under MAX_LIMIT
49+
while (lastPageSize === perPage && all.length < MAX_LIMIT) {
50+
page += 1;
51+
const next = await this.trustpilot.fetchProductReviews($, {
52+
...params,
53+
page,
54+
});
55+
const chunk = Array.isArray(next.reviews) ?
56+
next.reviews :
57+
[];
58+
if (chunk.length === 0) break;
59+
60+
all = all.concat(chunk);
61+
lastPageSize = chunk.length;
62+
result = next; // preserve any metadata from the latest fetch
63+
}
64+
65+
// truncate to MAX_LIMIT in case there are more than allowed
66+
result.reviews = all.slice(0, MAX_LIMIT);
67+
return result;
3468
},
3569
filterNewReviews(reviews, lastReviewTime) {
3670
// Product reviews require client-side filtering since API doesn't support

components/trustpilot/sources/new-service-reviews/new-service-reviews.mjs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export default {
88
...common,
99
key: "trustpilot-new-service-reviews",
1010
name: "New Service Reviews",
11-
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.",
11+
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.",
1212
version: "0.1.0",
1313
type: "source",
1414
dedupe: "unique",
@@ -46,8 +46,9 @@ export default {
4646
const nextResult = await this.trustpilot.fetchServiceReviews($, params);
4747
result.reviews = result.reviews.concat(nextResult.reviews || []);
4848

49-
if ((nextResult.reviews && nextResult.reviews.length < DEFAULT_LIMIT)
50-
|| result.reviews.length >= MAX_LIMIT) {
49+
if (!nextResult.reviews ||
50+
nextResult.reviews.length < DEFAULT_LIMIT ||
51+
result.reviews.length >= MAX_LIMIT) {
5152
break;
5253
}
5354
}

components/trustpilot/trustpilot.app.mjs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,6 @@ export default {
5959
description: "Filter by SKU",
6060
optional: true,
6161
},
62-
productUrl: {
63-
type: "string",
64-
label: "Product URL",
65-
description: "Filter by product URL",
66-
optional: true,
67-
},
6862
page: {
6963
type: "integer",
7064
label: "Page",
@@ -162,10 +156,16 @@ export default {
162156
// Handle the correct response structure (reviews array)
163157
const reviews = response.reviews?.map(parseServiceReview) || [];
164158
const pagination = {
165-
total: response.pagination?.total || 0,
166-
page: response.pagination?.page || queryParams.page,
167-
perPage: response.pagination?.perPage || queryParams.perPage,
168-
hasMore: response.pagination?.hasMore || false,
159+
total: typeof response.total === "number"
160+
? response.total :
161+
null,
162+
// Preserve the page and perPage we requested
163+
page: queryParams.page,
164+
perPage: queryParams.perPage,
165+
// Determine if there’s a next page by checking for a "next" link
166+
hasMore: Array.isArray(response.links)
167+
? response.links.some((l) => l?.rel === "next-page")
168+
: false,
169169
};
170170

171171
return {
@@ -195,6 +195,9 @@ export default {
195195
if (!businessUnitId) {
196196
throw new Error("Business Unit ID is required");
197197
}
198+
if (!validateBusinessUnitId(businessUnitId)) {
199+
throw new Error("Invalid business unit ID format");
200+
}
198201

199202
// Build the endpoint URL
200203
const endpoint = buildUrl(ENDPOINTS.PRIVATE_PRODUCT_REVIEWS, {
@@ -208,7 +211,6 @@ export default {
208211
locale,
209212
perPage,
210213
page,
211-
includeReportedReviews: false,
212214
language,
213215
};
214216

0 commit comments

Comments
 (0)