Skip to content

Add cross client analytics tracking #3176

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

Merged
merged 7 commits into from
Oct 24, 2017
22 changes: 22 additions & 0 deletions PublicAPI.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ const tns = require("nativescript");
* [disableDebugging](#disableDebugging)
* [getLiveSyncDeviceDescriptors](#getLiveSyncDeviceDescriptors)
* [events](#events)
* [analyticsSettingsService](#analyticsSettingsService)
* [getClientId](#getClientId)


## Module projectService
Expand Down Expand Up @@ -855,6 +857,26 @@ tns.liveSyncService.on("debuggerDetached", debugInfo => {
console.log(`Detached debugger for device with id ${debugInfo.deviceIdentifier}`);
});
```
## analyticsSettingsService
Provides methods for accessing the analytics settings file data.

### getClientId
The `getClientId` method allows retrieving the clientId used in the analytics tracking

* Definition:
```TypeScript
/**
* Gets the clientId used for analytics tracking
* @returns {Promise<string>} Client identifier in UUIDv4 standard.
*/
getClientId(): Promise<string>;
```

* Usage:
```JavaScript
tns.analyticsSettingsService.getClientId()
.then(clientId => console.log(clientId));
```

## How to add a new method to Public API
CLI is designed as command line tool and when it is used as a library, it does not give you access to all of the methods. This is mainly implementation detail. Most of the CLI's code is created to work in command line, not as a library, so before adding method to public API, most probably it will require some modification.
Expand Down
2 changes: 1 addition & 1 deletion lib/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ $injector.require("iOSDebugService", "./services/ios-debug-service");
$injector.require("androidDebugService", "./services/android-debug-service");

$injector.require("userSettingsService", "./services/user-settings-service");
$injector.require("analyticsSettingsService", "./services/analytics-settings-service");
$injector.requirePublic("analyticsSettingsService", "./services/analytics-settings-service");
$injector.require("analyticsService", "./services/analytics/analytics-service");
$injector.require("eqatecAnalyticsProvider", "./services/analytics/eqatec-analytics-provider");
$injector.require("googleAnalyticsProvider", "./services/analytics/google-analytics-provider");
Expand Down
2 changes: 2 additions & 0 deletions lib/services/analytics-settings-service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { createGUID } from "../common/helpers";
import { exported } from "../common/decorators";

class AnalyticsSettingsService implements IAnalyticsSettingsService {
private static SESSIONS_STARTED_KEY_PREFIX = "SESSIONS_STARTED_";
Expand All @@ -15,6 +16,7 @@ class AnalyticsSettingsService implements IAnalyticsSettingsService {
return this.getSettingValueOrDefault("USER_ID");
}

@exported("analyticsSettingsService")
public getClientId(): Promise<string> {
return this.getSettingValueOrDefault(this.$staticConfig.ANALYTICS_INSTALLATION_ID_SETTING_NAME);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Sync indexes with the custom dimensions of the cross client analytics project
declare const enum GoogleAnalyticsCrossClientCustomDimensions {
sessionId = "cd9",
clientId = "cd10",
crossClientId = "cd12",
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const enum GoogleAnalyticsCustomDimensions {
declare const enum GoogleAnalyticsCustomDimensions {
cliVersion = "cd1",
projectType = "cd2",
clientID = "cd3",
Expand Down
44 changes: 39 additions & 5 deletions lib/services/analytics/google-analytics-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,46 @@ import { AnalyticsClients } from "../../common/constants";

export class GoogleAnalyticsProvider implements IGoogleAnalyticsProvider {
private static GA_TRACKING_ID = "UA-111455-44";
private static GA_CROSS_CLIENT_TRACKING_ID = "UA-111455-51";
private currentPage: string;

constructor(private clientId: string,
private $staticConfig: IStaticConfig,
private $hostInfo: IHostInfo,
private $osInfo: IOsInfo) {
private $osInfo: IOsInfo,
private $logger: ILogger) {
}

public async trackHit(trackInfo: IGoogleAnalyticsData): Promise<void> {
const trackingIds = [GoogleAnalyticsProvider.GA_TRACKING_ID, GoogleAnalyticsProvider.GA_CROSS_CLIENT_TRACKING_ID];
const sessionId = uuid.v4();

for (const gaTrackingId of trackingIds) {
try {
await this.track(gaTrackingId, trackInfo, sessionId);
} catch (e) {
this.$logger.trace("Analytics exception: ", e);
}
}
}

private async track(gaTrackingId: string, trackInfo: IGoogleAnalyticsData, sessionId: string): Promise<void> {
const visitor = ua({
tid: GoogleAnalyticsProvider.GA_TRACKING_ID,
tid: gaTrackingId,
cid: this.clientId,
headers: {
["User-Agent"]: this.getUserAgentString()
}
});

this.setCustomDimensions(visitor, trackInfo.customDimensions);
switch (gaTrackingId) {
case GoogleAnalyticsProvider.GA_CROSS_CLIENT_TRACKING_ID:
this.setCrossClientCustomDimensions(visitor, sessionId);
break;
default:
this.setCustomDimensions(visitor, trackInfo.customDimensions, sessionId);
break;
}

switch (trackInfo.googleAnalyticsDataType) {
case GoogleAnalyticsDataType.Page:
Expand All @@ -33,13 +55,13 @@ export class GoogleAnalyticsProvider implements IGoogleAnalyticsProvider {
}
}

private setCustomDimensions(visitor: ua.Visitor, customDimensions: IStringDictionary): void {
private setCustomDimensions(visitor: ua.Visitor, customDimensions: IStringDictionary, sessionId: string): void {
const defaultValues: IStringDictionary = {
[GoogleAnalyticsCustomDimensions.cliVersion]: this.$staticConfig.version,
[GoogleAnalyticsCustomDimensions.nodeVersion]: process.version,
[GoogleAnalyticsCustomDimensions.clientID]: this.clientId,
[GoogleAnalyticsCustomDimensions.projectType]: null,
[GoogleAnalyticsCustomDimensions.sessionID]: uuid.v4(),
[GoogleAnalyticsCustomDimensions.sessionID]: sessionId,
[GoogleAnalyticsCustomDimensions.client]: AnalyticsClients.Unknown
};

Expand All @@ -50,6 +72,18 @@ export class GoogleAnalyticsProvider implements IGoogleAnalyticsProvider {
});
}

private async setCrossClientCustomDimensions(visitor: ua.Visitor, sessionId: string): Promise<void> {
const customDimensions: IStringDictionary = {
[GoogleAnalyticsCrossClientCustomDimensions.sessionId]: sessionId,
[GoogleAnalyticsCrossClientCustomDimensions.clientId]: this.clientId,
[GoogleAnalyticsCrossClientCustomDimensions.crossClientId]: this.clientId,
};

_.each(customDimensions, (value, key) => {
visitor.set(key, value);
});
}

private trackEvent(visitor: ua.Visitor, trackInfo: IGoogleAnalyticsEventData): Promise<void> {
return new Promise<void>((resolve, reject) => {
visitor.event(trackInfo.category, trackInfo.action, trackInfo.label, trackInfo.value, { p: this.currentPage }, (err: Error) => {
Expand Down
3 changes: 2 additions & 1 deletion test/nativescript-cli-lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ describe("nativescript-cli-lib", () => {
npm: ["install", "uninstall", "view", "search"],
extensibilityService: ["loadExtensions", "loadExtension", "getInstalledExtensions", "installExtension", "uninstallExtension"],
liveSyncService: ["liveSync", "stopLiveSync", "enableDebugging", "disableDebugging", "attachDebugger"],
debugService: ["debug"]
debugService: ["debug"],
analyticsSettingsService: ["getClientId"]
};

const pathToEntryPoint = path.join(__dirname, "..", "lib", "nativescript-cli-lib.js").replace(/\\/g, "\\\\");
Expand Down