workspaceStats.ts 19.4 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5 6 7
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

'use strict';

K
kieferrm 已提交
8
import * as crypto from 'crypto';
9 10
import { TPromise } from 'vs/base/common/winjs.base';
import { onUnexpectedError } from 'vs/base/common/errors';
11
import URI from 'vs/base/common/uri';
B
Bowden 已提交
12
import { IFileService, IFileStat, IResolveFileResult } from 'vs/platform/files/common/files';
J
Johannes Rieken 已提交
13
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
14
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
15
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
16 17
import { IWindowConfiguration, IWindowService } from 'vs/platform/windows/common/windows';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
18
import { endsWith } from 'vs/base/common/strings';
E
Erich Gamma 已提交
19

20
const SshProtocolMatcher = /^([^@:]+@)?([^:]+):/;
K
kieferrm 已提交
21 22
const SshUrlMatcher = /^([^@:]+@)?([^:]+):(.+)$/;
const AuthorityMatcher = /^([^@]+@)?([^:]+)(:\d+)?$/;
C
Christof Marti 已提交
23
const SecondLevelDomainMatcher = /([^@:.]+\.[^@:.]+)(:\d+)?$/;
C
Christof Marti 已提交
24
const RemoteMatcher = /^\s*url\s*=\s*(.+\S)\s*$/mg;
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
const AnyButDot = /[^.]/g;
const SecondLevelDomainWhitelist = [
	'github.com',
	'bitbucket.org',
	'visualstudio.com',
	'gitlab.com',
	'heroku.com',
	'azurewebsites.net',
	'ibm.com',
	'amazon.com',
	'amazonaws.com',
	'cloudapp.net',
	'rhcloud.com',
	'google.com'
];
K
kieferrm 已提交
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
const ModulesToLookFor = [
	// Packages that suggest a node server
	'express',
	'sails',
	'koa',
	'hapi',
	'socket.io',
	'restify',
	// JS frameworks
	'react',
	'react-native',
	'@angular/core',
	'vue',
	// Other interesting packages
	'aws-sdk',
	'azure',
	'azure-storage',
	'@google-cloud/common',
	'heroku-cli'
];
K
kieferrm 已提交
60

61
type Tags = { [index: string]: boolean | number | string };
K
kieferrm 已提交
62

63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
function stripLowLevelDomains(domain: string): string {
	let match = domain.match(SecondLevelDomainMatcher);
	return match ? match[1] : null;
}

function extractDomain(url: string): string {
	if (url.indexOf('://') === -1) {
		let match = url.match(SshProtocolMatcher);
		if (match) {
			return stripLowLevelDomains(match[2]);
		}
	}
	try {
		let uri = URI.parse(url);
		if (uri.authority) {
			return stripLowLevelDomains(uri.authority);
		}
	} catch (e) {
		// ignore invalid URIs
	}
	return null;
}

export function getDomainsOfRemotes(text: string, whitelist: string[]): string[] {
S
Sandeep Somavarapu 已提交
87
	let domains = new Set<string>();
B
Benjamin Pasero 已提交
88
	let match: RegExpExecArray;
89 90 91
	while (match = RemoteMatcher.exec(text)) {
		let domain = extractDomain(match[1]);
		if (domain) {
S
Sandeep Somavarapu 已提交
92
			domains.add(domain);
93 94 95 96 97 98 99 100
		}
	}

	const whitemap = whitelist.reduce((map, key) => {
		map[key] = true;
		return map;
	}, Object.create(null));

S
Sandeep Somavarapu 已提交
101 102 103 104
	const elements: string[] = [];
	domains.forEach(e => elements.push(e));

	return elements
105 106 107
		.map(key => whitemap[key] ? key : key.replace(AnyButDot, 'a'));
}

K
kieferrm 已提交
108 109 110 111 112
function stripPort(authority: string): string {
	const match = authority.match(AuthorityMatcher);
	return match ? match[2] : null;
}

113
function normalizeRemote(host: string, path: string, stripEndingDotGit: boolean): string {
K
kieferrm 已提交
114
	if (host && path) {
115 116 117
		if (stripEndingDotGit && endsWith(path, '.git')) {
			path = path.substr(0, path.length - 4);
		}
K
kieferrm 已提交
118 119 120 121 122
		return (path.indexOf('/') === 0) ? `${host}${path}` : `${host}/${path}`;
	}
	return null;
}

