-
Notifications
You must be signed in to change notification settings - Fork 4
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
DT-979: Add Appcues Support #2722
Changes from 5 commits
619966f
058918e
c020243
052e03b
554e21f
6a5750e
5552e3d
5ee185f
cda0778
8f3f267
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
/* eslint-disable no-undef */ | ||
import {Metrics} from '../../../src/libs/ajax/Metrics'; | ||
import eventList from '../../../src/libs/events'; | ||
|
||
describe('Metrics Tests', function () { | ||
|
||
// Intercept configuration calls | ||
beforeEach(() => { | ||
cy.intercept({ | ||
method: 'GET', | ||
url: '/config.json', | ||
hostname: 'localhost', | ||
}, {'env': 'ci'}); | ||
}); | ||
|
||
Cypress._.each(Object.keys(eventList), (eventType) => { | ||
it(`Captures ${eventType} Event`, function () { | ||
cy.intercept('**/event').as('event'); | ||
Metrics.captureEvent(eventType); | ||
cy.wait('@event').then(interception => { | ||
expect(interception).to.exist; | ||
}); | ||
}); | ||
}); | ||
|
||
it(`Sync Profile`, function () { | ||
cy.intercept('**/syncProfile').as('sync'); | ||
Metrics.syncProfile(); | ||
cy.wait('@sync').then(interception => { | ||
expect(interception).to.exist; | ||
}); | ||
}); | ||
|
||
it(`Identify`, function () { | ||
cy.intercept('**/identify').as('identify'); | ||
Metrics.identify('anonymousId'); | ||
cy.wait('@identify').then(interception => { | ||
expect(interception).to.exist; | ||
}); | ||
}); | ||
|
||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,8 +5,11 @@ | |
<meta charset="utf-8"> | ||
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||
<meta name="viewport" content="width=device-width,initial-scale=1.0"> | ||
<script src="https://accounts.google.com/gsi/client" async defer id="gsiClient"></script> | ||
<title>Components App</title> | ||
<!-- AppCues Requirements --> | ||
<script type="text/javascript"> | ||
window.AppcuesSettings = { enableURLDetection: true }; | ||
</script> | ||
Comment on lines
+9
to
+12
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm open to testing suggestions here. I was unable to figure out a way to unit test the global There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think at some point it is worth trusting the library. We could add tests that verify we're setting the right values, but as far as appcues itself, I think that's probably out of our purview to test? But happy to have that contested |
||
</head> | ||
|
||
<body> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,7 +22,6 @@ | |
<link rel="icon" type="image/png" href="%PUBLIC_URL%/images/favicon/favicon-32x32.png" sizes="32x32" /> | ||
<link rel="icon" type="image/png" href="%PUBLIC_URL%/images/favicon/favicon-16x16.png" sizes="16x16" /> | ||
<link rel="mask-icon" href="%PUBLIC_URL%/images/favicon/safari-pinned-tab.svg"> | ||
<script src="https://accounts.google.com/gsi/client" async defer id="gsiClient"></script> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Drive by fix - we removed "google sign in" in #2667 |
||
<!-- | ||
Notice the use of %PUBLIC_URL% in the tags above. | ||
It will be replaced with the URL of the `public` folder during the build. | ||
|
@@ -32,6 +31,15 @@ | |
work correctly both with client-side routing and a non-root public URL. | ||
Learn how to configure a non-root public URL by running `npm run build`. | ||
--> | ||
|
||
<!-- AppCues Requirements --> | ||
<script type="text/javascript"> | ||
window.AppcuesSettings = { enableURLDetection: true }; | ||
</script> | ||
<script src="https://fast.appcues.com/49767.js"> | ||
</script> | ||
<!-- End AppCues Requirements --> | ||
|
||
<title>Broad Data Use Oversight System</title> | ||
</head> | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,24 @@ | ||
import axios from 'axios'; | ||
import axios, {AxiosRequestConfig} from 'axios'; | ||
import {getDefaultProperties} from '@databiosphere/bard-client'; | ||
|
||
import {Storage} from '../storage'; | ||
import {getBardApiUrl} from '../ajax'; | ||
import {Token} from '../config'; | ||
import {MetricsEventName} from 'src/libs/events'; | ||
|
||
// Set default timeout for all metrics calls to 30 seconds | ||
const defaultSignal: AbortSignal = AbortSignal.timeout(30000); | ||
|
||
Comment on lines
+9
to
+10
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that this was necessary for typescriptification to make all the axios calls compile. I'm open to alternative suggestions here but this seems like a safe timeout for bard & appcues calls. |
||
export const Metrics = { | ||
captureEvent: (event, details, signal) => captureEventFn(event, details, signal).catch(() => { | ||
captureEvent: ( | ||
event: MetricsEventName, | ||
details: Record<string, any> = {}, | ||
signal: AbortSignal = defaultSignal, | ||
refreshAppcues: boolean = true | ||
) => captureEventFn(event, details, signal, refreshAppcues).catch(() => { | ||
}), | ||
syncProfile: (signal) => syncProfile(signal), | ||
identify: (anonId, signal) => identify(anonId, signal), | ||
syncProfile: (signal: AbortSignal = defaultSignal) => syncProfile(signal), | ||
identify: (anonId: String, signal: AbortSignal = defaultSignal) => identify(anonId, signal), | ||
}; | ||
|
||
/** | ||
|
@@ -18,12 +27,19 @@ export const Metrics = { | |
* @param {string} event - The event name. | ||
* @param {Object} [details={}] - The event details. | ||
* @param {AbortSignal} [signal] - The abort signal. | ||
* @param refreshAppcues - The refresh Appcues flag. | ||
* @returns {Promise} - A Promise that resolves when the event is captured. | ||
*/ | ||
const captureEventFn = async (event, details = {}, signal) => { | ||
const captureEventFn = async (event: MetricsEventName, details: object = {}, signal: AbortSignal, refreshAppcues: boolean): Promise<any> => { | ||
const isSignedIn = Storage.userIsLogged(); | ||
rushtong marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const isRegistered = isSignedIn && Storage.getCurrentUser(); | ||
|
||
// Send event to Appcues and refresh Appcues state | ||
window.Appcues?.track(event); | ||
if (refreshAppcues) { | ||
window.Appcues?.page(); | ||
} | ||
|
||
if (!isRegistered && !Storage.getAnonymousId()) { | ||
Storage.setAnonymousId(); | ||
} | ||
|
@@ -40,7 +56,7 @@ const captureEventFn = async (event, details = {}, signal) => { | |
}, | ||
}; | ||
|
||
const config = { | ||
const config: AxiosRequestConfig = { | ||
method: 'POST', | ||
url: `${await getBardApiUrl()}/api/event`, | ||
data: body, | ||
|
@@ -57,8 +73,8 @@ const captureEventFn = async (event, details = {}, signal) => { | |
* @param {AbortSignal} [signal] - The abort signal. | ||
* @returns {Promise} - A Promise that resolves when the profile is synced. | ||
*/ | ||
const syncProfile = async (signal) => { | ||
const config = { | ||
const syncProfile = async (signal: AbortSignal): Promise<any> => { | ||
const config: AxiosRequestConfig = { | ||
method: 'POST', | ||
url: `${await getBardApiUrl()}/api/syncProfile`, | ||
headers: {Authorization: `Bearer ${Token.getToken()}`}, | ||
|
@@ -76,10 +92,24 @@ const syncProfile = async (signal) => { | |
* @param {AbortSignal} [signal] - The abort signal. | ||
* @returns {Promise} - A Promise that resolves when the user is identified. | ||
*/ | ||
const identify = async (anonId, signal) => { | ||
const identify = async (anonId: String, signal: AbortSignal): Promise<any> => { | ||
const body = {anonId}; | ||
|
||
const config = { | ||
if (window.Appcues) { | ||
const user = Storage.getCurrentUser(); | ||
const createDate = user.createDate ? user.createDate : new Date().getTime(); | ||
const appcuesProps = { | ||
dateJoined: createDate, | ||
app: 'DUOS' | ||
}; | ||
if (user.userStatusInfo?.userSubjectId) { | ||
window.Appcues.identify(user.userStatusInfo.userSubjectId, appcuesProps); | ||
} else { | ||
window.Appcues.identify(`${user.userId}`, appcuesProps); | ||
} | ||
} | ||
|
||
const config: AxiosRequestConfig = { | ||
method: 'POST', | ||
url: `${await getBardApiUrl()}/api/identify`, | ||
data: body, | ||
|
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
/* | ||
* NOTE: See the Mixpanel guide in the terra-ui GitHub Wiki for more details: | ||
* https://github.com/DataBiosphere/terra-ui/wiki/Mixpanel | ||
*/ | ||
const eventList = { | ||
userRegister: 'user:register', | ||
userSignIn: 'user:signin', | ||
|
||
pageView: 'page:view', | ||
dataLibrary: 'page:view:data-library', | ||
dar: 'page:view:dar' | ||
}; | ||
|
||
export default eventList; | ||
|
||
// Helper type to create BaseMetricsEventName. | ||
type MetricsEventsMap<EventName> = { [key: string]: EventName | MetricsEventsMap<EventName> }; | ||
// Union type of all event names configured in eventsList. | ||
type BaseMetricsEventName = typeof eventList extends MetricsEventsMap<infer EventName> ? EventName : never; | ||
// Each route has its own page view event, where the event name includes the name of the route. | ||
type PageViewMetricsEventName = `${typeof eventList.pageView}:${string}`; | ||
|
||
/** | ||
* Union type of all metrics event names. | ||
*/ | ||
export type MetricsEventName = BaseMetricsEventName | PageViewMetricsEventName; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,7 +21,8 @@ | |
} | ||
}, | ||
"include": [ | ||
"src" | ||
"src", | ||
"types/index.d.ts" | ||
], | ||
"plugins": ["@typescript-eslint", "import"] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
declare module "@databiosphere/bard-client" { | ||
export function getDefaultProperties(): any | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another drive by fix - we removed "google sign in" in #2667