Add Azure Monitor Extension (#15397)

* Added Azure Log Analytics resource for generating AAD Token.

* Fixed AzureResource

* Removed debug code from connectionManagementService

* Moved AzureLogAnalytics from AzureResource enum in azdata.d.ts to azdata.proposed.d.ts.  Added azureLogAnalyticsResource to all azureSettings in providerSettings.ts

* Updated endpoint for generating AAD Token for LogAnalytics for UsGov, UsNat, and China

* Initial Commit of Azure Monitor Extension

* Added extension name to azuremonitor package strings

* Removed azureMonitor resource from germanyCloud in providerSettings

* Added logic to exclude menuItems in object explorer for LogAnalytics

* Changed exe from AzureMonitor to Kusto

* Added if clause for queryName for new queries

* Changed queryWindow name from KustoQuery to KQLQuery for Kusto and LogAnalytics.

* Added LogAnalytics for setTaskBarContent

* Added serialization and telemetry feature classes to AzureMonitor. Added references for azdata and vscode.

* Added azure monitor light and dark icons

* Added config for Dashboard in package.json

* Added workspace information to dashboard

* Added language support for LogAnalytics

* Added Notebook support

* Added Hide flag to package.json for databaseName

* Changed providerId from LogAnalytics to LOGANALYTICS

* Changed Workspace to Workspace ID in package.nls.json

* Added support for Azure Widget browser

* Changed fullName to use workspaceId when connecting

* Changed providerId from alertsManagement to azureMonitor

* Added .gitignore and *.vsix to vscodeignore.

* Removed unused devDependencies

* Code Review Feedback

* Changed tsconfig.json to match Kusto and Sql

* Changed package.json to match kusto package.

* Changed tsconfig to validate unused params and implictAny. Changed existing code to satisfy build.

* Fixed tsconfig to use the correct base class.

* Added objectExplorerNodeProvider and all related classes.

* Removed unused tmLanguage file

* Added logic to to download extension from toolservice

* Fixed launchArgs. Removed commented code from extension.ts. Changed config.json to use net5.0

* Added displayName to package.nls.json. Removed hide flag from databaseName. Other code review feedback.

* Added readme info to AzureMonitor

* Removed unused client-error-handler and ui-references files. Combined outputChannel in azuremonitorServer. Removed TODO from contextProvider. Renamed function in extension.ts. Removed unneeded 'use strict' from cancelableStream.ts. Removed second outputChannel from objectExplorerNodeProvider.

* Removed unused files
This commit is contained in:
Justin M
2021-07-06 15:27:19 -07:00
committed by GitHub
parent 9ac1f16cea
commit 48b7d96999
65 changed files with 3788 additions and 10 deletions

View File

@@ -0,0 +1,77 @@
// This code is originally from https://github.com/DonJayamanne/bowerVSCode
// License: https://github.com/DonJayamanne/bowerVSCode/blob/master/LICENSE
import { window } from 'vscode';
import PromptFactory from './factory';
import EscapeException from './escapeException';
import { IQuestion, IPrompter, IPromptCallback, Answers } from './question';
// Supports simple pattern for prompting for user input and acting on this
export default class CodeAdapter implements IPrompter {
// TODO define question interface
private fixQuestion(question: IQuestion): any {
if (question.type === 'checkbox' && Array.isArray(question.choices)) {
// For some reason when there's a choice of checkboxes, they aren't formatted properly
// Not sure where the issue is
question.choices = question.choices.map(item => {
if (typeof (item) === 'string') {
return { checked: false, name: item, value: item };
} else {
return item;
}
});
}
}
public async promptSingle<T>(question: IQuestion, ignoreFocusOut?: boolean): Promise<T | undefined> {
let questions: IQuestion[] = [question];
const answers = await this.prompt<T>(questions, ignoreFocusOut);
if (answers) {
let response: T = answers[question.name];
return response || undefined;
}
return undefined;
}
public async prompt<T>(questions: IQuestion[], ignoreFocusOut?: boolean): Promise<Answers<T> | undefined> {
// Collapse multiple questions into a set of prompt steps
const promptResult = new Promise<Answers<T>>((resolve) => {
let answers: Answers<T> = {};
questions.forEach(async (question: IQuestion) => {
this.fixQuestion(question);
const prompt = await PromptFactory.createPrompt(question, ignoreFocusOut);
if (!question.shouldPrompt || question.shouldPrompt(answers) === true) {
const result = await prompt.render();
answers[question.name] = result;
if (question.onAnswered) {
question.onAnswered(result);
}
return;
}
});
resolve(answers);
});
try {
return await promptResult;
} catch (err) {
if (err instanceof EscapeException || err instanceof TypeError) {
window.showErrorMessage(err.message);
}
return undefined;
}
}
// Helper to make it possible to prompt using callback pattern. Generally Promise is a preferred flow
public promptCallback(questions: IQuestion[], callback: IPromptCallback | undefined): void {
// Collapse multiple questions into a set of prompt steps
this.prompt(questions).then(answers => {
if (callback && answers) {
callback(answers);
}
});
}
}

View File

@@ -0,0 +1,52 @@
'use strict';
// This code is originally from https://github.com/DonJayamanne/bowerVSCode
// License: https://github.com/DonJayamanne/bowerVSCode/blob/master/LICENSE
import { window } from 'vscode';
import Prompt from './prompt';
import EscapeException from './escapeException';
const figures = require('figures');
export default class CheckboxPrompt extends Prompt {
constructor(question: any, ignoreFocusOut?: boolean) {
super(question, ignoreFocusOut);
}
public render(): any {
let choices = this._question.choices.reduce((result: { [x: string]: any; }, choice: { name: any; checked: boolean; }) => {
let choiceName = choice.name || choice;
result[`${choice.checked === true ? figures.radioOn : figures.radioOff} ${choiceName}`] = choice;
return result;
}, {});
let options = this.defaultQuickPickOptions;
options.placeHolder = this._question.message;
let quickPickOptions = Object.keys(choices);
quickPickOptions.push(figures.tick);
return window.showQuickPick(quickPickOptions, options)
.then(result => {
if (result === undefined) {
throw new EscapeException();
}
if (result !== figures.tick) {
choices[result].checked = !choices[result].checked;
return this.render();
}
return this._question.choices.reduce((result2: any[], choice: { checked: boolean; value: any; }) => {
if (choice.checked === true) {
result2.push(choice.value);
}
return result2;
}, []);
});
}
}

View File

@@ -0,0 +1,36 @@
'use strict';
// This code is originally from https://github.com/DonJayamanne/bowerVSCode
// License: https://github.com/DonJayamanne/bowerVSCode/blob/master/LICENSE
import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle();
import { window } from 'vscode';
import Prompt from './prompt';
import EscapeException from './escapeException';
export default class ConfirmPrompt extends Prompt {
constructor(question: any, ignoreFocusOut?: boolean) {
super(question, ignoreFocusOut);
}
public render(): any {
let choices: { [id: string]: boolean } = {};
choices[localize('msgYes', 'Yes')] = true;
choices[localize('msgNo', 'No')] = false;
let options = this.defaultQuickPickOptions;
options.placeHolder = this._question.message;
return window.showQuickPick(Object.keys(choices), options)
.then(result => {
if (result === undefined) {
throw new EscapeException();
}
return choices[result] || false;
});
}
}

View File

@@ -0,0 +1,8 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export default class EscapeException extends Error {
}

View File

@@ -0,0 +1,78 @@
'use strict';
// This code is originally from https://github.com/DonJayamanne/bowerVSCode
// License: https://github.com/DonJayamanne/bowerVSCode/blob/master/LICENSE
import vscode = require('vscode');
import Prompt from './prompt';
import EscapeException from './escapeException';
import { INameValueChoice } from './question';
const figures = require('figures');
export default class ExpandPrompt extends Prompt {
constructor(question: any, ignoreFocusOut?: boolean) {
super(question, ignoreFocusOut);
}
public render(): any {
// label indicates this is a quickpick item. Otherwise it's a name-value pair
if (this._question.choices[0].label) {
return this.renderQuickPick(this._question.choices);
} else {
return this.renderNameValueChoice(this._question.choices);
}
}
private renderQuickPick(choices: vscode.QuickPickItem[]): any {
let options = this.defaultQuickPickOptions;
options.placeHolder = this._question.message;
return vscode.window.showQuickPick(choices, options)
.then(result => {
if (result === undefined) {
throw new EscapeException();
}
return this.validateAndReturn(result || false);
});
}
private renderNameValueChoice(_choices: INameValueChoice[]): any {
const choiceMap = this._question.choices.reduce((result: { [x: string]: any; }, choice: { name: string | number; value: any; }) => {
result[choice.name] = choice.value;
return result;
}, {});
let options = this.defaultQuickPickOptions;
options.placeHolder = this._question.message;
return vscode.window.showQuickPick(Object.keys(choiceMap), options)
.then(result => {
if (result === undefined) {
throw new EscapeException();
}
// Note: cannot be used with 0 or false responses
let returnVal = choiceMap[result] || false;
return this.validateAndReturn(returnVal);
});
}
private validateAndReturn(value: any): any {
if (!this.validate(value)) {
return this.render();
}
return value;
}
private validate(value: any): boolean {
const validationError = this._question.validate ? this._question.validate(value || '') : undefined;
if (validationError) {
this._question.message = `${figures.warning} ${validationError}`;
return false;
}
return true;
}
}

View File

@@ -0,0 +1,35 @@
'use strict';
// This code is originally from https://github.com/DonJayamanne/bowerVSCode
// License: https://github.com/DonJayamanne/bowerVSCode/blob/master/LICENSE
import Prompt from './prompt';
import InputPrompt from './input';
import PasswordPrompt from './password';
import ListPrompt from './list';
import ConfirmPrompt from './confirm';
import CheckboxPrompt from './checkbox';
import ExpandPrompt from './expand';
export default class PromptFactory {
public static createPrompt(question: any, ignoreFocusOut?: boolean): Prompt {
switch (question.type || 'input') {
case 'string':
case 'input':
return new InputPrompt(question, ignoreFocusOut);
case 'password':
return new PasswordPrompt(question, ignoreFocusOut);
case 'list':
return new ListPrompt(question, ignoreFocusOut);
case 'confirm':
return new ConfirmPrompt(question, ignoreFocusOut);
case 'checkbox':
return new CheckboxPrompt(question, ignoreFocusOut);
case 'expand':
return new ExpandPrompt(question, ignoreFocusOut);
default:
throw new Error(`Could not find a prompt for question type ${question.type}`);
}
}
}

View File

@@ -0,0 +1,59 @@
'use strict';
// This code is originally from https://github.com/DonJayamanne/bowerVSCode
// License: https://github.com/DonJayamanne/bowerVSCode/blob/master/LICENSE
import { window, InputBoxOptions } from 'vscode';
import Prompt from './prompt';
import EscapeException from './escapeException';
const figures = require('figures');
export default class InputPrompt extends Prompt {
protected _options: InputBoxOptions;
constructor(question: any, ignoreFocusOut?: boolean) {
super(question, ignoreFocusOut);
this._options = this.defaultInputBoxOptions;
this._options.prompt = this._question.message;
}
// Helper for callers to know the right type to get from the type factory
public static get promptType(): string { return 'input'; }
public render(): any {
// Prefer default over the placeHolder, if specified
let placeHolder = this._question.default ? this._question.default : this._question.placeHolder;
if (this._question.default instanceof Error) {
placeHolder = this._question.default.message;
this._question.default = undefined;
}
this._options.placeHolder = placeHolder;
return window.showInputBox(this._options)
.then(result => {
if (result === undefined) {
throw new EscapeException();
}
if (result === '') {
// Use the default value, if defined
result = this._question.default || '';
}
const validationError = this._question.validate ? this._question.validate(result || '') : undefined;
if (validationError) {
this._question.default = new Error(`${figures.warning} ${validationError}`);
return this.render();
}
return result;
});
}
}

View File

@@ -0,0 +1,33 @@
'use strict';
// This code is originally from https://github.com/DonJayamanne/bowerVSCode
// License: https://github.com/DonJayamanne/bowerVSCode/blob/master/LICENSE
import { window } from 'vscode';
import Prompt from './prompt';
import EscapeException from './escapeException';
export default class ListPrompt extends Prompt {
constructor(question: any, ignoreFocusOut?: boolean) {
super(question, ignoreFocusOut);
}
public render(): any {
const choices = this._question.choices.reduce((result: { [x: string]: any; }, choice: { name: string | number; value: any; }) => {
result[choice.name] = choice.value;
return result;
}, {});
let options = this.defaultQuickPickOptions;
options.placeHolder = this._question.message;
return window.showQuickPick(Object.keys(choices), options)
.then(result => {
if (result === undefined) {
throw new EscapeException();
}
return choices[result];
});
}
}

View File

@@ -0,0 +1,15 @@
'use strict';
// This code is originally from https://github.com/DonJayamanne/bowerVSCode
// License: https://github.com/DonJayamanne/bowerVSCode/blob/master/LICENSE
import InputPrompt from './input';
export default class PasswordPrompt extends InputPrompt {
constructor(question: any, ignoreFocusOut?: boolean) {
super(question, ignoreFocusOut);
this._options.password = true;
}
}

View File

@@ -0,0 +1,70 @@
'use strict';
// This code is originally from https://github.com/DonJayamanne/bowerVSCode
// License: https://github.com/DonJayamanne/bowerVSCode/blob/master/LICENSE
import { window, StatusBarItem, StatusBarAlignment } from 'vscode';
export default class ProgressIndicator {
private _statusBarItem: StatusBarItem;
constructor() {
this._statusBarItem = window.createStatusBarItem(StatusBarAlignment.Left);
}
private _tasks: string[] = [];
public beginTask(task: string): void {
this._tasks.push(task);
this.displayProgressIndicator();
}
public endTask(_task: string): void {
if (this._tasks.length > 0) {
this._tasks.pop();
}
this.setMessage();
}
private setMessage(): void {
if (this._tasks.length === 0) {
this._statusBarItem.text = '';
this.hideProgressIndicator();
return;
}
this._statusBarItem.text = this._tasks[this._tasks.length - 1];
this._statusBarItem.show();
}
private _interval: any;
private displayProgressIndicator(): void {
this.setMessage();
this.hideProgressIndicator();
this._interval = setInterval(() => this.onDisplayProgressIndicator(), 100);
}
private hideProgressIndicator(): void {
if (this._interval) {
clearInterval(this._interval);
this._interval = undefined;
}
this.ProgressCounter = 0;
}
private ProgressText = ['|', '/', '-', '\\', '|', '/', '-', '\\'];
private ProgressCounter = 0;
private onDisplayProgressIndicator(): void {
if (this._tasks.length === 0) {
return;
}
let txt = this.ProgressText[this.ProgressCounter];
this._statusBarItem.text = this._tasks[this._tasks.length - 1] + ' ' + txt;
this.ProgressCounter++;
if (this.ProgressCounter >= this.ProgressText.length - 1) {
this.ProgressCounter = 0;
}
}
}

View File

@@ -0,0 +1,33 @@
'use strict';
// This code is originally from https://github.com/DonJayamanne/bowerVSCode
// License: https://github.com/DonJayamanne/bowerVSCode/blob/master/LICENSE
import { InputBoxOptions, QuickPickOptions } from 'vscode';
abstract class Prompt {
protected _question: any;
protected _ignoreFocusOut?: boolean;
constructor(question: any, ignoreFocusOut?: boolean) {
this._question = question;
this._ignoreFocusOut = ignoreFocusOut ? ignoreFocusOut : false;
}
public abstract render(): any;
protected get defaultQuickPickOptions(): QuickPickOptions {
return {
ignoreFocusOut: this._ignoreFocusOut
};
}
protected get defaultInputBoxOptions(): InputBoxOptions {
return {
ignoreFocusOut: this._ignoreFocusOut
};
}
}
export default Prompt;

View File

@@ -0,0 +1,73 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// TODO: Common file with mssql
import vscode = require('vscode');
export class QuestionTypes {
public static get input(): string { return 'input'; }
public static get password(): string { return 'password'; }
public static get list(): string { return 'list'; }
public static get confirm(): string { return 'confirm'; }
public static get checkbox(): string { return 'checkbox'; }
public static get expand(): string { return 'expand'; }
}
// Question interface to clarify how to use the prompt feature
// based on Bower Question format: https://github.com/bower/bower/blob/89069784bb46bfd6639b4a75e98a0d7399a8c2cb/packages/bower-logger/README.md
export interface IQuestion {
// Type of question (see QuestionTypes)
type: string;
// Name of the question for disambiguation
name: string;
// Message to display to the user
message: string;
// Optional placeHolder to give more detailed information to the user
placeHolder?: any;
// Optional default value - this will be used instead of placeHolder
default?: any;
// optional set of choices to be used. Can be QuickPickItems or a simple name-value pair
choices?: Array<vscode.QuickPickItem | INameValueChoice>;
// Optional validation function that returns an error string if validation fails
validate?: (value: any) => string;
// Optional pre-prompt function. Takes in set of answers so far, and returns true if prompt should occur
shouldPrompt?: (answers: { [id: string]: any }) => boolean;
// Optional action to take on the question being answered
onAnswered?: (value: any) => void;
// Optional set of options to support matching choices.
matchOptions?: vscode.QuickPickOptions;
}
// Pair used to display simple choices to the user
export interface INameValueChoice {
name: string;
value: any;
}
// Generic object that can be used to define a set of questions and handle the result
export interface IQuestionHandler {
// Set of questions to be answered
questions: IQuestion[];
// Optional callback, since questions may handle themselves
callback?: IPromptCallback;
}
export type Answers<T> = { [key: string]: T };
export interface IPrompter {
promptSingle<T>(question: IQuestion, ignoreFocusOut?: boolean): Promise<T | undefined>;
/**
* Prompts for multiple questions
*
* @returns Map of question IDs to results, or undefined if
* the user canceled the question session
*/
prompt<T>(questions: IQuestion[], ignoreFocusOut?: boolean): Promise<Answers<T> | undefined>;
promptCallback(questions: IQuestion[], callback: IPromptCallback): void;
}
export interface IPromptCallback {
(answers: { [id: string]: any }): void;
}