diff --git a/packages/angular/cli/models/analytics.ts b/packages/angular/cli/models/analytics.ts index 064044f365dd..7a151b4f5d53 100644 --- a/packages/angular/cli/models/analytics.ts +++ b/packages/angular/cli/models/analytics.ts @@ -565,6 +565,67 @@ export async function getGlobalAnalytics(): Promise { + try { + const globalWorkspace = await getWorkspace('local'); + const analyticsConfig: string | undefined | null | { uid?: string } = globalWorkspace + && globalWorkspace.getCli() + && globalWorkspace.getCli()['analytics']; + + if (analyticsConfig !== undefined) { + return true; + } + } catch {} + + return false; +} + +/** + * Get the workspace analytics object for the user. This returns an instance of UniversalAnalytics, + * or undefined if analytics are disabled. + * + * If any problem happens, it is considered the user has been opting out of analytics. + */ +export async function getWorkspaceAnalytics(): Promise { + analyticsDebug('getWorkspaceAnalytics'); + try { + const globalWorkspace = await getWorkspace('local'); + const analyticsConfig: string | undefined | null | { uid?: string } = globalWorkspace + && globalWorkspace.getCli() + && globalWorkspace.getCli()['analytics']; + analyticsDebug('Workspace Analytics config found: %j', analyticsConfig); + + if (analyticsConfig === false) { + analyticsDebug('Analytics disabled. Ignoring all analytics.'); + + return undefined; + } else if (analyticsConfig === undefined || analyticsConfig === null) { + analyticsDebug('Analytics settings not found. Ignoring all analytics.'); + + return undefined; + } else { + let uid: string | undefined = undefined; + if (typeof analyticsConfig == 'string') { + uid = analyticsConfig; + } else if (typeof analyticsConfig == 'object' && typeof analyticsConfig['uid'] == 'string') { + uid = analyticsConfig['uid']; + } + + analyticsDebug('client id: %j', uid); + if (uid == undefined) { + return undefined; + } + + return new UniversalAnalytics(AnalyticsProperties.AngularCliDefault, uid); + } + } catch (err) { + analyticsDebug('Error happened during reading of analytics config: %s', err.message); + + return undefined; + } + +} + /** * Return the usage analytics sharing setting, which is either a property string (GA-XXXXXXX-XX), * or undefined if no sharing. diff --git a/packages/angular/cli/models/command-runner.ts b/packages/angular/cli/models/command-runner.ts index 9fe02781e06f..acc13742f42a 100644 --- a/packages/angular/cli/models/command-runner.ts +++ b/packages/angular/cli/models/command-runner.ts @@ -19,7 +19,13 @@ import * as debug from 'debug'; import { readFileSync } from 'fs'; import { join, resolve } from 'path'; import { parseJsonSchemaToCommandDescription } from '../utilities/json-schema'; -import { UniversalAnalytics, getGlobalAnalytics, getSharedAnalytics } from './analytics'; +import { + getGlobalAnalytics, + getSharedAnalytics, + getWorkspaceAnalytics, + hasWorkspaceAnalyticsConfiguration, + promptProjectAnalytics, +} from './analytics'; import { Command } from './command'; import { CommandDescription, CommandWorkspace } from './interface'; import * as parser from './parser'; @@ -58,8 +64,19 @@ export interface CommandMapOptions { * Create the analytics instance. * @private */ -async function _createAnalytics(): Promise { - const config = await getGlobalAnalytics(); +async function _createAnalytics(workspace: boolean): Promise { + let config = await getGlobalAnalytics(); + // If in workspace and global analytics is enabled, defer to workspace level + if (workspace && config) { + // TODO: This should honor the `no-interactive` option. + // It is currently not an `ng` option but rather only an option for specific commands. + // The concept of `ng`-wide options are needed to cleanly handle this. + if (!(await hasWorkspaceAnalyticsConfiguration())) { + await promptProjectAnalytics(); + } + config = await getWorkspaceAnalytics(); + } + const maybeSharedAnalytics = await getSharedAnalytics(); if (config && maybeSharedAnalytics) { @@ -214,7 +231,7 @@ export async function runCommand( return map; }); - const analytics = options.analytics || await _createAnalytics(); + const analytics = options.analytics || await _createAnalytics(!!workspace.configFile); const context = { workspace, analytics }; const command = new description.impl(context, description, logger);