Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(analytics): fix analytics hook for expired or logged out #31

Merged
merged 10 commits into from
Feb 23, 2021
27 changes: 25 additions & 2 deletions packages/cli/src/hooks/analytics/analytics.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ jest.mock('@coveord/platform-client');
import {mocked} from 'ts-jest/utils';
import {CoveoAnalyticsClient, IRuntimeEnvironment} from 'coveo.analytics';
import {Configuration, Config} from '../../lib/config/config';
import {AuthenticatedClient} from '../../lib/platform/authenticatedClient';
import {
AuthenticatedClient,
AuthenticationStatus,
getAuthenticationStatus,
} from '../../lib/platform/authenticatedClient';
import hook, {AnalyticsHook} from './analytics';
import {IConfig} from '@oclif/config';
import {PlatformClient} from '@coveord/platform-client';
Expand All @@ -14,6 +18,7 @@ const mockedAnalytics = mocked(CoveoAnalyticsClient);
const mockedConfig = mocked(Config);
const mockedPlatformClient = mocked(PlatformClient);
const mockedAuthenticatedClient = mocked(AuthenticatedClient);
const mockedAuthenticationStatus = mocked(getAuthenticationStatus);

describe('analytics hook', () => {
let sendCustomEvent: jest.Mock;
Expand Down Expand Up @@ -69,7 +74,7 @@ describe('analytics hook', () => {
};

const doMockAuthenticatedClient = () => {
mockedAuthenticatedClient.mockImplementationOnce(
mockedAuthenticatedClient.mockImplementation(
() =>
({
getClient: () =>
Expand All @@ -82,6 +87,9 @@ describe('analytics hook', () => {
cfg: mockedConfig.getMockImplementation()!('./'),
} as AuthenticatedClient)
);
mockedAuthenticationStatus.mockImplementation(() =>
Promise.resolve(AuthenticationStatus.LOGGED_IN)
);
};

beforeEach(() => {
Expand Down Expand Up @@ -212,4 +220,19 @@ describe('analytics hook', () => {
await hook(getAnalyticsHook({}));
expect(sendCustomEvent).not.toHaveBeenCalled();
});

it('should not throw an error when the user is not logged in', async () => {
mockedAuthenticationStatus.mockImplementationOnce(() =>
Promise.resolve(AuthenticationStatus.LOGGED_OUT)
);

await expect(hook(getAnalyticsHook({}))).resolves.not.toThrow();
});

it('should not throw an error when the user is expired', async () => {
mockedAuthenticationStatus.mockImplementationOnce(() =>
Promise.resolve(AuthenticationStatus.EXPIRED)
);
await expect(hook(getAnalyticsHook({}))).resolves.not.toThrow();
});
});
15 changes: 14 additions & 1 deletion packages/cli/src/hooks/analytics/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ import {IConfig} from '@oclif/config';
import {CoveoAnalyticsClient} from 'coveo.analytics';
import {WebStorage} from 'coveo.analytics/dist/definitions/storage';
import {Config} from '../../lib/config/config';
import {AuthenticatedClient} from '../../lib/platform/authenticatedClient';
import {
AuthenticatedClient,
AuthenticationStatus,
getAuthenticationStatus,
} from '../../lib/platform/authenticatedClient';

export interface AnalyticsHook {
commandID: string;
Expand All @@ -15,6 +19,10 @@ export interface AnalyticsHook {
const analyticsAPIKey = 'xx01ad67bb-f837-4a3e-8669-16397994a6f2';

const hook = async function (opts: AnalyticsHook) {
if (!(await isLoggedIn())) {
return;
}

const {eventType, eventValue} = identifier(opts);
const {
environment,
Expand Down Expand Up @@ -109,6 +117,11 @@ const storage = (cfg: Config): WebStorage => {
};
};

const isLoggedIn = async () => {
const status = await getAuthenticationStatus();
return status === AuthenticationStatus.LOGGED_IN;
};

export type AnalyticsStatus = 'success' | 'failure';

export const buildAnalyticsHook = (
Expand Down
14 changes: 7 additions & 7 deletions packages/cli/src/lib/decorators/authenticationRequired.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
/* eslint-disable @typescript-eslint/no-namespace */
import Command from '@oclif/command';
import {IConfig} from '@oclif/config';
import {AuthenticatedClient} from '../platform/authenticatedClient';
import {
AuthenticationStatus,
getAuthenticationStatus,
} from '../platform/authenticatedClient';

declare global {
namespace NodeJS {
Expand All @@ -20,16 +23,13 @@ export default function AuthenticationRequired() {
) {
const originalRunCommand = descriptor.value!;
descriptor.value = async function () {
const authenticatedClient = new AuthenticatedClient();
const status = await getAuthenticationStatus();

const loggedIn = await authenticatedClient.isLoggedIn();

if (!loggedIn) {
if (status === AuthenticationStatus.LOGGED_OUT) {
target.error('Not currently logged in. Run coveo auth:login first.');
}

const isExpired = await authenticatedClient.isExpired();
if (isExpired) {
if (status === AuthenticationStatus.EXPIRED) {
target.error(
'Authentication token is expired. Run coveo auth:login first.'
);
Expand Down
20 changes: 20 additions & 0 deletions packages/cli/src/lib/platform/authenticatedClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,23 @@ export class AuthenticatedClient {
}
}
}

export enum AuthenticationStatus {
LOGGED_IN,
EXPIRED,
LOGGED_OUT,
}

export async function getAuthenticationStatus() {
const authenticatedClient = new AuthenticatedClient();

if (!(await authenticatedClient.isLoggedIn())) {
return AuthenticationStatus.LOGGED_OUT;
}

if (await authenticatedClient.isExpired()) {
return AuthenticationStatus.EXPIRED;
}

return AuthenticationStatus.LOGGED_IN;
}