123
function extractRemote(url: string, stripEndingDotGit: boolean): string {
K
kieferrm 已提交
124 125 126
	if (url.indexOf('://') === -1) {
		const match = url.match(SshUrlMatcher);
		if (match) {
127
			return normalizeRemote(match[2], match[3], stripEndingDotGit);
K
kieferrm 已提交
128 129 130 131 132
		}
	}
	try {
		const uri = URI.parse(url);
		if (uri.authority) {
133
			return normalizeRemote(stripPort(uri.authority), uri.path, stripEndingDotGit);
K
kieferrm 已提交
134 135 136 137 138 139 140
		}
	} catch (e) {
		// ignore invalid URIs
	}
	return null;
}

141
export function getRemotes(text: string, stripEndingDotGit: boolean = false): string[] {
K
kieferrm 已提交
142 143 144
	const remotes: string[] = [];
	let match: RegExpExecArray;
	while (match = RemoteMatcher.exec(text)) {
145
		const remote = extractRemote(match[1], stripEndingDotGit);
K
kieferrm 已提交
146 147 148 149 150 151 152
		if (remote) {
			remotes.push(remote);
		}
	}
	return remotes;
}

153 154
export function getHashedRemotesFromConfig(text: string, stripEndingDotGit: boolean = false): string[] {
	return getRemotes(text, stripEndingDotGit).map(r => {
K
kieferrm 已提交
155 156 157 158
		return crypto.createHash('sha1').update(r).digest('hex');
	});
}

159
export function getHashedRemotesFromUri(workspaceUri: URI, fileService: IFileService, stripEndingDotGit: boolean = false): TPromise<string[]> {
160 161
	let path = workspaceUri.path;
	let uri = workspaceUri.with({ path: `${path !== '/' ? path : ''}/.git/config` });
162 163 164 165 166 167
	return fileService.resolveFile(uri).then(() => {
		return fileService.resolveContent(uri, { acceptTextOnly: true }).then(
			content => getHashedRemotesFromConfig(content.value, stripEndingDotGit),
			err => [] // ignore missing or binary file
		);
	}, err => []);
168 169
}

