Skip to content

Commit

Permalink
EventsSDK: Beacon fallback
Browse files Browse the repository at this point in the history
Support falling back to beacon if fetch w/keepaliave is unsupported by
the browser.

TEST=manual,auto

Ran test-site locally and confirmed that beacon was used for firefox and
fetch w/keepalive was used in chrome. Confirmed all events still worked
as expected
  • Loading branch information
AjayBenno committed Feb 27, 2024
1 parent 29b1145 commit 0560a8e
Show file tree
Hide file tree
Showing 9 changed files with 61 additions and 26 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@yext/analytics",
"version": "0.6.5",
"version": "0.6.6",
"description": "An analytics library for Yext",
"author": "slapshot@yext.com",
"license": "BSD-3-Clause",
Expand Down
4 changes: 2 additions & 2 deletions src/infra/ChatAnalyticsReporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ export class ChatAnalyticsReporter {
*/
public async report(event: ChatEventPayLoad): Promise<EventAPIResponse> {
const headers: Record<string, string> = {
Authorization: `KEY ${this.apiKey}`,
'Content-Type': 'application/json',
};

Expand All @@ -68,7 +67,8 @@ export class ChatAnalyticsReporter {
this.endpoint,
{
...event,
sessionId
sessionId,
authorization: `KEY ${this.apiKey}`
},
headers);

Expand Down
38 changes: 28 additions & 10 deletions src/infra/HttpRequester.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { HttpRequesterService } from '../services';
import { AnalyticsPayload, EventPayload } from '../models';
import fetch from 'cross-fetch';
import { isFirefox } from '../utils/Browser';

/**
* Responsible for making web requests.
Expand All @@ -14,20 +15,36 @@ export class HttpRequester implements HttpRequesterService {
): Promise<Response> {
const data = JSON.stringify(body);

const fetchInit: RequestInit = {
method: 'POST',
headers,
body: data,
keepalive: true
};
if (isFirefox()) {
// send via beacon if the browser is using firefox since it does not support fetch w/keepalive
var enqueuedEvent = navigator.sendBeacon(url, data);
if (enqueuedEvent) {
return Promise.resolve(new Response(null, { status: 204 }));
} else {
// If there was a failure enqueing the event just reject
// with a Response that indicates an error.
// Fetch by default does not reject promises and instead just
// handles errors with a successful promise with an error that
// indicates an error
return Promise.resolve(Response.error());
}
} else {
const fetchInit: RequestInit = {
method: 'POST',
headers,
body: data,
keepalive: true
};

if (typeof(window) !== 'undefined' && window.fetch) {
return window.fetch(url, fetchInit);
if (typeof (window) !== 'undefined' && window.fetch) {
return window.fetch(url, fetchInit);
}
return fetch(url, fetchInit);
}

return fetch(url, fetchInit);
}



get(url: string): Promise<Response> {
const fetchInit: RequestInit = {
method: 'GET',
Expand All @@ -41,4 +58,5 @@ export class HttpRequester implements HttpRequesterService {

return fetch(url, fetchInit);
}

}
2 changes: 1 addition & 1 deletion src/infra/SearchAnalyticsReporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export class SearchAnalyticsReporter implements SearchAnalyticsService {
const res = await this.httpRequesterService.post(
this._endpoint, { data, ...additionalRequestAttributes }
);
if (res.status !== 200) {
if (!res.ok) {
const errorMessage = await res.text();
throw new Error(errorMessage);
}
Expand Down
13 changes: 13 additions & 0 deletions src/utils/Browser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

/**
* Detects if the browser is firefox and return true if so
*
*/
export function isFirefox(
): boolean {
// keepAlive is not supported in Firefox or Firefox for Android
return (
!!navigator.userAgent &&
navigator.userAgent.toLowerCase().includes('firefox')
);
}
2 changes: 1 addition & 1 deletion test-site/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion test-site/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export function fireListings() {
}

const chat = provideChatAnalytics({
apiKey: process.env.CHAT_API_KEY,
apiKey: 'd0e7eab25eb91c1eafe037d61eadd869',
});

export function fireChatEvent() {
Expand Down
20 changes: 12 additions & 8 deletions tests/infra/ChatAnalyticsReporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ const prodConfig: ChatAnalyticsConfig = {
};

const expectedHeaders: Record<string, string> = {
Authorization: 'KEY mock-api-key',
'Content-Type': 'application/json',
};

Expand All @@ -21,6 +20,11 @@ const payload: ChatEventPayLoad = {
sessionId: 'mocked-ulid-value'
};

const expectedPayload = {
...payload,
authorization: 'KEY mock-api-key'
}

beforeEach(() => {
jest.spyOn(ulidxLib, 'ulid').mockReturnValue('mocked-ulid-value');
});
Expand All @@ -35,7 +39,7 @@ it('should send events to the prod domain when configured', async () => {
expect(response).toEqual(mockedResponse);
expect(mockService.post).toBeCalledWith(
expectedUrl,
payload,
expectedPayload,
expectedHeaders
);
});
Expand All @@ -51,7 +55,7 @@ it('should send events to the custom endpoint when configured', async () => {
expect(response).toEqual(mockedResponse);
expect(mockService.post).toBeCalledWith(
expectedUrl,
payload,
expectedPayload,
expectedHeaders
);
});
Expand Down Expand Up @@ -114,7 +118,7 @@ describe('sessionId handling', () => {
await reporter.report(payload);
expect(mockService.post).toBeCalledWith(
expect.any(String),
payload,
expectedPayload,
expect.any(Object)
);
});
Expand All @@ -130,7 +134,7 @@ describe('sessionId handling', () => {
expect(mockService.post).toBeCalledWith(
expect.any(String),
{
...payload,
...expectedPayload,
sessionId: undefined
},
expect.any(Object)
Expand All @@ -150,7 +154,7 @@ describe('sessionId handling', () => {
expect(mockService.post).toBeCalledWith(
expect.any(String),
{
...payload,
...expectedPayload,
sessionId: 'custom-ulid-value',
},
expect.any(Object)
Expand All @@ -168,7 +172,7 @@ describe('sessionId handling', () => {
expect(mockService.post).toBeCalledWith(
expect.any(String),
{
...payload,
...expectedPayload,
sessionId: undefined
},
expect.any(Object)
Expand All @@ -182,7 +186,7 @@ describe('sessionId handling', () => {
expect(mockService.post).toBeCalledWith(
expect.any(String),
{
...payload,
...expectedPayload,
sessionId: undefined
},
expect.any(Object)
Expand Down

0 comments on commit 0560a8e

Please sign in to comment.