import { LightningElement, api, track } from "lwc"; const SEARCH_HUB = "developerWebsiteBlogs"; interface Recommendation { originalCoveoData: { uri: string; title: string; searchUid: string; raw: { permanentId: string; }; "@source": string; }; clickEvent: () => void; } const language_to_locale_map = { english: "en", japanese: "ja" }; export default class CoveoRecommendations extends LightningElement { @api coveoAuthToken!: string; @api coveoOrganizationId!: string; @api label? = "Readers Also Viewed"; @api postAcf: string = "{}"; _recommendations = [] as Recommendation[]; private get recommendations(): any[] { return this._recommendations || []; } @track showRecommendations = true; @track recommendationsLoading = true; getLocale() { const acf = JSON.parse(this.postAcf); return language_to_locale_map[ (acf?.language as keyof typeof language_to_locale_map) || "english" ]; } connectedCallback() { const actionsHistory = localStorage.getItem( "__coveo.analytics.history" ); const url = `https://${this.coveoOrganizationId}.org.coveo.com/rest/search`; const searchStart = performance.now(); fetch(url, { headers: { Authorization: `Bearer ${this.coveoAuthToken}`, "Content-Type": "application/json" }, method: "POST", body: JSON.stringify({ pipeline: "Recommendations_Developer_Blogs", locale: this.getLocale(), searchHub: SEARCH_HUB, numberOfResults: 3, actionsHistory: JSON.parse(actionsHistory!), isGuestUser: true }) }).then( async (response) => { try { const json = await response.json(); const results = json.results; this.logSearchAnalytics( json.searchUid, performance.now() - searchStart ); const blogDataLoadTasks = results .slice(0, 3) .map(async (rec: any, index: number) => { const slug = rec.uri.split("/").pop(); const blogDataUrl = `https://developer.salesforce.com/blogs/wp-json/wp/v2/posts?slug=${slug}&state=publish&_embed=wp:featuredmedia`; const wordPressData = ( await (await fetch(blogDataUrl)).json() )[0]; wordPressData.originalCoveoData = rec; wordPressData.originalCoveoData.searchUid = json.searchUid; wordPressData.clickEvent = this.buildClickAnalyticsLogger(index); return wordPressData; }); this._recommendations = await Promise.all( blogDataLoadTasks ); if (this._recommendations.length !== 3) { this.showRecommendations = false; } } catch (ex) { this.showRecommendations = false; } finally { if (this.recommendations.length === 3) { this.recommendationsLoading = false; } else { this.recommendationsLoading = false; this.showRecommendations = false; } } }, () => { this.showRecommendations = false; } ); } logSearchAnalytics = (uid: string, time: number) => { const payload = { anonymous: true, language: this.getLocale(), originLevel1: SEARCH_HUB, originLevel2: SEARCH_HUB, actionCause: "recommendationInterfaceLoad", queryText: "", // This has to be included, but is empty, because recommendations use the 'search' endpoint with an empty query string responseTime: time, searchQueryUid: uid }; fetch( `https://${this.coveoOrganizationId}.analytics.org.coveo.com/rest/ua/v15/analytics/search`, { headers: { Authorization: `Bearer ${this.coveoAuthToken}`, "Content-Type": "application/json" }, method: "POST", body: JSON.stringify(payload) } ); }; buildClickAnalyticsLogger = (index: number) => () => { const payload = { anonymous: true, documentPosition: index, documentTitle: this._recommendations[index].originalCoveoData.title, documentUrl: this._recommendations[index].originalCoveoData.uri, language: this.getLocale(), originLevel1: SEARCH_HUB, originLevel2: SEARCH_HUB, actionCause: "recommendationOpen", queryText: "", // This has to be included, but is empty, because recommendations use the 'search' endpoint with an empty query string searchQueryUid: this._recommendations[index].originalCoveoData.uri, sourceName: this._recommendations[index].originalCoveoData["@source"], customData: { contentIDKey: "permanentId", contentIDValue: this._recommendations[index].originalCoveoData.raw .permanentId } }; fetch( `https://${this.coveoOrganizationId}.analytics.org.coveo.com/rest/ua/v15/analytics/click`, { headers: { Authorization: `Bearer ${this.coveoAuthToken}`, "Content-Type": "application/json" }, method: "POST", body: JSON.stringify(payload) } ); }; }