170
export class WorkspaceStats implements IWorkbenchContribution {
E
Erich Gamma 已提交
171 172 173
	constructor(
		@IFileService private fileService: IFileService,
		@IWorkspaceContextService private contextService: IWorkspaceContextService,
174
		@ITelemetryService private telemetryService: ITelemetryService,
175 176
		@IEnvironmentService private environmentService: IEnvironmentService,
		@IWindowService windowService: IWindowService
E
Erich Gamma 已提交
177
	) {
178 179 180 181
		this.reportWorkspaceTags(windowService.getConfiguration());
		this.reportCloudStats();
	}

E
Erich Gamma 已提交
182
	private searchArray(arr: string[], regEx: RegExp): boolean {
183
		return arr.some(v => v.search(regEx) > -1) || undefined;
E
Erich Gamma 已提交
184 185
	}

K
kieferrm 已提交
186
	/* __GDPR__FRAGMENT__
K
kieferrm 已提交
187
		"WorkspaceTags" : {
K
kieferrm 已提交
188 189 190 191 192 193 194 195 196 197 198 199 200
			"workbench.filesToOpen" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
			"workbench.filesToCreate" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
			"workbench.filesToDiff" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
			"workspace.id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
			"workspace.roots" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
			"workspace.empty" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
			"workspace.grunt" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
			"workspace.gulp" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
			"workspace.jake" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
			"workspace.tsconfig" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
			"workspace.jsconfig" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
			"workspace.config.xml" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
			"workspace.vsc.extension" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
201
			"workspace.asp<NUMBER>" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
K
kieferrm 已提交
202 203 204
			"workspace.sln" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
			"workspace.unity" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
			"workspace.npm" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
205 206 207 208 209 210 211 212 213 214
			"workspace.npm.express" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
			"workspace.npm.sails" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
			"workspace.npm.koa" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
			"workspace.npm.hapi" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
			"workspace.npm.socket.io" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
			"workspace.npm.restify" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
			"workspace.npm.react" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
			"workspace.npm.@angular/core" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
			"workspace.npm.vue" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
			"workspace.npm.aws-sdk" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
215 216 217 218
			"workspace.npm.azure" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
			"workspace.npm.azure-storage" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
			"workspace.npm.@google-cloud/common" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
			"workspace.npm.heroku-cli" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
K
kieferrm 已提交
219 220 221 222 223 224 225 226
			"workspace.bower" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
			"workspace.yeoman.code.ext" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
			"workspace.cordova.high" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
			"workspace.cordova.low" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
			"workspace.xamarin.android" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
			"workspace.xamarin.ios" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
			"workspace.android.cpp" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
			"workspace.reactNative" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
K
kieferrm 已提交
227 228
		}
	*/
B
Bowden 已提交
229
	private async getWorkspaceTags(configuration: IWindowConfiguration): TPromise<Tags> {
K
kieferrm 已提交
230
		const tags: Tags = Object.create(null);
E
Erich Gamma 已提交
231

232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
		const state = this.contextService.getWorkbenchState();
		const workspace = this.contextService.getWorkspace();

		let workspaceId: string;
		switch (state) {
			case WorkbenchState.EMPTY:
				workspaceId = void 0;
				break;
			case WorkbenchState.FOLDER:
				workspaceId = crypto.createHash('sha1').update(workspace.folders[0].uri.fsPath).digest('hex');
				break;
			case WorkbenchState.WORKSPACE:
				workspaceId = crypto.createHash('sha1').update(workspace.configuration.fsPath).digest('hex');
		}

		tags['workspace.id'] = workspaceId;

B
Benjamin Pasero 已提交
249
		const { filesToOpen, filesToCreate, filesToDiff } = configuration;
250 251 252
		tags['workbench.filesToOpen'] = filesToOpen && filesToOpen.length || 0;
		tags['workbench.filesToCreate'] = filesToCreate && filesToCreate.length || 0;
		tags['workbench.filesToDiff'] = filesToDiff && filesToDiff.length || 0;
253

254
		const isEmpty = state === WorkbenchState.EMPTY;
S
Sandeep Somavarapu 已提交
255
		tags['workspace.roots'] = isEmpty ? 0 : workspace.folders.length;
256
		tags['workspace.empty'] = isEmpty;
E
Erich Gamma 已提交
257

258
		let folders = !isEmpty ? workspace.folders.map(folder => folder.uri) : this.environmentService.appQuality !== 'stable' && this.findFolders(configuration);
259
		if (folders && folders.length && this.fileService) {
260
			folders = folders.filter(x => x.scheme === 'file');
B
Bowden 已提交
261
			//return
K
kieferrm 已提交
262
			const files: IResolveFileResult[] = await this.fileService.resolveFiles(folders.map(resource => ({ resource })));
B
Bowden 已提交
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
			const names = (<IFileStat[]>[]).concat(...files.map(result => result.success ? (result.stat.children || []) : [])).map(c => c.name);
			const nameSet = names.reduce((s, n) => s.add(n.toLowerCase()), new Set());

			tags['workspace.grunt'] = nameSet.has('gruntfile.js');
			tags['workspace.gulp'] = nameSet.has('gulpfile.js');
			tags['workspace.jake'] = nameSet.has('jakefile.js');

			tags['workspace.tsconfig'] = nameSet.has('tsconfig.json');
			tags['workspace.jsconfig'] = nameSet.has('jsconfig.json');
			tags['workspace.config.xml'] = nameSet.has('config.xml');
			tags['workspace.vsc.extension'] = nameSet.has('vsc-extension-quickstart.md');

			tags['workspace.ASP5'] = nameSet.has('project.json') && this.searchArray(names, /^.+\.cs$/i);
			tags['workspace.sln'] = this.searchArray(names, /^.+\.sln$|^.+\.csproj$/i);
			tags['workspace.unity'] = nameSet.has('assets') && nameSet.has('library') && nameSet.has('projectsettings');
			tags['workspace.npm'] = nameSet.has('package.json') || nameSet.has('node_modules');
			tags['workspace.bower'] = nameSet.has('bower.json') || nameSet.has('bower_components');

			tags['workspace.yeoman.code.ext'] = nameSet.has('vsc-extension-quickstart.md');

			let mainActivity = nameSet.has('mainactivity.cs') || nameSet.has('mainactivity.fs');
			let appDelegate = nameSet.has('appdelegate.cs') || nameSet.has('appdelegate.fs');
			let androidManifest = nameSet.has('androidmanifest.xml');

			let platforms = nameSet.has('platforms');
			let plugins = nameSet.has('plugins');
			let www = nameSet.has('www');
			let properties = nameSet.has('properties');
			let resources = nameSet.has('resources');
			let jni = nameSet.has('jni');

			if (tags['workspace.config.xml'] &&
				!tags['workspace.language.cs'] && !tags['workspace.language.vb'] && !tags['workspace.language.aspx']) {
				if (platforms && plugins && www) {
					tags['workspace.cordova.high'] = true;
				} else {
					tags['workspace.cordova.low'] = true;
E
Erich Gamma 已提交
300
				}
B
Bowden 已提交
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317
			}

			if (mainActivity && properties && resources) {
				tags['workspace.xamarin.android'] = true;
			}

			if (appDelegate && resources) {
				tags['workspace.xamarin.ios'] = true;
			}

			if (androidManifest && jni) {
				tags['workspace.android.cpp'] = true;
			}

			if (nameSet.has('package.json')) {
				await TPromise.join(folders.map(async workspaceUri => {
					const uri = workspaceUri.with({ path: `${workspaceUri.path !== '/' ? workspaceUri.path : ''}/package.json` });
K
kieferrm 已提交
318
					const content = await this.fileService.resolveContent(uri, { acceptTextOnly: true });
B
Bowden 已提交
319 320
					const packageJsonContents = JSON.parse(content.value);
					if (packageJsonContents['dependencies']) {
K
kieferrm 已提交
321 322 323 324 325 326 327 328 329 330
						for (let module of ModulesToLookFor) {
							if ('react-native' === module) {
								if (packageJsonContents['dependencies'][module]) {
									tags['workspace.reactNative'] = true;
								}
							} else {
								if (packageJsonContents['dependencies'][module]) {
									tags['workspace.npm.' + module] = true;
								}
							}
B
Bowden 已提交
331 332 333 334
						}
					}
				}));
			}
E
Erich Gamma 已提交
335
		}
B
Bowden 已提交
336
		return TPromise.as(tags);
E
Erich Gamma 已提交
337 338
	}

B
Benjamin Pasero 已提交
339 340
	private findFolders(configuration: IWindowConfiguration): URI[] {
		const folder = this.findFolder(configuration);
341 342 343
		return folder && [folder];
	}

B
Benjamin Pasero 已提交
344
	private findFolder({ filesToOpen, filesToCreate, filesToDiff }: IWindowConfiguration): URI {
345
		if (filesToOpen && filesToOpen.length) {
B
Benjamin Pasero 已提交
346
			return this.parentURI(URI.file(filesToOpen[0].filePath));
347
		} else if (filesToCreate && filesToCreate.length) {
B
Benjamin Pasero 已提交
348
			return this.parentURI(URI.file(filesToCreate[0].filePath));
349
		} else if (filesToDiff && filesToDiff.length) {
B
Benjamin Pasero 已提交
350
			return this.parentURI(URI.file(filesToDiff[0].filePath));
351
		}
352
		return undefined;
353 354 355 356 357 358 359 360
	}

