Skip to content

feat: track data from preview app on preview and run commands #4287

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
Jan 30, 2019
Merged
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
7 changes: 5 additions & 2 deletions lib/commands/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,18 @@ export class PreviewCommand implements ICommand {
public allowedParameters: ICommandParameter[] = [];
private static MIN_SUPPORTED_WEBPACK_VERSION = "0.17.0";

constructor(private $bundleValidatorHelper: IBundleValidatorHelper,
constructor(private $analyticsService: IAnalyticsService,
private $bundleValidatorHelper: IBundleValidatorHelper,
private $errors: IErrors,
private $liveSyncService: ILiveSyncService,
private $logger: ILogger,
private $networkConnectivityValidator: INetworkConnectivityValidator,
private $projectData: IProjectData,
private $options: IOptions,
private $previewAppLogProvider: IPreviewAppLogProvider,
private $previewQrCodeService: IPreviewQrCodeService) { }
private $previewQrCodeService: IPreviewQrCodeService) {
this.$analyticsService.setShouldDispose(this.$options.justlaunch || !this.$options.watch);
}

public async execute(): Promise<void> {
this.$previewAppLogProvider.on(DEVICE_LOG_EVENT_NAME, (deviceId: string, message: string) => {
Expand Down
5 changes: 4 additions & 1 deletion lib/commands/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ export class RunCommandBase implements ICommand {
private liveSyncCommandHelperAdditionalOptions: ILiveSyncCommandHelperAdditionalOptions = <ILiveSyncCommandHelperAdditionalOptions>{};

public platform: string;
constructor(private $projectData: IProjectData,
constructor(
private $analyticsService: IAnalyticsService,
private $projectData: IProjectData,
private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
private $errors: IErrors,
private $hostInfo: IHostInfo,
Expand All @@ -15,6 +17,7 @@ export class RunCommandBase implements ICommand {

public allowedParameters: ICommandParameter[] = [];
public async execute(args: string[]): Promise<void> {
await this.$analyticsService.trackPreviewAppData(this.platform, this.$projectData.projectDir);
return this.$liveSyncCommandHelper.executeCommandLiveSync(this.platform, this.liveSyncCommandHelperAdditionalOptions);
}

Expand Down
10 changes: 10 additions & 0 deletions lib/common/declarations.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,11 @@ declare const enum TrackingTypes {
*/
GoogleAnalyticsData = "googleAnalyticsData",

/**
* Defines that the broker process should get and track the data from preview app to Google Analytics
*/
PreviewAppData = "PreviewAppData",

/**
* Defines that all information has been sent and no more data will be tracked in current session.
*/
Expand Down Expand Up @@ -690,6 +695,11 @@ interface IAnalyticsService {
*/
trackEventActionInGoogleAnalytics(data: IEventActionData): Promise<void>;

/**
* Tracks preview's app data to Google Analytics project.
*/
trackPreviewAppData(platform: string, projectDir: string): Promise<void>

/**
* Defines if the instance should be disposed.
* @param {boolean} shouldDispose Defines if the instance should be disposed and the child processes should be disconnected.
Expand Down
5 changes: 5 additions & 0 deletions lib/common/definitions/google-analytics.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ interface IGoogleAnalyticsData {
customDimensions?: IStringDictionary;
}

interface IPreviewAppGoogleAnalyticsData {
platform: string;
additionalData?: string;
}

/**
* Describes information about event that should be tracked.
*/
Expand Down
1 change: 1 addition & 0 deletions lib/common/definitions/mobile.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ declare module Mobile {
interface IDeviceFileSystem {
listFiles(devicePath: string, appIdentifier?: string): Promise<any>;
getFile(deviceFilePath: string, appIdentifier: string, outputFilePath?: string): Promise<void>;
getFileContent(deviceFilePath: string, appIdentifier: string): Promise<string>;
putFile(localFilePath: string, deviceFilePath: string, appIdentifier: string): Promise<void>;
deleteFile(deviceFilePath: string, appIdentifier: string): Promise<void>;
transferFiles(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]): Promise<Mobile.ILocalToDevicePathData[]>;
Expand Down
5 changes: 5 additions & 0 deletions lib/common/mobile/android/android-device-file-system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ export class AndroidDeviceFileSystem implements Mobile.IDeviceFileSystem {
}
}

public async getFileContent(deviceFilePath: string, appIdentifier: string): Promise<string> {
const result = await this.adb.executeShellCommand(["cat", deviceFilePath]);
return result;
}

public async putFile(localFilePath: string, deviceFilePath: string, appIdentifier: string): Promise<void> {
await this.adb.pushFile(localFilePath, deviceFilePath);
}
Expand Down
18 changes: 11 additions & 7 deletions lib/common/mobile/ios/device/ios-device-file-system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,19 @@ export class IOSDeviceFileSystem implements Mobile.IDeviceFileSystem {
}

public async getFile(deviceFilePath: string, appIdentifier: string, outputFilePath?: string): Promise<void> {
if (!outputFilePath) {
const result = await this.$iosDeviceOperations.readFiles([{ deviceId: this.device.deviceInfo.identifier, path: deviceFilePath, appId: appIdentifier }]);
const response = result[this.device.deviceInfo.identifier][0];
if (response) {
this.$logger.out(response.response);
}
} else {
if (outputFilePath) {
await this.$iosDeviceOperations.downloadFiles([{ appId: appIdentifier, deviceId: this.device.deviceInfo.identifier, source: deviceFilePath, destination: outputFilePath }]);
return;
}

const fileContent = await this.getFileContent(deviceFilePath, appIdentifier);
this.$logger.out(fileContent);
}

public async getFileContent(deviceFilePath: string, appIdentifier: string): Promise<string> {
const result = await this.$iosDeviceOperations.readFiles([{ deviceId: this.device.deviceInfo.identifier, path: deviceFilePath, appId: appIdentifier }]);
const response = result[this.device.deviceInfo.identifier][0];
return response.response;
}

public async putFile(localFilePath: string, deviceFilePath: string, appIdentifier: string): Promise<void> {
Expand Down
7 changes: 6 additions & 1 deletion lib/common/mobile/ios/simulator/ios-simulator-file-system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ export class IOSSimulatorFileSystem implements Mobile.IDeviceFileSystem {
}
}

public async getFileContent(deviceFilePath: string, appIdentifier: string): Promise<string> {
const result = this.$fs.readText(deviceFilePath);
return result;
}

public async putFile(localFilePath: string, deviceFilePath: string, appIdentifier: string): Promise<void> {
shelljs.cp("-f", localFilePath, deviceFilePath);
}
Expand All @@ -27,7 +32,7 @@ export class IOSSimulatorFileSystem implements Mobile.IDeviceFileSystem {
public async transferFiles(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]): Promise<Mobile.ILocalToDevicePathData[]> {
await Promise.all(
_.map(localToDevicePaths, localToDevicePathData => this.transferFile(localToDevicePathData.getLocalPath(), localToDevicePathData.getDevicePath())
));
));
return localToDevicePaths;
}

Expand Down
3 changes: 2 additions & 1 deletion lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,8 @@ export const enum TrackActionNames {
CheckEnvironmentRequirements = "Check Environment Requirements",
Options = "Options",
AcceptTracking = "Accept Tracking",
Performance = "Performance"
Performance = "Performance",
PreviewAppData = "Preview App Data"
}

export const AnalyticsEventLabelDelimiter = "__";
Expand Down
34 changes: 33 additions & 1 deletion lib/services/analytics/analytics-broker-process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,47 @@ const finishTracking = async (data?: ITrackingInformation) => {
}
};

const trackPreviewAppData = async (data: any) => {
const mobileHelper = $injector.resolve<Mobile.IMobileHelper>("mobileHelper");
const devicesService = $injector.resolve<Mobile.IDevicesService>("devicesService");
await devicesService.initialize({ platform: data.platform, skipDeviceDetectionInterval: true, skipEmulatorStart: true });

const devices = await devicesService.getDevicesForPlatform(data.platform);
_.each(devices, async (device: Mobile.IDevice) => {
try {
let previewAppFilePath = null;
if (mobileHelper.isAndroidPlatform(device.deviceInfo.platform)) {
previewAppFilePath = "/sdcard/org.nativescript.preview/device.json";
} else if (mobileHelper.isiOSPlatform(device.deviceInfo.platform)) {
previewAppFilePath = "Documents/device.json";
}

const previewAppFileContent = await device.fileSystem.getFileContent(previewAppFilePath, "org.nativescript.preview");
const previewAppDeviceId = JSON.parse(previewAppFileContent).id;
data.label += `_${previewAppDeviceId}`;

analyticsLoggingService.logData({ message: `analytics-broker-process will send the data from preview app: ${data}` });
await sendDataForTracking(data);
} catch (err) {
// ignore the error
}
});
};

process.on("message", async (data: ITrackingInformation) => {
analyticsLoggingService.logData({ message: `analytics-broker-process received message of type: ${data.type}` });
analyticsLoggingService.logData({ message: `analytics-broker-process received message of type: ${JSON.stringify(data)}` });

if (data.type === TrackingTypes.Finish) {
receivedFinishMsg = true;
await finishTracking(data);
return;
}

if (data.type === TrackingTypes.PreviewAppData) {
await trackPreviewAppData(<IPreviewAppTrackingInformation>data);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add return

return;
}

await sendDataForTracking(data);
});

Expand Down
20 changes: 19 additions & 1 deletion lib/services/analytics/analytics-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ export class AnalyticsService implements IAnalyticsService, IDisposable {
const platform = device ? device.deviceInfo.platform : data.platform;
const normalizedPlatform = platform ? this.$mobileHelper.normalizePlatformName(platform) : platform;
const isForDevice = device ? !device.isEmulator : data.isForDevice;

let label: string = "";
label = this.addDataToLabel(label, normalizedPlatform);

Expand Down Expand Up @@ -129,6 +128,25 @@ export class AnalyticsService implements IAnalyticsService, IDisposable {
await this.trackInGoogleAnalytics(googleAnalyticsEventData);
}

public async trackPreviewAppData(platform: string, projectDir: string): Promise<void> {
const customDimensions: IStringDictionary = {};
this.setProjectRelatedCustomDimensions(customDimensions, projectDir);

let label: string = "";
label = this.addDataToLabel(label, this.$mobileHelper.normalizePlatformName(platform));

const eventActionData = {
googleAnalyticsDataType: GoogleAnalyticsDataType.Event,
action: TrackActionNames.PreviewAppData,
platform,
label,
customDimensions,
type: TrackingTypes.PreviewAppData
};

await this.trackInGoogleAnalytics(eventActionData);
}

private forcefullyTrackInGoogleAnalytics(gaSettings: IGoogleAnalyticsData): Promise<void> {
gaSettings.customDimensions = gaSettings.customDimensions || {};
gaSettings.customDimensions[GoogleAnalyticsCustomDimensions.client] = this.$options.analyticsClient || (isInteractive() ? AnalyticsClients.Cli : AnalyticsClients.Unknown);
Expand Down
2 changes: 2 additions & 0 deletions lib/services/analytics/analytics.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ interface IAnalyticsBroker {

interface IGoogleAnalyticsTrackingInformation extends IGoogleAnalyticsData, ITrackingInformation { }

interface IPreviewAppTrackingInformation extends IPreviewAppGoogleAnalyticsData, ITrackingInformation { }

/**
* Describes methods required to track in Google Analytics.
*/
Expand Down
11 changes: 10 additions & 1 deletion lib/services/livesync/playground/preview-app-livesync-service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as path from "path";
import { Device, FilesPayload } from "nativescript-preview-sdk";
import { APP_RESOURCES_FOLDER_NAME, APP_FOLDER_NAME } from "../../../constants";
import { APP_RESOURCES_FOLDER_NAME, APP_FOLDER_NAME, TrackActionNames } from "../../../constants";
import { PreviewAppLiveSyncEvents } from "./preview-app-constants";
import { HmrConstants } from "../../../common/constants";
import { stringify } from "../../../common/helpers";
Expand All @@ -12,6 +12,7 @@ export class PreviewAppLiveSyncService extends EventEmitter implements IPreviewA
private deviceInitializationPromise: IDictionary<Promise<FilesPayload>> = {};

constructor(
private $analyticsService: IAnalyticsService,
private $errors: IErrors,
private $hooksService: IHooksService,
private $logger: ILogger,
Expand All @@ -37,6 +38,14 @@ export class PreviewAppLiveSyncService extends EventEmitter implements IPreviewA
return this.deviceInitializationPromise[device.id];
}

if (device.uniqueId) {
await this.$analyticsService.trackEventActionInGoogleAnalytics({
action: TrackActionNames.PreviewAppData,
platform: device.platform,
additionalData: device.uniqueId
});
}

this.deviceInitializationPromise[device.id] = this.getInitialFilesForDevice(data, device);
try {
const payloads = await this.deviceInitializationPromise[device.id];
Expand Down
19 changes: 16 additions & 3 deletions npm-shrinkwrap.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
"mkdirp": "0.5.1",
"mute-stream": "0.0.5",
"nativescript-doctor": "1.8.1",
"nativescript-preview-sdk": "0.3.2",
"nativescript-preview-sdk": "0.3.3",
"open": "0.0.5",
"ora": "2.0.0",
"osenv": "0.1.3",
Expand Down
3 changes: 3 additions & 0 deletions test/services/playground/preview-app-livesync-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ function createTestInjector(options?: {
getConnectedDevices: () => [deviceMockData]
});
injector.register("previewAppFilesService", PreviewAppFilesService);
injector.register("analyticsService", {
trackEventActionInGoogleAnalytics: () => ({})
});

return injector;
}
Expand Down
3 changes: 2 additions & 1 deletion test/services/playground/preview-app-plugins-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ function createDevice(plugins: string): Device {
previewAppVersion: "28.0.0",
runtimeVersion: "4.3.0",
plugins,
pluginsExpanded: false
pluginsExpanded: false,
uniqueId: "testId"
};
}

Expand Down
3 changes: 2 additions & 1 deletion test/services/preview-devices-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ function createDevice(id: string): Device {
name: "my test name",
osVersion: "10.0.0",
previewAppVersion: "19.0.0",
runtimeVersion: "5.0.0"
runtimeVersion: "5.0.0",
uniqueId: "testUniqueId"
};
}

Expand Down