Skip to content

Commit

Permalink
feat: Rework analytics (#801)
Browse files Browse the repository at this point in the history
  • Loading branch information
mbriggs authored and mrmeku committed Aug 26, 2019
1 parent d5d07e1 commit 795bd63
Show file tree
Hide file tree
Showing 62 changed files with 1,604 additions and 295 deletions.
22 changes: 21 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ After cloning the project run: `yarn`.

After that, run `nps prepare.electron`. Every time you add or remove dependencies in electron/package.json, you will need to rerun `nps prepare.electron`.

After this, run `nps dev.up` to start the dev environment. The application will start the process listening on port 4200. The development is done in the browser, but the server uses electron.
After this, run `nps dev.up` to start the dev environment. The application will start the process listening on port 4200. The development is done in the browser, but the server uses electron. This is the most straight forward way of building new functionality.

To test the electron app, if you go to `dist/apps/electron` and run `electron .` you can have the app running in the electron environment.

### Running Unit Tests

Expand All @@ -38,6 +40,24 @@ Cypress, which we use to run e2e tests, records the videos of the tests ran on C

## Building Electron App

Before starting, be sure that you have run `nps prepare.electron` at least once (as previously mentioned).

Building the electron app requires a code signing certificate to be set. You can read more [here](https://www.electron.build/code-signing).

On macOS, you can export your root certificate by following these steps:

- Open "Keychain Access" using spotlight (or whatever)
- In the "Keychains" side panel, choose "logins"
- In the "Category" side panel, choose "My Certificates"
- There should at least be one certificate in the main area. expand it by clicking the triangle to the right
- Right click the key, and choose "Export"
- Choose a location and password to save it in .p12 format

Once you have a certificate saved, you can use it with:

- Setting `CSC_LINK` environment variable to the path to the file
- Setting `CSC_KEY_PASSWORD` environment variable to the password set (if any)

You can build the electron app by running `nps package.electronMac` or `nps package.electronWin`. Usually, you only need to do it locally if you change something related to electron-builder.

## Building VSCode Plugin
Expand Down
12 changes: 11 additions & 1 deletion angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,17 @@
"server": {
"root": "libs/server",
"sourceRoot": "libs/server/src",
"projectType": "library"
"projectType": "library",
"architect": {
"test": {
"builder": "@nrwl/jest:jest",
"options": {
"jestConfig": "libs/server/jest.config.js",
"tsConfig": "libs/server/tsconfig.spec.json",
"setupFile": "libs/server/src/test-setup.ts"
}
}
}
},
"environment": {
"root": "libs/environment",
Expand Down
16 changes: 5 additions & 11 deletions apps/angular-console/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
Telemetry
} from '@angular-console/utils';
import { HttpClientModule } from '@angular/common/http';
import { Inject, NgModule } from '@angular/core';
import { NgModule } from '@angular/core';
import {
MatIconModule,
MatListModule,
Expand All @@ -45,23 +45,21 @@ export function initApollo(
messenger: Messenger,
httpLink: HttpLink
) {
telemetry.setUpRouterLogging();

const errorLink = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors) {
graphQLErrors.forEach(error => {
messenger.error(error.message);
telemetry.reportException(error.message);
telemetry.exceptionOccured(error.message);
});
} else if (networkError) {
const n: any = networkError;
messenger.error('Angular Console Server was shutdown');
if (n.error && n.error.errors && n.error.errors.length > 0) {
const message = n.error.errors[0].message;
telemetry.reportException(message);
telemetry.exceptionOccured(message);
console.error(message);
} else {
telemetry.reportException(n.message);
telemetry.exceptionOccured(n.message);
console.error(n.message);
}
}
Expand Down Expand Up @@ -115,14 +113,10 @@ export function initApollo(
],
providers: [
IsNodeJsInstalledGuard,
{
provide: 'telemetry',
useClass: Telemetry
},
{
provide: APOLLO_OPTIONS,
useFactory: initApollo,
deps: [[new Inject('telemetry')], Messenger, HttpLink]
deps: [Telemetry, Messenger, HttpLink]
},
{ provide: ENVIRONMENT, useValue: environment as Environment },
{ provide: IS_VSCODE, useValue: environment.application === 'vscode' },
Expand Down
3 changes: 2 additions & 1 deletion apps/electron/src/app/start-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export async function startServer(
const assetsPath = path.join(__dirname, 'assets/public');
const providers = [
{ provide: 'serverAddress', useValue: `http://localhost:${port}` },
{ provide: 'telemetry', useValue: telemetry },
{ provide: 'store', useValue: store },
{ provide: 'selectDirectory', useValue: selectDirectory },
{
Expand Down Expand Up @@ -70,7 +71,7 @@ export async function startServer(
console.log(`Listening on port ${port}`);
});
} catch (e) {
telemetry.reportException(`Start Server: ${e.message}`);
telemetry.exceptionOccured(`Start Server: ${e.message}`);
throw e;
}
}
3 changes: 2 additions & 1 deletion apps/electron/src/environments/environment.prod.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export const environment = {
production: true
production: true,
disableTelemetry: false
};
3 changes: 2 additions & 1 deletion apps/electron/src/environments/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
// The list of file replacements can be found in `angular.json`.

export const environment = {
production: false
production: false,
disableTelemetry: true
};
38 changes: 13 additions & 25 deletions apps/electron/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,42 +4,31 @@ import {
storeSettings,
Telemetry
} from '@angular-console/server';
import { app, BrowserWindow, dialog, ipcMain, Menu } from 'electron';
import { app, BrowserWindow, dialog, Menu } from 'electron';
import * as ElectronStore from 'electron-store';
import { environment } from './environments/environment';
import { autoUpdater } from 'electron-updater';
import { statSync } from 'fs';
import { platform } from 'os';
import * as path from 'path';

import { startServer } from './app/start-server';

const start = process.hrtime();
const fixPath = require('fix-path');
const getPort = require('get-port');

const store = new ElectronStore();
const telemetry = new Telemetry(store);

const telemetry = environment.disableTelemetry
? Telemetry.withLogger(store)
: Telemetry.withGoogleAnalytics(store, 'electron');

export let mainWindow: BrowserWindow;

fixPath();
const currentDirectory = process.cwd();

function setupEvents() {
process.env.trackingID = 'UA-88380372-8';
ipcMain.on('event', (_: any, arg: any) =>
telemetry.reportEvent(arg.category, arg.action, arg.label, arg.value)
);
ipcMain.on('dataCollectionEvent', (_: any, arg: any) =>
telemetry.dataCollectionEvent(arg.value)
);
ipcMain.on('reportPageView', (_: any, arg: any) =>
telemetry.reportPageView(arg.path)
);
ipcMain.on('reportException', (_: any, arg: any) =>
telemetry.reportException(arg.description)
);
}

function createMenu() {
let menu = [];
const name = app.getName();
Expand Down Expand Up @@ -149,7 +138,7 @@ function createWindow() {
});
} catch (e) {
showCloseDialog(`Error when starting Angular Console: ${e.message}`);
telemetry.reportException(`Start failed: ${e.message}`);
telemetry.exceptionOccured(`Start failed: ${e.message}`);
}
});

Expand Down Expand Up @@ -190,7 +179,6 @@ function showRestartDialog() {
dialog.showMessageBox(dialogOptions, i => {
if (i === 0) {
// Restart
telemetry.reportLifecycleEvent('QuitAndInstall');
autoUpdater.quitAndInstall();
}
});
Expand All @@ -200,7 +188,7 @@ function checkForUpdates() {
setTimeout(async () => {
autoUpdater.channel = getUpdateChannel();
autoUpdater.allowDowngrade = false;
if (process.env.NODE_ENV !== 'development') {
if (!environment.production) {
try {
const r = await autoUpdater.checkForUpdates();
if (r.downloadPromise) {
Expand All @@ -216,7 +204,7 @@ function checkForUpdates() {
console.log('checkForUpdates is called. downloadPromise is null.');
}
} catch (e) {
telemetry.reportException(e);
telemetry.exceptionOccured(e.message);
}
}
}, 0);
Expand Down Expand Up @@ -245,7 +233,7 @@ function saveWindowInfo() {
try {
store.set('windowBounds', JSON.stringify(mainWindow.getBounds()));
} catch (e) {
telemetry.reportException(`Saving window bounds failed: ${e.message}`);
telemetry.exceptionOccured(`Saving window bounds failed: ${e.message}`);
}
}
}
Expand All @@ -260,10 +248,10 @@ app.on('ready', async () => {
}
startServer(port, telemetry, store, mainWindow);
} else {
setupEvents();
telemetry.reportLifecycleEvent('StartSession');
createMenu();
createWindow();
checkForUpdates();
const time = process.hrtime(start);
telemetry.appLoaded(time[0]);
}
});
1 change: 1 addition & 0 deletions apps/electron/src/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"name": "angular-console",
"description": "Angular Console",
"homepage": "https://angularconsole.com",
"version": "8.0.0",
"author": {
"name": "Narwhal Technologies Inc",
Expand Down
10 changes: 0 additions & 10 deletions apps/vscode/src/app/get-store-for-context.ts

This file was deleted.

30 changes: 19 additions & 11 deletions apps/vscode/src/app/start-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ import {
createServerModule,
QueryResolver,
SelectDirectory,
Commands,
Telemetry,
PseudoTerminalFactory
} from '@angular-console/server';
import { NestFactory } from '@nestjs/core';
import * as path from 'path';
import { commands, ExtensionContext, window } from 'vscode';

import { getStoreForContext } from './get-store-for-context';
import { environment } from '../environments/environment';
import { executeTask } from './pseudo-terminal.factory';
import { VSCodeStorage } from './vscode-storage';

function getPseudoTerminalFactory(): PseudoTerminalFactory {
return config => executeTask(config);
Expand All @@ -23,7 +24,10 @@ export async function startServer(
workspacePath?: string
) {
const port = await getPort({ port: environment.production ? 8888 : 8889 });
const store = getStoreForContext(context);
const store = VSCodeStorage.fromContext(context);
const telemetry = environment.disableTelemetry
? Telemetry.withLogger(store)
: Telemetry.withGoogleAnalytics(store, 'vscode');

const selectDirectory: SelectDirectory = async ({ buttonLabel }) => {
return await window
Expand Down Expand Up @@ -79,15 +83,19 @@ export async function startServer(

const assetsPath = path.join(context.extensionPath, 'assets', 'public');

const queryResolver = new QueryResolver(store);

// Pre-warm cache for workspace.
if (workspacePath) {
queryResolver.workspace(workspacePath, {});
}

const providers = [
{ provide: QueryResolver, useValue: queryResolver },
{
provide: QueryResolver,
useFactory: (commandsController: Commands) => {
const resolver = new QueryResolver(store, commandsController);
if (workspacePath) {
resolver.workspace(workspacePath, {});
}
return resolver;
},
inject: ['commands']
},
{ provide: 'telemetry', useValue: telemetry },
{ provide: 'serverAddress', useValue: `http://localhost:${port}` },
{ provide: 'store', useValue: store },
{ provide: 'selectDirectory', useValue: selectDirectory },
Expand Down
42 changes: 42 additions & 0 deletions apps/vscode/src/app/vscode-storage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { ExtensionContext } from 'vscode';
import { Store } from '@nrwl/angular-console-enterprise-electron';

export class VSCodeStorage implements Store {
static fromContext(context: ExtensionContext): VSCodeStorage {
const store = new VSCodeStorage(context.globalState);
return store;
}

constructor(private readonly state: VSCGlobalState) {}

get<T>(key: string, defaultValue?: T): T | null {
const value = this.state.get(key, defaultValue);
return value || defaultValue || null;
}

set<T>(key: string, value: T): void {
this.state.update(key, value);
}

delete(key: string): void {
this.state.update(key, undefined);
}
}

export interface VSCGlobalState {
get<T>(key: string): T | undefined;
get<T>(key: string, defaultValue: T): T;
update(key: string, value: any): void;
}

export class SubstituteGlobalState implements VSCGlobalState {
state: { [key: string]: any } = {};

get<T>(key: string, defaultValue?: T): T {
return this.state[key] || defaultValue;
}

update<T>(key: string, value: T): void {
this.state[key] = value;
}
}
3 changes: 2 additions & 1 deletion apps/vscode/src/environments/environment.prod.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export const environment = {
production: true
production: true,
disableTelemetry: false
};
3 changes: 2 additions & 1 deletion apps/vscode/src/environments/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
// The list of file replacements can be found in `angular.json`.

export const environment = {
production: false
production: false,
disableTelemetry: true
};
Loading

0 comments on commit 795bd63

Please sign in to comment.