	private parentURI(uri: URI): URI {
		const path = uri.path;
		const i = path.lastIndexOf('/');
		return i !== -1 ? uri.with({ path: path.substr(0, i) }) : undefined;
	}

B
Benjamin Pasero 已提交
361 362
	public reportWorkspaceTags(configuration: IWindowConfiguration): void {
		this.getWorkspaceTags(configuration).then((tags) => {
K
kieferrm 已提交
363
			/* __GDPR__
K
kieferrm 已提交
364 365 366 367 368 369
				"workspce.tags" : {
					"${include}": [
						"${WorkspaceTags}"
					]
				}
			*/
E
Erich Gamma 已提交
370
			this.telemetryService.publicLog('workspce.tags', tags);
371
		}, error => onUnexpectedError(error));
E
Erich Gamma 已提交
372
	}
K
kieferrm 已提交
373

374 375 376 377
	private reportRemoteDomains(workspaceUris: URI[]): void {
		TPromise.join<string[]>(workspaceUris.map(workspaceUri => {
			const path = workspaceUri.path;
			const uri = workspaceUri.with({ path: `${path !== '/' ? path : ''}/.git/config` });
378 379 380 381 382 383
			return this.fileService.resolveFile(uri).then(() => {
				return this.fileService.resolveContent(uri, { acceptTextOnly: true }).then(
					content => getDomainsOfRemotes(content.value, SecondLevelDomainWhitelist),
					err => [] // ignore missing or binary file
				);
			}, err => []);
384 385 386 387
		})).then(domains => {
			const set = domains.reduce((set, list) => list.reduce((set, item) => set.add(item), set), new Set<string>());
			const list: string[] = [];
			set.forEach(item => list.push(item));
K
kieferrm 已提交
388
			/* __GDPR__
K
kieferrm 已提交
389
				"workspace.remotes" : {
R
Ramya Achutha Rao 已提交
390
					"domains" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
K
kieferrm 已提交
391 392
				}
			*/
393 394
			this.telemetryService.publicLog('workspace.remotes', { domains: list.sort() });
		}, onUnexpectedError);
K
kieferrm 已提交
395 396
	}

K
kieferrm 已提交
397 398
	private reportRemotes(workspaceUris: URI[]): void {
		TPromise.join<string[]>(workspaceUris.map(workspaceUri => {
399
			return getHashedRemotesFromUri(workspaceUri, this.fileService, true);
K
kieferrm 已提交
400 401
		})).then(hashedRemotes => {
			/* __GDPR__
402
					"workspace.hashedRemotes" : {
K
kieferrm 已提交
403
						"remotes" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
404 405
					}
				*/
K
kieferrm 已提交
406 407
			this.telemetryService.publicLog('workspace.hashedRemotes', { remotes: hashedRemotes });
		}, onUnexpectedError);
K
kieferrm 已提交
408 409
	}

K
kieferrm 已提交
410
	/* __GDPR__FRAGMENT__
K
kieferrm 已提交
411
		"AzureTags" : {
K
kieferrm 已提交
412
			"node" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
K
kieferrm 已提交
413 414
		}
	*/
415
	private reportAzureNode(workspaceUris: URI[], tags: Tags): TPromise<Tags> {
C
Christof Marti 已提交
416
		// TODO: should also work for `node_modules` folders several levels down
417 418 419 420 421
		const uris = workspaceUris.map(workspaceUri => {
			const path = workspaceUri.path;
			return workspaceUri.with({ path: `${path !== '/' ? path : ''}/node_modules` });
		});
		return this.fileService.resolveFiles(uris.map(resource => ({ resource }))).then(
I
isidor 已提交
422 423
			results => {
				const names = (<IFileStat[]>[]).concat(...results.map(result => result.success ? (result.stat.children || []) : [])).map(c => c.name);
424
				const referencesAzure = this.searchArray(names, /azure/i);
K
kieferrm 已提交
425 426 427 428 429 430 431 432 433 434
				if (referencesAzure) {
					tags['node'] = true;
				}
				return tags;
			},
			err => {
				return tags;
			});
	}

K
kieferrm 已提交
435 436

