11import  {  defineApp  }  from  "@pipedream/types" ; 
2+ import  {  axios  }  from  "@pipedream/platform" ; 
3+ import  {  
4+   BASE_URL ,  
5+   ENDPOINTS ,  
6+   DEFAULT_LIMIT ,  
7+   MAX_LIMIT ,  
8+   SORT_OPTIONS , 
9+   RATING_SCALE , 
10+   RETRY_CONFIG , 
11+   HTTP_STATUS , 
12+ }  from  "../common/constants.mjs" ; 
13+ import  {  
14+   buildUrl ,  
15+   parseReview ,  
16+   parseBusinessUnit ,  
17+   parseWebhookPayload , 
18+   validateBusinessUnitId , 
19+   validateReviewId , 
20+   formatQueryParams , 
21+   parseApiError , 
22+   sleep , 
23+ }  from  "../common/utils.mjs" ; 
224
325export  default  defineApp ( { 
426  type : "app" , 
527  app : "trustpilot" , 
6-   propDefinitions : { } , 
28+   propDefinitions : { 
29+     businessUnitId : { 
30+       type : "string" , 
31+       label : "Business Unit ID" , 
32+       description : "The unique identifier for your business unit on Trustpilot" , 
33+       async  options ( )  { 
34+         try  { 
35+           const  businessUnits  =  await  this . searchBusinessUnits ( { 
36+             query : "" , 
37+             limit : 20 , 
38+           } ) ; 
39+           return  businessUnits . map ( unit  =>  ( { 
40+             label : unit . displayName , 
41+             value : unit . id , 
42+           } ) ) ; 
43+         }  catch  ( error )  { 
44+           console . error ( "Error fetching business units:" ,  error ) ; 
45+           return  [ ] ; 
46+         } 
47+       } , 
48+     } , 
49+     reviewId : { 
50+       type : "string" , 
51+       label : "Review ID" , 
52+       description : "The unique identifier for a review" , 
53+     } , 
54+     stars : { 
55+       type : "integer" , 
56+       label : "Star Rating" , 
57+       description : "Filter by star rating (1-5)" , 
58+       options : RATING_SCALE , 
59+       optional : true , 
60+     } , 
61+     sortBy : { 
62+       type : "string" , 
63+       label : "Sort By" , 
64+       description : "How to sort the results" , 
65+       options : Object . entries ( SORT_OPTIONS ) . map ( ( [ key ,  value ] )  =>  ( { 
66+         label : key . replace ( / _ / g,  " " ) . toLowerCase ( ) , 
67+         value, 
68+       } ) ) , 
69+       optional : true , 
70+       default : SORT_OPTIONS . CREATED_AT_DESC , 
71+     } , 
72+     limit : { 
73+       type : "integer" , 
74+       label : "Limit" , 
75+       description : "Maximum number of results to return" , 
76+       min : 1 , 
77+       max : MAX_LIMIT , 
78+       default : DEFAULT_LIMIT , 
79+       optional : true , 
80+     } , 
81+     includeReportedReviews : { 
82+       type : "boolean" , 
83+       label : "Include Reported Reviews" , 
84+       description : "Whether to include reviews that have been reported" , 
85+       default : false , 
86+       optional : true , 
87+     } , 
88+     tags : { 
89+       type : "string[]" , 
90+       label : "Tags" , 
91+       description : "Filter reviews by tags" , 
92+       optional : true , 
93+     } , 
94+     language : { 
95+       type : "string" , 
96+       label : "Language" , 
97+       description : "Filter reviews by language (ISO 639-1 code)" , 
98+       optional : true , 
99+     } , 
100+   } , 
7101  methods : { 
8-     // this.$auth contains connected account data 
102+     // Authentication and base request methods 
103+     _getAuthHeaders ( )  { 
104+       const  headers  =  { 
105+         "Content-Type" : "application/json" , 
106+         "User-Agent" : "Pipedream/1.0" , 
107+       } ; 
108+ 
109+       if  ( this . $auth ?. api_key )  { 
110+         headers [ "apikey" ]  =  this . $auth . api_key ; 
111+       } 
112+ 
113+       if  ( this . $auth ?. oauth_access_token )  { 
114+         headers [ "Authorization" ]  =  `Bearer ${ this . $auth . oauth_access_token }  ` ; 
115+       } 
116+ 
117+       return  headers ; 
118+     } , 
119+ 
120+     async  _makeRequest ( {  endpoint,  method =  "GET" ,  params =  { } ,  data =  null ,  ...args  } )  { 
121+       const  url  =  `${ BASE_URL } ${ endpoint }  ` ; 
122+       const  headers  =  this . _getAuthHeaders ( ) ; 
123+       
124+       const  config  =  { 
125+         method, 
126+         url, 
127+         headers, 
128+         params : formatQueryParams ( params ) , 
129+         timeout : 30000 , 
130+         ...args , 
131+       } ; 
132+ 
133+       if  ( data )  { 
134+         config . data  =  data ; 
135+       } 
136+ 
137+       try  { 
138+         const  response  =  await  axios ( this ,  config ) ; 
139+         return  response . data  ||  response ; 
140+       }  catch  ( error )  { 
141+         const  parsedError  =  parseApiError ( error ) ; 
142+         throw  new  Error ( `Trustpilot API Error: ${ parsedError . message }   (${ parsedError . code }  )` ) ; 
143+       } 
144+     } , 
145+ 
146+     async  _makeRequestWithRetry ( config ,  retries  =  RETRY_CONFIG . MAX_RETRIES )  { 
147+       try  { 
148+         return  await  this . _makeRequest ( config ) ; 
149+       }  catch  ( error )  { 
150+         if  ( retries  >  0  &&  error . response ?. status  ===  HTTP_STATUS . TOO_MANY_REQUESTS )  { 
151+           const  delay  =  Math . min ( RETRY_CONFIG . INITIAL_DELAY  *  ( RETRY_CONFIG . MAX_RETRIES  -  retries  +  1 ) ,  RETRY_CONFIG . MAX_DELAY ) ; 
152+           await  sleep ( delay ) ; 
153+           return  this . _makeRequestWithRetry ( config ,  retries  -  1 ) ; 
154+         } 
155+         throw  error ; 
156+       } 
157+     } , 
158+ 
159+     // Business Unit methods 
160+     async  getBusinessUnit ( businessUnitId )  { 
161+       if  ( ! validateBusinessUnitId ( businessUnitId ) )  { 
162+         throw  new  Error ( "Invalid business unit ID" ) ; 
163+       } 
164+ 
165+       const  endpoint  =  buildUrl ( ENDPOINTS . BUSINESS_UNIT_BY_ID ,  {  businessUnitId } ) ; 
166+       const  response  =  await  this . _makeRequest ( {  endpoint } ) ; 
167+       return  parseBusinessUnit ( response ) ; 
168+     } , 
169+ 
170+     async  searchBusinessUnits ( {  query =  "" ,  limit =  DEFAULT_LIMIT ,  offset =  0  }  =  { } )  { 
171+       const  response  =  await  this . _makeRequest ( { 
172+         endpoint : ENDPOINTS . BUSINESS_UNITS , 
173+         params : { 
174+           query, 
175+           limit, 
176+           offset, 
177+         } , 
178+       } ) ; 
179+ 
180+       return  response . businessUnits ?. map ( parseBusinessUnit )  ||  [ ] ; 
181+     } , 
182+ 
183+     // Service Review methods 
184+     async  getServiceReviews ( { 
185+       businessUnitId, 
186+       stars =  null , 
187+       sortBy =  SORT_OPTIONS . CREATED_AT_DESC , 
188+       limit =  DEFAULT_LIMIT , 
189+       offset =  0 , 
190+       includeReportedReviews =  false , 
191+       tags =  [ ] , 
192+       language =  null , 
193+     }  =  { } )  { 
194+       if  ( ! validateBusinessUnitId ( businessUnitId ) )  { 
195+         throw  new  Error ( "Invalid business unit ID" ) ; 
196+       } 
197+ 
198+       const  endpoint  =  buildUrl ( ENDPOINTS . PRIVATE_SERVICE_REVIEWS ,  {  businessUnitId } ) ; 
199+       const  params  =  { 
200+         stars, 
201+         orderBy : sortBy , 
202+         perPage : limit , 
203+         page : Math . floor ( offset  /  limit )  +  1 , 
204+         includeReportedReviews, 
205+         language, 
206+       } ; 
207+ 
208+       if  ( tags . length  >  0 )  { 
209+         params . tags  =  tags . join ( "," ) ; 
210+       } 
211+ 
212+       const  response  =  await  this . _makeRequestWithRetry ( { 
213+         endpoint, 
214+         params, 
215+       } ) ; 
216+ 
217+       return  { 
218+         reviews : response . reviews ?. map ( parseReview )  ||  [ ] , 
219+         pagination : { 
220+           total : response . pagination ?. total  ||  0 , 
221+           page : response . pagination ?. page  ||  1 , 
222+           perPage : response . pagination ?. perPage  ||  limit , 
223+           hasMore : response . pagination ?. hasMore  ||  false , 
224+         } , 
225+       } ; 
226+     } , 
227+ 
228+     async  getServiceReviewById ( {  businessUnitId,  reviewId } )  { 
229+       if  ( ! validateBusinessUnitId ( businessUnitId ) )  { 
230+         throw  new  Error ( "Invalid business unit ID" ) ; 
231+       } 
232+       if  ( ! validateReviewId ( reviewId ) )  { 
233+         throw  new  Error ( "Invalid review ID" ) ; 
234+       } 
235+ 
236+       const  endpoint  =  buildUrl ( ENDPOINTS . PRIVATE_SERVICE_REVIEW_BY_ID ,  {  businessUnitId,  reviewId } ) ; 
237+       const  response  =  await  this . _makeRequest ( {  endpoint } ) ; 
238+       return  parseReview ( response ) ; 
239+     } , 
240+ 
241+     async  replyToServiceReview ( {  businessUnitId,  reviewId,  message } )  { 
242+       if  ( ! validateBusinessUnitId ( businessUnitId ) )  { 
243+         throw  new  Error ( "Invalid business unit ID" ) ; 
244+       } 
245+       if  ( ! validateReviewId ( reviewId ) )  { 
246+         throw  new  Error ( "Invalid review ID" ) ; 
247+       } 
248+       if  ( ! message  ||  typeof  message  !==  'string' )  { 
249+         throw  new  Error ( "Reply message is required" ) ; 
250+       } 
251+ 
252+       const  endpoint  =  buildUrl ( ENDPOINTS . REPLY_TO_SERVICE_REVIEW ,  {  businessUnitId,  reviewId } ) ; 
253+       const  response  =  await  this . _makeRequest ( { 
254+         endpoint, 
255+         method : "POST" , 
256+         data : {  message } , 
257+       } ) ; 
258+       return  response ; 
259+     } , 
260+ 
261+     // Product Review methods 
262+     async  getProductReviews ( { 
263+       businessUnitId, 
264+       stars =  null , 
265+       sortBy =  SORT_OPTIONS . CREATED_AT_DESC , 
266+       limit =  DEFAULT_LIMIT , 
267+       offset =  0 , 
268+       includeReportedReviews =  false , 
269+       tags =  [ ] , 
270+       language =  null , 
271+     }  =  { } )  { 
272+       if  ( ! validateBusinessUnitId ( businessUnitId ) )  { 
273+         throw  new  Error ( "Invalid business unit ID" ) ; 
274+       } 
275+ 
276+       const  endpoint  =  buildUrl ( ENDPOINTS . PRIVATE_PRODUCT_REVIEWS ,  {  businessUnitId } ) ; 
277+       const  params  =  { 
278+         stars, 
279+         orderBy : sortBy , 
280+         perPage : limit , 
281+         page : Math . floor ( offset  /  limit )  +  1 , 
282+         includeReportedReviews, 
283+         language, 
284+       } ; 
285+ 
286+       if  ( tags . length  >  0 )  { 
287+         params . tags  =  tags . join ( "," ) ; 
288+       } 
289+ 
290+       const  response  =  await  this . _makeRequestWithRetry ( { 
291+         endpoint, 
292+         params, 
293+       } ) ; 
294+ 
295+       return  { 
296+         reviews : response . reviews ?. map ( parseReview )  ||  [ ] , 
297+         pagination : { 
298+           total : response . pagination ?. total  ||  0 , 
299+           page : response . pagination ?. page  ||  1 , 
300+           perPage : response . pagination ?. perPage  ||  limit , 
301+           hasMore : response . pagination ?. hasMore  ||  false , 
302+         } , 
303+       } ; 
304+     } , 
305+ 
306+     async  getProductReviewById ( {  reviewId } )  { 
307+       if  ( ! validateReviewId ( reviewId ) )  { 
308+         throw  new  Error ( "Invalid review ID" ) ; 
309+       } 
310+ 
311+       const  endpoint  =  buildUrl ( ENDPOINTS . PRIVATE_PRODUCT_REVIEW_BY_ID ,  {  reviewId } ) ; 
312+       const  response  =  await  this . _makeRequest ( {  endpoint } ) ; 
313+       return  parseReview ( response ) ; 
314+     } , 
315+ 
316+     async  replyToProductReview ( {  reviewId,  message } )  { 
317+       if  ( ! validateReviewId ( reviewId ) )  { 
318+         throw  new  Error ( "Invalid review ID" ) ; 
319+       } 
320+       if  ( ! message  ||  typeof  message  !==  'string' )  { 
321+         throw  new  Error ( "Reply message is required" ) ; 
322+       } 
323+ 
324+       const  endpoint  =  buildUrl ( ENDPOINTS . REPLY_TO_PRODUCT_REVIEW ,  {  reviewId } ) ; 
325+       const  response  =  await  this . _makeRequest ( { 
326+         endpoint, 
327+         method : "POST" , 
328+         data : {  message } , 
329+       } ) ; 
330+       return  response ; 
331+     } , 
332+ 
333+     // Webhook methods 
334+     async  createWebhook ( {  url,  events =  [ ] ,  businessUnitId =  null  } )  { 
335+       if  ( ! url )  { 
336+         throw  new  Error ( "Webhook URL is required" ) ; 
337+       } 
338+       if  ( ! Array . isArray ( events )  ||  events . length  ===  0 )  { 
339+         throw  new  Error ( "At least one event must be specified" ) ; 
340+       } 
341+ 
342+       const  data  =  { 
343+         url, 
344+         events, 
345+       } ; 
346+ 
347+       if  ( businessUnitId )  { 
348+         data . businessUnitId  =  businessUnitId ; 
349+       } 
350+ 
351+       const  response  =  await  this . _makeRequest ( { 
352+         endpoint : ENDPOINTS . WEBHOOKS , 
353+         method : "POST" , 
354+         data, 
355+       } ) ; 
356+       return  response ; 
357+     } , 
358+ 
359+     async  deleteWebhook ( webhookId )  { 
360+       if  ( ! webhookId )  { 
361+         throw  new  Error ( "Webhook ID is required" ) ; 
362+       } 
363+ 
364+       const  endpoint  =  buildUrl ( ENDPOINTS . WEBHOOK_BY_ID ,  {  webhookId } ) ; 
365+       await  this . _makeRequest ( { 
366+         endpoint, 
367+         method : "DELETE" , 
368+       } ) ; 
369+     } , 
370+ 
371+     async  listWebhooks ( )  { 
372+       const  response  =  await  this . _makeRequest ( { 
373+         endpoint : ENDPOINTS . WEBHOOKS , 
374+       } ) ; 
375+       return  response . webhooks  ||  [ ] ; 
376+     } , 
377+ 
378+     // Utility methods 
379+     parseWebhookPayload ( payload )  { 
380+       return  parseWebhookPayload ( payload ) ; 
381+     } , 
382+ 
383+     validateWebhookSignature ( payload ,  signature ,  secret )  { 
384+       // TODO: Implement webhook signature validation when Trustpilot provides it 
385+       return  true ; 
386+     } , 
387+ 
388+     // Legacy method for debugging 
9389    authKeys ( )  { 
10-       console . log ( Object . keys ( this . $auth ) ) ; 
390+       console . log ( "Auth keys:" ,  Object . keys ( this . $auth  ||  { } ) ) ; 
391+       return  Object . keys ( this . $auth  ||  { } ) ; 
11392    } , 
12393  } , 
13394} ) ; 
0 commit comments