diff --git a/PublicAPI.md b/PublicAPI.md index 206287aee4..70542d1b3a 100644 --- a/PublicAPI.md +++ b/PublicAPI.md @@ -35,6 +35,8 @@ const tns = require("nativescript"); * [disableDebugging](#disableDebugging) * [getLiveSyncDeviceDescriptors](#getLiveSyncDeviceDescriptors) * [events](#events) +* [analyticsSettingsService](#analyticsSettingsService) + * [getClientId](#getClientId) ## Module projectService @@ -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} Client identifier in UUIDv4 standard. + */ +getClientId(): Promise; +``` + +* 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. diff --git a/lib/bootstrap.ts b/lib/bootstrap.ts index 643a0ea7d2..aa52fd1939 100644 --- a/lib/bootstrap.ts +++ b/lib/bootstrap.ts @@ -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"); diff --git a/lib/services/analytics-settings-service.ts b/lib/services/analytics-settings-service.ts index 6ca50db27e..250e4b0db0 100644 --- a/lib/services/analytics-settings-service.ts +++ b/lib/services/analytics-settings-service.ts @@ -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_"; @@ -15,6 +16,7 @@ class AnalyticsSettingsService implements IAnalyticsSettingsService { return this.getSettingValueOrDefault("USER_ID"); } + @exported("analyticsSettingsService") public getClientId(): Promise { return this.getSettingValueOrDefault(this.$staticConfig.ANALYTICS_INSTALLATION_ID_SETTING_NAME); } diff --git a/lib/services/analytics/google-analytics-cross-client-custom-dimensions.d.ts b/lib/services/analytics/google-analytics-cross-client-custom-dimensions.d.ts new file mode 100644 index 0000000000..c58e2b54c0 --- /dev/null +++ b/lib/services/analytics/google-analytics-cross-client-custom-dimensions.d.ts @@ -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", +} diff --git a/lib/services/analytics/google-analytics-custom-dimensions.ts b/lib/services/analytics/google-analytics-custom-dimensions.d.ts similarity index 69% rename from lib/services/analytics/google-analytics-custom-dimensions.ts rename to lib/services/analytics/google-analytics-custom-dimensions.d.ts index 9e7c1d7007..488439b814 100644 --- a/lib/services/analytics/google-analytics-custom-dimensions.ts +++ b/lib/services/analytics/google-analytics-custom-dimensions.d.ts @@ -1,4 +1,4 @@ -const enum GoogleAnalyticsCustomDimensions { +declare const enum GoogleAnalyticsCustomDimensions { cliVersion = "cd1", projectType = "cd2", clientID = "cd3", diff --git a/lib/services/analytics/google-analytics-provider.ts b/lib/services/analytics/google-analytics-provider.ts index 1bb8d9fe13..8f93ab4388 100644 --- a/lib/services/analytics/google-analytics-provider.ts +++ b/lib/services/analytics/google-analytics-provider.ts @@ -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 { + 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 { 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: @@ -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 }; @@ -50,6 +72,18 @@ export class GoogleAnalyticsProvider implements IGoogleAnalyticsProvider { }); } + private async setCrossClientCustomDimensions(visitor: ua.Visitor, sessionId: string): Promise { + 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 { return new Promise((resolve, reject) => { visitor.event(trackInfo.category, trackInfo.action, trackInfo.label, trackInfo.value, { p: this.currentPage }, (err: Error) => { diff --git a/test/nativescript-cli-lib.ts b/test/nativescript-cli-lib.ts index 10854e48f0..da541f223a 100644 --- a/test/nativescript-cli-lib.ts +++ b/test/nativescript-cli-lib.ts @@ -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, "\\\\");