Added telemetry helper and set telemetry and configured telemetry events

This commit is contained in:
Himanshu Yadav 2019-08-19 20:30:57 +05:30
Родитель 1428650d7f
Коммит 33928dc06f
14 изменённых файлов: 270 добавлений и 73 удалений

Просмотреть файл

@ -71,7 +71,13 @@
],
"configuration": {
"title": "Azure Pipelines extension configuration.",
"properties": {}
"properties": {
"[azure-pipelines].configure" : {
"type": "boolean",
"default": true,
"description": "Enable to opt-in for Configure Pipeline feature."
}
}
},
"configurationDefaults": {
"[azure-pipelines]": {

Просмотреть файл

@ -3,10 +3,11 @@ import { registerCommand, IActionContext, createApiProvider } from 'vscode-azure
import { AzureExtensionApi, AzureExtensionApiProvider } from 'vscode-azureextensionui/api';
import { configurePipeline } from './configure';
import { Messages } from './messages';
import { Messages } from './resources/messages';
import { AzureAccountExtensionExports, extensionVariables } from './model/models';
import { TelemetryHelper } from './helper/telemetryHelper';
export async function activateConfigurePipeline(context: vscode.ExtensionContext): Promise<AzureExtensionApiProvider> {
export async function activateConfigurePipeline(): Promise<AzureExtensionApiProvider> {
let azureAccountExtension = vscode.extensions.getExtension("ms-vscode.azure-account");
if (!azureAccountExtension) {
throw new Error(Messages.azureAccountExntesionUnavailable);
@ -23,7 +24,8 @@ export async function activateConfigurePipeline(context: vscode.ExtensionContext
// The commandId parameter must match the command field in package.json
registerCommand('configure-pipeline', async (actionContext: IActionContext, node: any) => {
// The code you place here will be executed every time your command is executed
await configurePipeline(actionContext, node);
let telemetryHelper = new TelemetryHelper(actionContext, 'configure', journeyId);
await configurePipeline(telemetryHelper, node);
});
return createApiProvider([<AzureExtensionApi>

Просмотреть файл

@ -1,5 +1,5 @@
import { AzureResourceClient } from './azureResourceClient';
import { Messages } from '../../messages';
import { Messages } from '../../resources/messages';
import { ResourceListResult, GenericResource } from 'azure-arm-resource/lib/resource/models';
import { ServiceClientCredentials } from 'ms-rest';
import { WebAppKind } from '../../model/models';

Просмотреть файл

@ -1,7 +1,7 @@
import { BuildDefinition, Build } from '../../model/azureDevOps';
import { Messages } from '../../messages';
import { Messages } from '../../resources/messages';
import { Organization, DevOpsProject } from '../../model/models';
import { ReservedHostNames } from '../../constants';
import { ReservedHostNames } from '../../resources/constants';
import { RestClient } from '../restClient';
import { ServiceClientCredentials, UrlBasedRequestPrepareOptions } from 'ms-rest';
import { sleepForMilliSeconds, stringCompareFunction } from "../../helper/commonHelper";

Просмотреть файл

@ -2,43 +2,63 @@ const uuid = require('uuid/v4');
import { AppServiceClient } from './clients/azure/appServiceClient';
import { AzureDevOpsClient } from './clients/devOps/azureDevOpsClient';
import { AzureDevOpsHelper } from './helper/devOps/azureDevOpsHelper';
import { AzureTreeItem, IActionContext } from 'vscode-azureextensionui';
import { AzureTreeItem, UserCancelledError } from 'vscode-azureextensionui';
import { exit } from 'process';
import { generateDevOpsProjectName } from './helper/commonHelper';
import { GenericResource } from 'azure-arm-resource/lib/resource/models';
import { GraphHelper } from './helper/graphHelper';
import { LocalGitRepoHelper } from './helper/LocalGitRepoHelper';
import { Messages } from './messages';
import { Messages } from './resources/messages';
import { QuickPickItem } from 'vscode';
import { ServiceConnectionHelper } from './helper/devOps/serviceConnectionHelper';
import { SourceOptions, RepositoryProvider, extensionVariables, WizardInputs, WebAppKind, PipelineTemplate, QuickPickItemWithData } from './model/models';
import { TracePoints } from './resources/tracePoints';
import * as path from 'path';
import * as templateHelper from './helper/templateHelper';
import * as utils from 'util';
import * as vscode from 'vscode';
import { TelemetryHelper } from './helper/telemetryHelper';
export async function configurePipeline(actionContext: IActionContext, node: AzureTreeItem) {
export async function configurePipeline(telemetryHelper: TelemetryHelper, node: AzureTreeItem) {
try {
telemetryHelper.setTelemetry(TracePoints.CommandStartTime, Date.UTC.toString());
if (!(await extensionVariables.azureAccountExtensionApi.waitForLogin())) {
// set telemetry
telemetryHelper.setTelemetry(TracePoints.AzureLoginRequired, 'true');
let signIn = await vscode.window.showInformationMessage(Messages.azureLoginRequired, Messages.signInLabel);
if(signIn.toLowerCase() === Messages.signInLabel.toLowerCase()) {
if (signIn.toLowerCase() === Messages.signInLabel.toLowerCase()) {
await vscode.commands.executeCommand("azure-account.login");
}
else {
throw new Error(Messages.azureLoginRequired);
let error = new Error(Messages.azureLoginRequired);
extensionVariables.reporter.sendTelemetryEvent(
TracePoints.AzureLoginFailure,
{
'errorMessage': error.message
}
);
throw error;
}
}
var configurer = new PipelineConfigurer();
var configurer = new PipelineConfigurer(telemetryHelper);
await configurer.configure(node);
}
catch (error) {
// log error in telemetery.
extensionVariables.outputChannel.appendLine(error.message);
vscode.window.showErrorMessage(error.message);
actionContext.telemetry.properties.error = error;
actionContext.telemetry.properties.errorMessage = error.message;
if (error instanceof UserCancelledError) {
telemetryHelper.setError(error);
}
else {
telemetryHelper.setError(error);
extensionVariables.outputChannel.appendLine(error.message);
vscode.window.showErrorMessage(error.message);
}
}
telemetryHelper.setTelemetry(TracePoints.CommandEndTime, Date.UTC.toString());
}
class PipelineConfigurer {
@ -50,26 +70,38 @@ class PipelineConfigurer {
private appServiceClient: AppServiceClient;
private workspacePath: string;
private uniqueResourceNameSuffix: string;
private telemetryHelper: TelemetryHelper;
public constructor() {
public constructor(telemetryHelper: TelemetryHelper) {
this.inputs = new WizardInputs();
this.inputs.azureSession = extensionVariables.azureAccountExtensionApi.sessions[0];
this.azureDevOpsClient = new AzureDevOpsClient(this.inputs.azureSession.credentials);
this.azureDevOpsHelper = new AzureDevOpsHelper(this.azureDevOpsClient);
this.uniqueResourceNameSuffix = uuid().substr(0, 5);
this.telemetryHelper = telemetryHelper;
}
public async configure(node: any) {
this.telemetryHelper.setCurrentStep('GetAllRequiredInputs');
await this.getAllRequiredInputs(node);
this.telemetryHelper.setCurrentStep('CreatePreRequisites');
await this.createPreRequisites();
this.telemetryHelper.setCurrentStep('CheckInPipeline');
await this.checkInPipelineFileToRepository();
this.telemetryHelper.setCurrentStep('CreateAndRunPipeline');
let queuedPipelineUrl = await vscode.window.withProgress<string>({ location: vscode.ProgressLocation.Notification, title: Messages.configuringPipelineAndDeployment }, () => {
let pipelineName = `${this.inputs.targetResource.resource.name}.${this.uniqueResourceNameSuffix}`;
return this.azureDevOpsHelper.createAndRunPipeline(pipelineName, this.inputs);
});
this.telemetryHelper.setCurrentStep('DisplayCreatedPipeline');
vscode.window.showInformationMessage(Messages.pipelineSetupSuccessfully, Messages.browsePipeline)
.then((action: string) => {
if (action && action.toLowerCase() === Messages.browsePipeline.toLowerCase()) {
this.telemetryHelper.setTelemetry(TracePoints.ViewPipelineClicked, 'true');
vscode.env.openExternal(vscode.Uri.parse(queuedPipelineUrl));
}
});
@ -90,7 +122,7 @@ class PipelineConfigurer {
if (this.inputs.isNewOrganization) {
this.inputs.project = {
id: "",
name: generateDevOpsProjectName(this.inputs.sourceRepository.repositoryName)
name: generateDevOpsProjectName(this.inputs.sourceRepository.repositoryName)
};
await vscode.window.withProgress(
{
@ -108,6 +140,10 @@ class PipelineConfigurer {
})
.then((projectId) => {
this.inputs.project.id = projectId;
})
.catch((error) => {
this.telemetryHelper.logError(TracePoints.CreateOrganizationFailure, error);
throw error;
});
});
}
@ -119,13 +155,13 @@ class PipelineConfigurer {
await this.createAzureRMServiceConnection();
}
private async analyzeNode(node: any): Promise<void> {
if (node instanceof AzureTreeItem) {
await this.extractAzureResourceFromNode(node);
}
else if (node && node.fsPath) {
this.workspacePath = node.fsPath;
this.telemetryHelper.setTelemetry(TracePoints.SourceRepoLocation, SourceOptions.CurrentWorkspace);
}
}
@ -143,6 +179,7 @@ class PipelineConfigurer {
{ placeHolder: Messages.selectFolderOrRepository }
);
this.telemetryHelper.setTelemetry(TracePoints.SourceRepoLocation, selectedSourceOption.label);
switch (selectedSourceOption.label) {
case SourceOptions.BrowseLocalMachine:
let selectedFolder: vscode.Uri[] = await vscode.window.showOpenDialog(
@ -175,6 +212,9 @@ class PipelineConfigurer {
this.localGitRepoHelper = await LocalGitRepoHelper.GetHelperInstance(workspacePath);
this.inputs.sourceRepository = await this.localGitRepoHelper.getGitRepoDetails(workspacePath);
// set telemetry
this.telemetryHelper.setTelemetry(TracePoints.RepoProvider, this.inputs.sourceRepository.repositoryProvider);
if (this.inputs.sourceRepository.repositoryProvider === RepositoryProvider.AzureRepos) {
let orgAndProjectName = AzureDevOpsHelper.getOrganizationAndProjectNameFromRepositoryUrl(this.inputs.sourceRepository.remoteUrl);
this.inputs.organizationName = orgAndProjectName.orgnizationName;
@ -229,6 +269,8 @@ class PipelineConfigurer {
this.inputs.project = selectedProject.data;
}
else {
this.telemetryHelper.setTelemetry(TracePoints.NewOrganization, 'true');
this.inputs.isNewOrganization = true;
this.inputs.organizationName = await extensionVariables.ui.showInputBox({
placeHolder: Messages.enterAzureDevOpsOrganizationName,
@ -240,7 +282,7 @@ class PipelineConfigurer {
private async getSelectedPipeline(): Promise<void> {
let appropriatePipelines: PipelineTemplate[] = await vscode.window.withProgress(
{ location: vscode.ProgressLocation.Notification, title: Messages.analyzingRepo },
{ location: vscode.ProgressLocation.Notification, title: Messages.analyzingRepo },
() => templateHelper.analyzeRepoAndListAppropriatePipeline(this.inputs.sourceRepository.localPath)
);
@ -248,10 +290,10 @@ class PipelineConfigurer {
let selectedOption = await extensionVariables.ui.showQuickPick(appropriatePipelines.map((pipeline) => { return { label: pipeline.label }; }), {
placeHolder: Messages.selectPipelineTemplate
});
this.inputs.pipelineParameters.pipelineTemplate = appropriatePipelines.find((pipeline) => {
return pipeline.label === selectedOption.label;
});
this.telemetryHelper.setTelemetry(TracePoints.ChosenTemplate, this.inputs.pipelineParameters.pipelineTemplate.label);
}
private async getAzureResourceDetails(): Promise<void> {
@ -267,7 +309,7 @@ class PipelineConfigurer {
// show available resources and get the chosen one
this.appServiceClient = new AppServiceClient(extensionVariables.azureAccountExtensionApi.sessions[0].credentials, this.inputs.targetResource.subscriptionId);
let selectedResource: QuickPickItemWithData = await extensionVariables.ui.showQuickPick(
this.appServiceClient.GetAppServices(WebAppKind.WindowsApp).then((webApps) => webApps.map(x => {return {label: x.name, data: x};})),
this.appServiceClient.GetAppServices(WebAppKind.WindowsApp).then((webApps) => webApps.map(x => { return { label: x.name, data: x }; })),
{ placeHolder: Messages.selectWebApp });
this.inputs.targetResource.resource = selectedResource.data;
}
@ -277,18 +319,36 @@ class PipelineConfigurer {
this.serviceConnectionHelper = new ServiceConnectionHelper(this.inputs.organizationName, this.inputs.project.name, this.azureDevOpsClient);
}
let githubPat = await extensionVariables.ui.showInputBox({ placeHolder: Messages.enterGitHubPat, prompt: Messages.githubPatTokenHelpMessage });
// Get GitHub PAT as an input from the user.
let githubPat = null;
try {
// TO-DO Create a new helper function to time and log time for all user inputs.
// Log the time taken by the user to enter GitHub PAT
this.telemetryHelper.setTelemetry(TracePoints.GitHubPatStartTime, Date.UTC.toString());
githubPat = await extensionVariables.ui.showInputBox({ placeHolder: Messages.enterGitHubPat, prompt: Messages.githubPatTokenHelpMessage });
this.telemetryHelper.setTelemetry(TracePoints.GitHubPatEndTime, Date.UTC.toString());
}
catch (error) {
// This logs when the user cancels the operation.
this.telemetryHelper.setTelemetry(TracePoints.GitHubPatEndTime, Date.UTC.toString());
throw error;
}
// Create GitHub service connection in Azure DevOps
await vscode.window.withProgress(
{
location: vscode.ProgressLocation.Notification,
title: Messages.creatingGitHubServiceConnection
},
() => {
let serviceConnectionName = `${this.inputs.sourceRepository.repositoryName}-${this.uniqueResourceNameSuffix}`;
return this.serviceConnectionHelper.createGitHubServiceConnection(serviceConnectionName, githubPat)
.then((serviceConnectionId) => {
this.inputs.sourceRepository.serviceConnectionId = serviceConnectionId;
});
async () => {
try {
let serviceConnectionName = `${this.inputs.sourceRepository.repositoryName}-${this.uniqueResourceNameSuffix}`;
this.inputs.sourceRepository.serviceConnectionId = await this.serviceConnectionHelper.createGitHubServiceConnection(serviceConnectionName, githubPat);
}
catch (error) {
this.telemetryHelper.logError(TracePoints.GitHubServiceConnectionError, error);
throw error;
}
});
}
@ -303,14 +363,18 @@ class PipelineConfigurer {
location: vscode.ProgressLocation.Notification,
title: utils.format(Messages.creatingAzureServiceConnection, this.inputs.targetResource.subscriptionId)
},
() => {
let scope = this.inputs.targetResource.resource.id;
let aadAppName = GraphHelper.generateAadApplicationName(this.inputs.organizationName, this.inputs.project.name);
return GraphHelper.createSpnAndAssignRole(this.inputs.azureSession, aadAppName, scope)
.then((aadApp) => {
async () => {
try {
let scope = this.inputs.targetResource.resource.id;
let aadAppName = GraphHelper.generateAadApplicationName(this.inputs.organizationName, this.inputs.project.name);
let aadApp = await GraphHelper.createSpnAndAssignRole(this.inputs.azureSession, aadAppName, scope);
let serviceConnectionName = `${this.inputs.targetResource.resource.name}-${this.uniqueResourceNameSuffix}`;
return this.serviceConnectionHelper.createAzureServiceConnection(serviceConnectionName, this.inputs.azureSession.tenantId, this.inputs.targetResource.subscriptionId, scope, aadApp);
});
}
catch (error) {
this.telemetryHelper.logError(TracePoints.AzureServiceConnectionCreateFailure, error);
throw error;
}
});
}
@ -324,13 +388,20 @@ class PipelineConfigurer {
let commitOrDiscard = await vscode.window.showInformationMessage(Messages.modifyAndCommitFile, Messages.commitAndPush, Messages.discardPipeline);
if (commitOrDiscard.toLowerCase() === Messages.commitAndPush.toLowerCase()) {
await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, title: Messages.configuringPipelineAndDeployment }, async (progress) => {
// handle when the branch is not upto date with remote branch and push fails
let commitDetails = await this.localGitRepoHelper.commitAndPushPipelineFile(this.inputs.pipelineParameters.pipelineFilePath);
this.inputs.sourceRepository.branch = commitDetails.branch;
this.inputs.sourceRepository.commitId = commitDetails.commitId;
try {
// handle when the branch is not upto date with remote branch and push fails
let commitDetails = await this.localGitRepoHelper.commitAndPushPipelineFile(this.inputs.pipelineParameters.pipelineFilePath);
this.inputs.sourceRepository.branch = commitDetails.branch;
this.inputs.sourceRepository.commitId = commitDetails.commitId;
}
catch (error) {
this.telemetryHelper.logError(TracePoints.CheckInPipelineFailure, error);
throw (error);
}
});
}
else {
this.telemetryHelper.setTelemetry(TracePoints.PipelineDiscarded, 'true');
throw new Error(Messages.operationCancelled);
}
}

Просмотреть файл

@ -2,7 +2,7 @@ import { AzureDevOpsHelper } from './devOps/azureDevOpsHelper';
import { BranchSummary } from 'simple-git/typings/response';
import { GitHubProvider } from './gitHubHelper';
import { GitRepositoryParameters, RepositoryProvider } from '../model/models';
import { Messages } from '../messages';
import { Messages } from '../resources/messages';
import * as fs from 'fs';
import * as git from 'simple-git/promise';
import * as path from 'path';

Просмотреть файл

@ -1,6 +1,6 @@
import { AzureDevOpsClient } from '../../clients/devOps/azureDevOpsClient';
import { BuildDefinition, BuildDefinitionRepositoryProperties, Build } from '../../model/azureDevOps';
import { Messages } from '../../messages';
import { Messages } from '../../resources/messages';
import { WizardInputs, RepositoryProvider } from '../../model/models';
import * as util from 'util';

Просмотреть файл

@ -1,6 +1,6 @@
import { AadApplication } from '../../model/models';
import { AzureDevOpsClient } from '../../clients/devOps/azureDevOpsClient';
import { Messages } from '../../messages';
import { Messages } from '../../resources/messages';
import { ServiceConnectionClient } from '../../clients/devOps/serviceConnectionClient';
import * as util from 'util';

Просмотреть файл

@ -2,7 +2,7 @@ const uuid = require('uuid/v1');
import { AzureEnvironment } from 'ms-rest-azure';
import { AzureSession, Token, AadApplication } from '../model/models';
import { generateRandomPassword } from './commonHelper';
import { Messages } from '../messages';
import { Messages } from '../resources/messages';
import { RestClient } from '../clients/restClient';
import { TokenCredentials, UrlBasedRequestPrepareOptions, ServiceClientCredentials } from 'ms-rest';
import { TokenResponse, MemoryCache, AuthenticationContext } from 'adal-node';

Просмотреть файл

@ -0,0 +1,66 @@
import { IActionContext, ITelemetryReporter } from "vscode-azureextensionui";
import { extensionVariables } from "../model/models";
import { TracePoints } from "../resources/tracePoints";
const uuid = require('uuid/v4');
export class TelemetryHelper {
private actionContext: IActionContext;
private telemetryReporter: ITelemetryReporter;
private journeyId: string;
private command: string;
constructor(actionContext: IActionContext, command: string) {
this.actionContext = actionContext;
this.telemetryReporter = extensionVariables.reporter;
this.journeyId = uuid();
this.command = command;
this.setTelemetry(TracePoints.Command, command);
this.setTelemetry(TracePoints.JourneyId, this.journeyId);
}
public setTelemetry(key: string, value: string) {
if (key) {
this.actionContext.telemetry.properties[key] = value;
}
}
public setError(error: Error) {
this.actionContext.telemetry.properties.error = error.stack;
this.actionContext.telemetry.properties.errorMessage = error.message;
}
public setResult(result: 'Succeeded' | 'Failed' | 'Canceled', error?: Error, currentStep?: string) {
this.actionContext.telemetry.properties.result = result;
if (result === "Failed" && error) {
this.setError(error);
}
else if(result === 'Canceled' && currentStep) {
this.setCurrentStep(currentStep);
}
}
public setCurrentStep(stepName: string) {
this.actionContext.telemetry.properties.cancelStep = stepName;
}
public logError(tracePoint: string, error: Error) {
this.telemetryReporter.sendTelemetryEvent(
tracePoint,
{
'journeyId': this.journeyId,
'command': this.command,
'error': `Error: ${error.name}, Error.Message: ${error.message}, Error.Stack: ${error.stack}`
});
}
public logData(tracePoint: string, data: string) {
this.telemetryReporter.sendTelemetryEvent(
tracePoint,
{
'journeyId': this.journeyId,
'command': this.command,
'data': data
});
}
}

Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -0,0 +1,34 @@
export class TracePoints {
// Combined telemetry trace points
public static telemetryKey: string = '';
public static CurrentFunction: string = 'currentFunction';
public static RepoProvider: string = 'repoProvider';
public static AzureLoginRequired: string = 'azureLoginRequired';
public static Command: string = 'command';
public static JourneyId: string = 'journeyId';
public static EntryPoint: string = 'entryPoint';
public static SourceRepoLocation: string = 'sourceRepoLocation';
public static NewOrganization: string = 'newOrganization';
public static ChosenTemplate: string = 'chosenTemplate';
public static PipelineDiscarded: string = 'pipelineDiscarded';
public static ViewPipelineClicked: string = 'viewPipelineClicked';
public static GitHubPatStartTime = 'gitHubPatStartTime';
public static GitHubPatEndTime = 'gitHubPatEndTime';
public static CommandStartTime = 'commandStartTime';
public static CommandEndTime = 'commandEndTime';
public static OrganizationListDuration = 'organizationListDuration';
public static OrganizationListCount = 'organizationListCount';
public static ProjectListDuration = 'projectListDuration';
public static ProjectListCount = 'projectListCount';
public static AzureResourceListDuration = 'azureResourceListDuration';
public static AzureResourceListCount = 'azureResourceListCount';
// Failure trace points
public static AzureLoginFailure = 'azureLoginFailure';
public static AzureServiceConnectionCreateFailure = 'AzureServiceConnectionCreateFailure';
public static CheckInPipelineFailure = 'checkInPipelineFailure';
public static CreateOrganizationFailure = 'createOrganizationFailure';
public static GitHubServiceConnectionError = 'gitHubServiceConnectionError';
}

Просмотреть файл

@ -19,9 +19,24 @@ let perfStats = {
loadEndTime: undefined
};
const configurePipelineEnabled: boolean = vscode.workspace.getConfiguration('[azure-pipelines]', null).get('configure') ? true : false;
export async function activate(context: vscode.ExtensionContext) {
logger.log('Extension has been activated!', 'ExtensionActivated');
setTelemetryReporter(context);
await callWithTelemetryAndErrorHandling('azurePipelines.activate', async (activateContext: IActionContext) => {
activateContext.telemetry.properties['configurePipelineEnabled'] = `${configurePipelineEnabled}`;
await activateYmlContributor(context, activateContext);
if (configurePipelineEnabled) {
await activateConfigurePipeline();
}
});
return schemacontributor.schemaContributor;
}
function setTelemetryReporter(context: vscode.ExtensionContext) {
// Register ui extension variables is required to be done for telemetry to start flowing for extension activation and other events.
// It also facilitates registering command and called events telemetry.
extensionVariables.reporter = createTelemetryReporter(context);
@ -30,51 +45,54 @@ export async function activate(context: vscode.ExtensionContext) {
extensionVariables.context = context;
extensionVariables.ui = new AzureUserInput(context.globalState);
registerUIExtensionVariables(extensionVariables);
}
await callWithTelemetryAndErrorHandling('azurePipelines.activate', async (activateContext: IActionContext) => {
async function activateYmlContributor(context: vscode.ExtensionContext, activateContext: IActionContext) {
if (activateContext) {
activateContext.telemetry.properties.isActivationEvent = 'true';
activateContext.telemetry.measurements.mainFileLoad = (perfStats.loadEndTime - perfStats.loadStartTime) / 1000;
}
const serverOptions: languageclient.ServerOptions = getServerOptions(context);
const clientOptions: languageclient.LanguageClientOptions = getClientOptions();
const client = new languageclient.LanguageClient('azure-pipelines', 'Azure Pipelines Support', serverOptions, clientOptions);
const serverOptions: languageclient.ServerOptions = getServerOptions(context);
const clientOptions: languageclient.LanguageClientOptions = getClientOptions();
const client = new languageclient.LanguageClient('azure-pipelines', 'Azure Pipelines Support', serverOptions, clientOptions);
const schemaAssociationService: schemaassociationservice.ISchemaAssociationService = new schemaassociationservice.SchemaAssociationService(context.extensionPath);
const schemaAssociationService: schemaassociationservice.ISchemaAssociationService = new schemaassociationservice.SchemaAssociationService(context.extensionPath);
const disposable = client.start();
context.subscriptions.push(disposable);
const disposable = client.start();
context.subscriptions.push(disposable);
const initialSchemaAssociations: schemaassociationservice.ISchemaAssociations = schemaAssociationService.getSchemaAssociation();
const initialSchemaAssociations: schemaassociationservice.ISchemaAssociations = schemaAssociationService.getSchemaAssociation();
await client.onReady().then(() => {
//logger.log(`${JSON.stringify(initialSchemaAssociations)}`, 'SendInitialSchemaAssociation');
client.sendNotification(schemaassociationservice.SchemaAssociationNotification.type, initialSchemaAssociations);
await client.onReady().then(() => {
//logger.log(`${JSON.stringify(initialSchemaAssociations)}`, 'SendInitialSchemaAssociation');
client.sendNotification(schemaassociationservice.SchemaAssociationNotification.type, initialSchemaAssociations);
// TODO: Should we get rid of these events and handle other events like Ctrl + Space? See when this event gets fired and send updated schema on that event.
client.onRequest(schemacontributor.CUSTOM_SCHEMA_REQUEST, (resource: any) => {
//logger.log('Custom schema request. Resource: ' + JSON.stringify(resource), 'CustomSchemaRequest');
// TODO: Should we get rid of these events and handle other events like Ctrl + Space? See when this event gets fired and send updated schema on that event.
client.onRequest(schemacontributor.CUSTOM_SCHEMA_REQUEST, (resource: any) => {
//logger.log('Custom schema request. Resource: ' + JSON.stringify(resource), 'CustomSchemaRequest');
// TODO: Can this return the location of the new schema file?
return schemacontributor.schemaContributor.requestCustomSchema(resource); // TODO: Have a single instance for the extension but dont return a global from this namespace.
});
// TODO: Can this return the location of the new schema file?
return schemacontributor.schemaContributor.requestCustomSchema(resource); // TODO: Have a single instance for the extension but dont return a global from this namespace.
});
// TODO: Can we get rid of this? Never seems to happen.
client.onRequest(schemacontributor.CUSTOM_CONTENT_REQUEST, (uri: any) => {
//logger.log('Custom content request.', 'CustomContentRequest');
// TODO: Can we get rid of this? Never seems to happen.
client.onRequest(schemacontributor.CUSTOM_CONTENT_REQUEST, (uri: any) => {
//logger.log('Custom content request.', 'CustomContentRequest');
return schemacontributor.schemaContributor.requestCustomSchemaContent(uri);
});
}).catch((reason) => {
return schemacontributor.schemaContributor.requestCustomSchemaContent(uri);
});
})
.catch((reason) => {
logger.log(JSON.stringify(reason), 'ClientOnReadyError');
if (activateContext) {
activateContext.errorHandling.suppressDisplay = true;
}
extensionVariables.reporter.sendTelemetryEvent('extension.languageserver.onReadyError', { 'reason': JSON.stringify(reason) });
});
// TODO: Can we get rid of this since it's set in package.json?
vscode.languages.setLanguageConfiguration('azure-pipelines', { wordPattern: /("(?:[^\\\"]*(?:\\.)?)*"?)|[^\s{}\[\],:]+/ });
await activateConfigurePipeline(context);
});
return schemacontributor.schemaContributor;
// TODO: Can we get rid of this since it's set in package.json?
vscode.languages.setLanguageConfiguration('azure-pipelines', { wordPattern: /("(?:[^\\\"]*(?:\\.)?)*"?)|[^\s{}\[\],:]+/ });
}
function getServerOptions(context: vscode.ExtensionContext): languageclient.ServerOptions {