	/* __GDPR__FRAGMENT__
K
kieferrm 已提交
437
		"AzureTags" : {
K
kieferrm 已提交
438
			"java" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
K
kieferrm 已提交
439 440
		}
	*/
441 442 443 444
	private reportAzureJava(workspaceUris: URI[], tags: Tags): TPromise<Tags> {
		return TPromise.join(workspaceUris.map(workspaceUri => {
			const path = workspaceUri.path;
			const uri = workspaceUri.with({ path: `${path !== '/' ? path : ''}/pom.xml` });
445 446 447 448 449 450
			return this.fileService.resolveFile(uri).then(stats => {
				return this.fileService.resolveContent(uri, { acceptTextOnly: true }).then(
					content => !!content.value.match(/azure/i),
					err => false
				);
			}, err => false);
451 452 453
		})).then(javas => {
			if (javas.indexOf(true) !== -1) {
				tags['java'] = true;
K
kieferrm 已提交
454
			}
455 456
			return tags;
		});
K
kieferrm 已提交
457 458
	}

459
	private reportAzure(uris: URI[]) {
K
kieferrm 已提交
460
		const tags: Tags = Object.create(null);
461 462
		this.reportAzureNode(uris, tags).then((tags) => {
			return this.reportAzureJava(uris, tags);
K
kieferrm 已提交
463
		}).then((tags) => {
C
Christof Marti 已提交
464
			if (Object.keys(tags).length) {
K
kieferrm 已提交
465
				/* __GDPR__
K
kieferrm 已提交
466 467 468 469 470 471
					"workspace.azure" : {
						"${include}": [
							"${AzureTags}"
						]
					}
				*/
C
Christof Marti 已提交
472
				this.telemetryService.publicLog('workspace.azure', tags);
K
kieferrm 已提交
473
			}
474
		}).then(null, onUnexpectedError);
K
kieferrm 已提交
475 476 477
	}

	public reportCloudStats(): void {
478 479
		const uris = this.contextService.getWorkspace().folders.map(folder => folder.uri);
		if (uris.length && this.fileService) {
480
			this.reportRemoteDomains(uris);
K
kieferrm 已提交
481
			this.reportRemotes(uris);
482
			this.reportAzure(uris);
K
kieferrm 已提交
483
		}
484
	}
485
}