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

Update to Google Analytics 4 #1839

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions src/SIL.XForge.Scripture/ClientApp/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,11 @@
import { issuesEmailTemplate, supportedBrowser } from 'xforge-common/utils';
import versionData from '../../../version.json';
import { environment } from '../environments/environment';
import { AnalyticsService } from '../xforge-common/analytics.service';
import { SFProjectProfileDoc } from './core/models/sf-project-profile-doc';
import { roleCanAccessTranslate } from './core/models/sf-project-role-info';
import { SFProjectService } from './core/sf-project.service';

declare function gtag(...args: any): void;

export const CONNECT_PROJECT_OPTION = '*connect-project*';

@Component({
Expand Down Expand Up @@ -81,7 +80,8 @@
readonly urls: ExternalUrlService,
readonly featureFlags: FeatureFlagService,
private readonly pwaService: PwaService,
onlineStatusService: OnlineStatusService
onlineStatusService: OnlineStatusService,
private readonly analytics: AnalyticsService
) {
super(noticeService);
this.subscribe(
Expand Down Expand Up @@ -122,8 +122,7 @@
);
this.subscribe(navEndEvent$, e => {
if (this.isAppOnline) {
// eslint-disable-next-line @typescript-eslint/naming-convention
gtag('config', 'UA-22170471-15', { page_path: e.urlAfterRedirects });
this.analytics.logNavigation(e.urlAfterRedirects);

Check warning on line 125 in src/SIL.XForge.Scripture/ClientApp/src/app/app.component.ts

View check run for this annotation

Codecov / codecov/patch

src/SIL.XForge.Scripture/ClientApp/src/app/app.component.ts#L125

Added line #L125 was not covered by tests
}
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ export const environment = {
realtimeUrl: '/realtime-api/',
authDomain: 'login.languagetechnology.org',
authClientId: 'tY2wXn40fsL5VsPM4uIHNtU6ZUEXGeFn',
offlineDBVersion: 8
offlineDBVersion: 8,
googleTagId: 'G-SVKBDV7K3Q'
};
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ export const environment = {
realtimeUrl: '/',
authDomain: 'sil-appbuilder.auth0.com',
authClientId: 'aoAGb9Yx1H5WIsvCW6JJCteJhSa37ftH',
offlineDBVersion: 8
offlineDBVersion: 8,
googleTagId: null
};
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ export const environment = {
realtimeUrl: '/realtime-api/',
authDomain: 'dev-sillsdev.auth0.com',
authClientId: '4eHLjo40mAEGFU6zUxdYjnpnC1K1Ydnj',
offlineDBVersion: 8
offlineDBVersion: 8,
googleTagId: null
};
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,6 @@ export const environment = {
realtimeUrl: '/',
authDomain: 'sil-appbuilder.auth0.com',
authClientId: 'aoAGb9Yx1H5WIsvCW6JJCteJhSa37ftH',
offlineDBVersion: 8
offlineDBVersion: 8,
googleTagId: null
};
5 changes: 3 additions & 2 deletions src/SIL.XForge.Scripture/ClientApp/src/index.html
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-22170471-15"></script>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-SVKBDV7K3Q"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag("js", new Date());

gtag("config", "G-SVKBDV7K3Q");
</script>

<link href="https://fonts.googleapis.com/icon?family=Material+Icons|Material+Icons+Outlined" rel="stylesheet" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { sanitizeUrl } from './analytics.service';

describe('AnalyticsService', () => {
it('should redact the access token from URL', () => {
const url = 'https://example.com/#access_token=123';
expect(sanitizeUrl(url)).toEqual('https://example.com/#access_token=redacted');
});

it('should redact the join key from URL', () => {
const url = 'https://example.com/join/123';
expect(sanitizeUrl(url)).toEqual('https://example.com/join/redacted');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { Injectable } from '@angular/core';
import { environment } from '../environments/environment';
import { OnlineStatusService } from './online-status.service';

declare function gtag(...args: any): void;

// Using a type rather than interface because I intend to turn in into a union type later for each type of event that
// can be reported.
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
type EventParams = {
page_path: string;
};

@Injectable({ providedIn: 'root' })
export class AnalyticsService {
constructor(private readonly onlineStatus: OnlineStatusService) {}

/**
* Logs the page navigation event to the analytics service. This method is responsible for sanitizing the URL before
* logging it.
* @param url The URL of the page that was navigated to.
*/
logNavigation(url: string): void {
const sanitizedUrl = sanitizeUrl(url);
this.logEvent('page_view', { page_path: sanitizedUrl });

Check warning on line 25 in src/SIL.XForge.Scripture/ClientApp/src/xforge-common/analytics.service.ts

View check run for this annotation

Codecov / codecov/patch

src/SIL.XForge.Scripture/ClientApp/src/xforge-common/analytics.service.ts#L24-L25

Added lines #L24 - L25 were not covered by tests
}

private logEvent(eventName: string, eventParams: EventParams): void {
if (this.onlineStatus.isOnline && typeof environment.googleTagId === 'string') {
gtag(eventName, environment.googleTagId, eventParams);

Check warning on line 30 in src/SIL.XForge.Scripture/ClientApp/src/xforge-common/analytics.service.ts

View check run for this annotation

Codecov / codecov/patch

src/SIL.XForge.Scripture/ClientApp/src/xforge-common/analytics.service.ts#L30

Added line #L30 was not covered by tests
}
}
}

const redacted = 'redacted';

// redact access token from the hash
function redactAccessToken(url: string): string {
const urlObj = new URL(url);
const hash = urlObj.hash;

if (hash === '') return url;

const hashObj = new URLSearchParams(hash.slice(1));
const accessToken = hashObj.get('access_token');

if (accessToken === null) return url;

hashObj.set('access_token', redacted);
urlObj.hash = hashObj.toString();
return urlObj.toString();
}

function redactJoinKey(url: string): string {
const urlObj = new URL(url);
const pathParts = urlObj.pathname.split('/');
const joinIndex = pathParts.indexOf('join');

if (joinIndex === -1) {
return url;
}

pathParts[joinIndex + 1] = redacted;
urlObj.pathname = pathParts.join('/');
return urlObj.toString();
}

/**
* Redacts sensitive information from the given URL. Currently this only redacts the access token and the join key, so
* if relying on this method in the future, be sure to check that it is still redacting everything you need it to.
* @param url The URL to sanitize.
* @returns A sanitized version of the URL.
*/
export function sanitizeUrl(url: string): string {
return redactAccessToken(redactJoinKey(url));
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,8 @@ describe('ErrorReportingService', () => {
ErrorReportingService.beforeSend({}, event);
expect(event.breadcrumbs[0].metadata.from).toEqual('http://localhost:5000/somewhere&access_token=thing');
expect(event.breadcrumbs[0].metadata.to).toEqual('http://localhost:5000/somewhere');
expect(event.breadcrumbs[1].metadata.from).toEqual(
'http://localhost:5000/projects#access_token=redacted_for_error_report'
);
expect(event.breadcrumbs[1].metadata.from).toEqual('http://localhost:5000/projects#access_token=redacted');
expect(event.breadcrumbs[1].metadata.to).toEqual('http://localhost:5000/projects');
expect(event.request.url).toEqual('http://localhost:5000/projects#access_token=redacted_for_error_report');
expect(event.request.url).toEqual('http://localhost:5000/projects#access_token=redacted');
});
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Injectable } from '@angular/core';
import Bugsnag, { Event, NotifiableError } from '@bugsnag/js';
import { sanitizeUrl } from './analytics.service';

export interface EventMetadata {
[key: string]: object;
Expand All @@ -9,14 +10,14 @@ export interface EventMetadata {
providedIn: 'root'
})
export class ErrorReportingService {
static beforeSend(metaData: EventMetadata, event: Event): any {
static beforeSend(metaData: EventMetadata, event: Event): void {
if (typeof event.request.url === 'string') {
event.request.url = ErrorReportingService.redactAccessToken(event.request.url as string);
event.request.url = sanitizeUrl(event.request.url as string);
}
event.breadcrumbs = event.breadcrumbs.map(breadcrumb => {
if (breadcrumb.type === 'navigation' && breadcrumb.metadata && typeof breadcrumb.metadata.from === 'string') {
breadcrumb.metadata.from = ErrorReportingService.redactAccessToken(breadcrumb.metadata.from);
breadcrumb.metadata.to = ErrorReportingService.redactAccessToken(breadcrumb.metadata.to);
breadcrumb.metadata.from = sanitizeUrl(breadcrumb.metadata.from);
breadcrumb.metadata.to = sanitizeUrl(breadcrumb.metadata.to);
}
return breadcrumb;
});
Expand All @@ -40,10 +41,6 @@ export class ErrorReportingService {
} else return error;
}

private static redactAccessToken(url: string): string {
return url.replace(/^(.*#access_token=).*$/, '$1redacted_for_error_report');
}

private metadata: EventMetadata = {};

addMeta(data: object, tabName: string = 'custom'): void {
Expand Down
9 changes: 5 additions & 4 deletions src/SIL.XForge.Scripture/Pages/Shared/_Layout.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@

<head>
<environment include="Production">
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-22170471-15"></script>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-SVKBDV7K3Q"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() { dataLayer.push(arguments); }
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());

gtag('config', 'UA-22170471-15');
gtag('config', 'G-SVKBDV7K3Q');
</script>
</environment>

Expand Down
Loading