Skip to content

Commit 0815d52

Browse files
committed
Coveo: Add new implementation using API
1 parent a3fccd6 commit 0815d52

File tree

5 files changed

+194
-1
lines changed

5 files changed

+194
-1
lines changed

assets/css/v2/style.css

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1275,6 +1275,38 @@ main {
12751275
}
12761276
}
12771277

1278+
/* MARK: Coveo API
1279+
*/
1280+
1281+
.standalone__searchbox {
1282+
background-color: white;
1283+
border: 1px solid black;
1284+
width: fit-content;
1285+
1286+
.form {
1287+
display: flex;
1288+
justify-content: center;
1289+
gap: 1rem;
1290+
padding: 0.25rem 0.5rem;
1291+
1292+
.submit-button {
1293+
background-color: inherit;
1294+
color: oklch(var(--color-brand));
1295+
border: none;
1296+
padding: 0;
1297+
cursor: pointer;
1298+
margin-top: 0.2rem;
1299+
}
1300+
1301+
.input {
1302+
background-color: inherit;
1303+
border: none;
1304+
padding: 0;
1305+
outline: none;
1306+
}
1307+
}
1308+
}
1309+
12781310
/* MARK: Coveo
12791311
*/
12801312

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
const SEARCH_HUB = 'HUB_ES_Nginx_Docs_And_Org';
2+
3+
function isJwtExpired(token) {
4+
const parts = token.split('.');
5+
if (parts.length !== 3) {
6+
return true;
7+
}
8+
9+
const [_, payload] = parts;
10+
const decodedPayload = atob(payload);
11+
const payloadObj = JSON.parse(decodedPayload);
12+
13+
const currentTime = Math.floor(Date.now() / 1000);
14+
const expTime = payloadObj.exp;
15+
return currentTime >= expTime;
16+
}
17+
18+
async function getsearchObj() {
19+
const response = await fetch(
20+
window.location.origin + '/api/v1/auth/search_token'
21+
);
22+
return response.json();
23+
}
24+
25+
async function getValidSearchCredentials() {
26+
const accessToken = localStorage.getItem('coveo_jwt_v1');
27+
const organizationId = localStorage.getItem('coveo_org_id_v1');
28+
29+
const needsFetch =
30+
!accessToken || !organizationId || isJwtExpired(accessToken);
31+
if (needsFetch) {
32+
const { token, org_id } = await getsearchObj();
33+
localStorage.setItem('coveo_jwt_v1', token);
34+
localStorage.setItem('coveo_org_id_v1', org_id);
35+
return {
36+
accessToken: token,
37+
organizationId: org_id,
38+
};
39+
}
40+
41+
return {
42+
accessToken,
43+
organizationId,
44+
};
45+
}
46+
47+
async function sendCoveoRequest(payload, endpoint = '') {
48+
const { organizationId, accessToken } = await getValidSearchCredentials();
49+
const url = `https://${organizationId}.org.coveo.com/rest/${endpoint}/`;
50+
51+
fetch(
52+
new Request(url, {
53+
method: 'POST',
54+
headers: new Headers({
55+
Accept: 'application/json',
56+
'Content-Type': 'application/json',
57+
Authorization: `Bearer ${accessToken}`,
58+
}),
59+
body: JSON.stringify(payload),
60+
})
61+
)
62+
.then((response) => {
63+
if (!response.ok) {
64+
// Throw some error to user
65+
}
66+
return response.json();
67+
})
68+
.then((responseData) => {
69+
const queryText = payload.q;
70+
const duration = responseData.duration;
71+
const searchUid = responseData.searchUid;
72+
73+
logQuery(queryText, duration, searchUid);
74+
});
75+
}
76+
77+
async function sendCoveoAnalyticsRequest(payload) {
78+
const { organizationId, accessToken } = await getValidSearchCredentials();
79+
const url = `https://${organizationId}.analytics.org.coveo.com/rest/ua/v15/analytics/search`;
80+
81+
fetch(
82+
new Request(url, {
83+
method: 'POST',
84+
headers: new Headers({
85+
Accept: 'application/json',
86+
'Content-Type': 'application/json',
87+
Authorization: `Bearer ${accessToken}`,
88+
}),
89+
body: JSON.stringify(payload),
90+
})
91+
)
92+
.then((response) => {
93+
if (!response.ok) {
94+
// Throw some error to user
95+
}
96+
return response.json();
97+
})
98+
.then((responseData) => {
99+
console.log(responseData);
100+
});
101+
}
102+
103+
/**
104+
* Sends a request for suggestions based on given query. Should run everytime a query is updated.
105+
*
106+
* @param {*} q
107+
*/
108+
const suggestQuery = async (q) => {};
109+
110+
/**
111+
* Sends a request to submit the given query. Should run whenever the form is submitted.
112+
*
113+
* @param {*} q
114+
*/
115+
const submitQuery = async (q) => {
116+
const payload = {
117+
q: q,
118+
locale: 'en-US',
119+
searchHub: SEARCH_HUB,
120+
tab: 'default',
121+
};
122+
123+
sendCoveoRequest(payload, 'search/v2');
124+
125+
// const destination = `/search.html?q=${q}`;
126+
// window.location.href = destination;
127+
};
128+
129+
const logQuery = async (q, duration, searchUid) => {
130+
const payload = {
131+
language: 'en',
132+
queryText: q,
133+
responseTime: duration,
134+
searchQueryUid: searchUid,
135+
actionCause: 'searchFromLink',
136+
};
137+
138+
sendCoveoAnalyticsRequest(payload);
139+
};
140+
141+
document.addEventListener('DOMContentLoaded', () => {
142+
const form = document.getElementById('standalone__searchbox-form');
143+
const input = document.getElementById('standalone__searchbox-input');
144+
145+
form.addEventListener('submit', (event) => {
146+
event.preventDefault();
147+
const q = encodeURIComponent(input.value.trim());
148+
submitQuery(q);
149+
});
150+
});
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<div class="standalone__searchbox" id="standalone__searchbox">
2+
<form class="form" id="standalone__searchbox-form" action="/search.html">
3+
<button class="submit-button" id="standalone__searchbox-submit-button">{{ partial "lucide" (dict "context" . "icon" "search") }}</button>
4+
<input type="text" class="input" id="standalone__searchbox-input" placeholder="Search NGINX Docs..." name="q">
5+
</form>
6+
</div>

layouts/partials/header.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
</label>
1818
<div class="header__search" data-testid="header__search">
1919
<!-- Standalone search box. -->
20-
{{ partial "coveo-atomic-search.html" (dict "id" "search-standalone-header") }}
20+
<!-- {{ partial "coveo-atomic-search.html" (dict "id" "search-standalone-header") }} -->
21+
{{ partial "coveo-api-search" . }}
2122
</div>
2223
{{ end }}
2324
</div>

layouts/partials/scripts.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@
1616
<script src="{{ $mermaid.RelPermalink }}" type="text/javascript" integrity="{{ $mermaid.Data.Integrity }}"></script>
1717
{{ end }}
1818

19+
<!-- Coveo API -->
20+
{{ $searchStandalone := resources.Get "/js/coveo/standalone-search.js" | fingerprint "sha512" }}
21+
<script src="{{ $searchStandalone.RelPermalink }}" type="text/javascript"></script>
22+
1923
<!-- Coveo Atomic -->
2024
<script
2125
type="module"

0 commit comments

Comments
 (0)