mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 02:51:36 -05:00
Remove unused files in sql and extensions folders (#22444)
This commit is contained in:
@@ -3,16 +3,16 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { ControllerInfo } from 'arc';
|
// import { ControllerInfo } from 'arc';
|
||||||
import { v4 as uuid } from 'uuid';
|
// import { v4 as uuid } from 'uuid';
|
||||||
import { ControllerModel } from '../../models/controllerModel';
|
// import { ControllerModel } from '../../models/controllerModel';
|
||||||
import { AzureArcTreeDataProvider } from '../../ui/tree/azureArcTreeDataProvider';
|
// import { AzureArcTreeDataProvider } from '../../ui/tree/azureArcTreeDataProvider';
|
||||||
|
|
||||||
export class FakeControllerModel extends ControllerModel {
|
// export class FakeControllerModel extends ControllerModel {
|
||||||
|
|
||||||
constructor(treeDataProvider?: AzureArcTreeDataProvider, info?: Partial<ControllerInfo>) {
|
// constructor(treeDataProvider?: AzureArcTreeDataProvider, info?: Partial<ControllerInfo>) {
|
||||||
const _info: ControllerInfo = Object.assign({ id: uuid(), endpoint: '', kubeConfigFilePath: '', kubeClusterContext: '', name: '', namespace: '', username: '', rememberPassword: false, resources: [], resourceGroup: '', connectionMode: '', location: '', customLocation: '' }, info);
|
// const _info: ControllerInfo = Object.assign({ id: uuid(), endpoint: '', kubeConfigFilePath: '', kubeClusterContext: '', name: '', namespace: '', username: '', rememberPassword: false, resources: [], resourceGroup: '', connectionMode: '', location: '', customLocation: '' }, info);
|
||||||
super(treeDataProvider!, _info);
|
// super(treeDataProvider!, _info);
|
||||||
}
|
// }
|
||||||
|
|
||||||
}
|
// }
|
||||||
|
|||||||
@@ -1,66 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import * as vscode from 'vscode';
|
|
||||||
import * as azdata from 'azdata';
|
|
||||||
import * as loc from '../../../localizedConstants';
|
|
||||||
import { IconPathHelper, cssStyles } from '../../../constants';
|
|
||||||
import { DashboardPage } from '../../components/dashboardPage';
|
|
||||||
import { PostgresModel } from '../../../models/postgresModel';
|
|
||||||
import { ControllerModel } from '../../../models/controllerModel';
|
|
||||||
|
|
||||||
export class PostgresDiagnoseAndSolveProblemsPage extends DashboardPage {
|
|
||||||
constructor(modelView: azdata.ModelView, dashboard: azdata.window.ModelViewDashboard, private _context: vscode.ExtensionContext, private _controllerModel: ControllerModel, private _postgresModel: PostgresModel) {
|
|
||||||
super(modelView, dashboard);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected get title(): string {
|
|
||||||
return loc.diagnoseAndSolveProblems;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected get id(): string {
|
|
||||||
return 'postgres-diagnose-and-solve-problems';
|
|
||||||
}
|
|
||||||
|
|
||||||
protected get icon(): { dark: string; light: string; } {
|
|
||||||
return IconPathHelper.wrench;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected get container(): azdata.Component {
|
|
||||||
const root = this.modelView.modelBuilder.divContainer().component();
|
|
||||||
const content = this.modelView.modelBuilder.divContainer().component();
|
|
||||||
root.addItem(content, { CSSStyles: { 'margin': '20px' } });
|
|
||||||
|
|
||||||
content.addItem(this.modelView.modelBuilder.text().withProps({
|
|
||||||
value: loc.diagnoseAndSolveProblems,
|
|
||||||
CSSStyles: { ...cssStyles.title, 'margin-bottom': '20px' }
|
|
||||||
}).component());
|
|
||||||
|
|
||||||
content.addItem(this.modelView.modelBuilder.text().withProps({
|
|
||||||
value: loc.clickTheTroubleshootButton('Postgres'),
|
|
||||||
CSSStyles: { ...cssStyles.text, 'margin-bottom': '20px' }
|
|
||||||
}).component());
|
|
||||||
|
|
||||||
const troubleshootButton = this.modelView.modelBuilder.button().withProps({
|
|
||||||
iconPath: IconPathHelper.wrench,
|
|
||||||
label: loc.troubleshoot,
|
|
||||||
width: '160px'
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
this.disposables.push(
|
|
||||||
troubleshootButton.onDidClick(() => {
|
|
||||||
process.env['POSTGRES_SERVER_NAMESPACE'] = this._controllerModel.controllerConfig?.metadata.namespace ?? '';
|
|
||||||
process.env['POSTGRES_SERVER_NAME'] = this._postgresModel.info.name;
|
|
||||||
vscode.commands.executeCommand('bookTreeView.openBook', this._context.asAbsolutePath('notebooks/arcDataServices'), true, 'postgres/tsg100-troubleshoot-postgres');
|
|
||||||
}));
|
|
||||||
|
|
||||||
content.addItem(troubleshootButton);
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected get toolbarContainer(): azdata.ToolbarContainer {
|
|
||||||
return this.modelView.modelBuilder.toolbarContainer().component();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,119 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import * as vscode from 'vscode';
|
|
||||||
import * as azdata from 'azdata';
|
|
||||||
import * as loc from '../../../localizedConstants';
|
|
||||||
import { IconPathHelper, cssStyles } from '../../../constants';
|
|
||||||
import { KeyValueContainer, KeyValue, InputKeyValue, TextKeyValue, LinkKeyValue } from '../../components/keyValueContainer';
|
|
||||||
import { DashboardPage } from '../../components/dashboardPage';
|
|
||||||
import { ControllerModel } from '../../../models/controllerModel';
|
|
||||||
import { PostgresModel } from '../../../models/postgresModel';
|
|
||||||
import { ControllerDashboard } from '../controller/controllerDashboard';
|
|
||||||
|
|
||||||
export class PostgresPropertiesPage extends DashboardPage {
|
|
||||||
private loading?: azdata.LoadingComponent;
|
|
||||||
private keyValueContainer?: KeyValueContainer;
|
|
||||||
|
|
||||||
constructor(modelView: azdata.ModelView, dashboard: azdata.window.ModelViewDashboard, private _controllerModel: ControllerModel, private _postgresModel: PostgresModel) {
|
|
||||||
super(modelView, dashboard);
|
|
||||||
|
|
||||||
this.disposables.push(this._postgresModel.onConfigUpdated(
|
|
||||||
() => this.eventuallyRunOnInitialized(() => this.handleServiceUpdated())));
|
|
||||||
|
|
||||||
this.disposables.push(this._controllerModel.onRegistrationsUpdated(
|
|
||||||
() => this.eventuallyRunOnInitialized(() => this.handleRegistrationsUpdated())));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected get title(): string {
|
|
||||||
return loc.properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected get id(): string {
|
|
||||||
return 'postgres-properties';
|
|
||||||
}
|
|
||||||
|
|
||||||
protected get icon(): { dark: string; light: string; } {
|
|
||||||
return IconPathHelper.properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected get container(): azdata.Component {
|
|
||||||
const root = this.modelView.modelBuilder.divContainer().component();
|
|
||||||
const content = this.modelView.modelBuilder.divContainer().component();
|
|
||||||
root.addItem(content, { CSSStyles: { 'margin': '20px' } });
|
|
||||||
|
|
||||||
content.addItem(this.modelView.modelBuilder.text().withProps({
|
|
||||||
value: loc.properties,
|
|
||||||
CSSStyles: { ...cssStyles.title, 'margin-bottom': '25px' }
|
|
||||||
}).component());
|
|
||||||
|
|
||||||
this.keyValueContainer = new KeyValueContainer(this.modelView.modelBuilder, this.getProperties());
|
|
||||||
this.keyValueContainer.container.updateCssStyles({ 'max-width': '750px' });
|
|
||||||
this.disposables.push(this.keyValueContainer);
|
|
||||||
|
|
||||||
this.loading = this.modelView.modelBuilder.loadingComponent()
|
|
||||||
.withItem(this.keyValueContainer.container)
|
|
||||||
.withProps({
|
|
||||||
loading: !this._postgresModel.configLastUpdated && !this._controllerModel.registrationsLastUpdated
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
content.addItem(this.loading);
|
|
||||||
this.initialized = true;
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected get toolbarContainer(): azdata.ToolbarContainer {
|
|
||||||
const refreshButton = this.modelView.modelBuilder.button().withProps({
|
|
||||||
label: loc.refresh,
|
|
||||||
iconPath: IconPathHelper.refresh
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
this.disposables.push(
|
|
||||||
refreshButton.onDidClick(async () => {
|
|
||||||
refreshButton.enabled = false;
|
|
||||||
try {
|
|
||||||
this.loading!.loading = true;
|
|
||||||
await Promise.all([
|
|
||||||
this._postgresModel.refresh(),
|
|
||||||
this._controllerModel.refresh(false, this._controllerModel.info.namespace)
|
|
||||||
]);
|
|
||||||
} catch (error) {
|
|
||||||
vscode.window.showErrorMessage(loc.refreshFailed(error));
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
refreshButton.enabled = true;
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
return this.modelView.modelBuilder.toolbarContainer().withToolbarItems([
|
|
||||||
{ component: refreshButton }
|
|
||||||
]).component();
|
|
||||||
}
|
|
||||||
|
|
||||||
private getProperties(): KeyValue[] {
|
|
||||||
const endpoint = this._postgresModel.endpoint;
|
|
||||||
const status = this._postgresModel.config?.status;
|
|
||||||
const controllerDashboard = new ControllerDashboard(this._controllerModel);
|
|
||||||
|
|
||||||
return [
|
|
||||||
new InputKeyValue(this.modelView.modelBuilder, loc.coordinatorEndpoint, endpoint ? `postgresql://postgres@${endpoint.ip}:${endpoint.port}` : ''),
|
|
||||||
new InputKeyValue(this.modelView.modelBuilder, loc.postgresAdminUsername, 'postgres'),
|
|
||||||
new InputKeyValue(this.modelView.modelBuilder, loc.subscriptionId, this._controllerModel.controllerConfig?.spec.settings.azure.subscription ?? ''),
|
|
||||||
new TextKeyValue(this.modelView.modelBuilder, loc.resourceGroup, this._controllerModel.controllerConfig?.spec.settings.azure.resourceGroup ?? ''),
|
|
||||||
new LinkKeyValue(this.modelView.modelBuilder, loc.dataController, this._controllerModel.controllerConfig?.metadata.name ?? '', () => controllerDashboard.showDashboard()),
|
|
||||||
new TextKeyValue(this.modelView.modelBuilder, loc.status, status ? `${status.state} (${status.readyPods} ${loc.podsReady})` : loc.unknown)
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleRegistrationsUpdated() {
|
|
||||||
this.keyValueContainer?.refresh(this.getProperties());
|
|
||||||
this.loading!.loading = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleServiceUpdated() {
|
|
||||||
this.keyValueContainer?.refresh(this.getProperties());
|
|
||||||
this.loading!.loading = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,348 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import * as vscode from 'vscode';
|
|
||||||
import * as azdata from 'azdata';
|
|
||||||
import * as loc from '../../../localizedConstants';
|
|
||||||
import { IconPathHelper, cssStyles, iconSize } from '../../../constants';
|
|
||||||
import { DashboardPage } from '../../components/dashboardPage';
|
|
||||||
import { PostgresModel } from '../../../models/postgresModel';
|
|
||||||
|
|
||||||
export type PodHealthModel = {
|
|
||||||
condition: string,
|
|
||||||
details: azdata.Component,
|
|
||||||
lastUpdate: string
|
|
||||||
};
|
|
||||||
|
|
||||||
export enum PodConditionType {
|
|
||||||
initialized = 'Initialized',
|
|
||||||
ready = 'Ready',
|
|
||||||
containersReady = 'ContainersReady',
|
|
||||||
podScheduled = 'PodScheduled'
|
|
||||||
}
|
|
||||||
|
|
||||||
export class PostgresResourceHealthPage extends DashboardPage {
|
|
||||||
private podSummaryContainer!: azdata.DivContainer;
|
|
||||||
|
|
||||||
private podConditionsContainer!: azdata.DivContainer;
|
|
||||||
private podConditionsLoading!: azdata.LoadingComponent;
|
|
||||||
private podConditionsTable!: azdata.DeclarativeTableComponent;
|
|
||||||
private podConditionsTableIndexes: Map<string, number[]> = new Map();
|
|
||||||
|
|
||||||
private podDropDown!: azdata.DropDownComponent;
|
|
||||||
private coordinatorPodName!: string;
|
|
||||||
private coordinatorData: PodHealthModel[] = [];
|
|
||||||
private podsData: PodHealthModel[] = [];
|
|
||||||
|
|
||||||
constructor(modelView: azdata.ModelView, dashboard: azdata.window.ModelViewDashboard, private _postgresModel: PostgresModel) {
|
|
||||||
super(modelView, dashboard);
|
|
||||||
|
|
||||||
this.disposables.push(
|
|
||||||
this._postgresModel.onConfigUpdated(() => this.eventuallyRunOnInitialized(() => this.handleConfigUpdated())));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected get title(): string {
|
|
||||||
return loc.resourceHealth;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected get id(): string {
|
|
||||||
return 'postgres-resource-health';
|
|
||||||
}
|
|
||||||
|
|
||||||
protected get icon(): { dark: string; light: string; } {
|
|
||||||
return IconPathHelper.health;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected get container(): azdata.Component {
|
|
||||||
const root = this.modelView.modelBuilder.divContainer().component();
|
|
||||||
const content = this.modelView.modelBuilder.divContainer().component();
|
|
||||||
root.addItem(content, { CSSStyles: { 'margin': '10px 20px 0px 20px' } });
|
|
||||||
|
|
||||||
content.addItem(this.modelView.modelBuilder.text().withProps({
|
|
||||||
value: loc.resourceHealth,
|
|
||||||
CSSStyles: { ...cssStyles.title }
|
|
||||||
}).component());
|
|
||||||
|
|
||||||
content.addItem(this.modelView.modelBuilder.text().withProps({
|
|
||||||
value: loc.resourceHealthDescription,
|
|
||||||
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
|
||||||
}).component());
|
|
||||||
|
|
||||||
this.podSummaryContainer = this.modelView.modelBuilder.divContainer().component();
|
|
||||||
|
|
||||||
this.refreshPodSummarySection();
|
|
||||||
|
|
||||||
content.addItem(this.podSummaryContainer);
|
|
||||||
|
|
||||||
// Pod Conditions
|
|
||||||
content.addItem(this.modelView.modelBuilder.text().withProps({
|
|
||||||
value: loc.podsPresent,
|
|
||||||
CSSStyles: { ...cssStyles.title }
|
|
||||||
}).component());
|
|
||||||
|
|
||||||
content.addItem(this.modelView.modelBuilder.text().withProps({
|
|
||||||
value: loc.podsUsedDescription,
|
|
||||||
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px', 'margin-top': '10px' }
|
|
||||||
}).component());
|
|
||||||
|
|
||||||
this.podConditionsContainer = this.modelView.modelBuilder.divContainer().component();
|
|
||||||
this.podConditionsTable = this.modelView.modelBuilder.declarativeTable().withProps({
|
|
||||||
width: '100%',
|
|
||||||
ariaLabel: loc.podConditionsTable,
|
|
||||||
columns: [
|
|
||||||
{
|
|
||||||
displayName: loc.condition,
|
|
||||||
valueType: azdata.DeclarativeDataType.string,
|
|
||||||
isReadOnly: true,
|
|
||||||
width: '20%',
|
|
||||||
headerCssStyles: cssStyles.tableHeader,
|
|
||||||
rowCssStyles: cssStyles.tableRow
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: loc.details,
|
|
||||||
valueType: azdata.DeclarativeDataType.component,
|
|
||||||
isReadOnly: true,
|
|
||||||
width: '50%',
|
|
||||||
headerCssStyles: cssStyles.tableHeader,
|
|
||||||
rowCssStyles: {
|
|
||||||
...cssStyles.tableRow,
|
|
||||||
'min-width': '150px'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: loc.lastTransition,
|
|
||||||
valueType: azdata.DeclarativeDataType.string,
|
|
||||||
isReadOnly: true,
|
|
||||||
width: '30%',
|
|
||||||
headerCssStyles: cssStyles.tableHeader,
|
|
||||||
rowCssStyles: cssStyles.tableRow
|
|
||||||
}
|
|
||||||
],
|
|
||||||
dataValues: this.createPodConditionsDataValues(this.coordinatorData)
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
this.podDropDown = this.modelView.modelBuilder.dropDown().withProps({
|
|
||||||
width: '150px',
|
|
||||||
ariaLabel: loc.podsUsedDescriptionAria
|
|
||||||
}).component();
|
|
||||||
this.disposables.push(
|
|
||||||
this.podDropDown.onValueChanged(() => {
|
|
||||||
this.podConditionsTable.setFilter(this.podConditionsTableIndexes.get(String(this.podDropDown.value)));
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
this.podConditionsContainer.addItem(this.podDropDown, { CSSStyles: { 'margin': '10px 0px 10px 0px' } });
|
|
||||||
this.podConditionsContainer.addItem(this.podConditionsTable);
|
|
||||||
this.podConditionsLoading = this.modelView.modelBuilder.loadingComponent()
|
|
||||||
.withItem(this.podConditionsContainer)
|
|
||||||
.withProps({
|
|
||||||
loading: !this._postgresModel.configLastUpdated
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
this.refreshPodConditions();
|
|
||||||
|
|
||||||
content.addItem(this.podConditionsLoading, { CSSStyles: cssStyles.text });
|
|
||||||
|
|
||||||
this.initialized = true;
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected get toolbarContainer(): azdata.ToolbarContainer {
|
|
||||||
// Refresh
|
|
||||||
const refreshButton = this.modelView.modelBuilder.button().withProps({
|
|
||||||
label: loc.refresh,
|
|
||||||
iconPath: IconPathHelper.refresh
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
this.disposables.push(
|
|
||||||
refreshButton.onDidClick(async () => {
|
|
||||||
refreshButton.enabled = false;
|
|
||||||
try {
|
|
||||||
this.podConditionsLoading!.loading = true;
|
|
||||||
await this._postgresModel.refresh();
|
|
||||||
} catch (error) {
|
|
||||||
vscode.window.showErrorMessage(loc.refreshFailed(error));
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
refreshButton.enabled = true;
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
return this.modelView.modelBuilder.toolbarContainer().withToolbarItems([
|
|
||||||
{ component: refreshButton }
|
|
||||||
]).component();
|
|
||||||
}
|
|
||||||
|
|
||||||
private createPodList(): string[] {
|
|
||||||
const podStatus = this._postgresModel.config?.status.podsStatus;
|
|
||||||
let podNames: string[] = [];
|
|
||||||
|
|
||||||
podStatus?.forEach(p => {
|
|
||||||
let podHealthModels: PodHealthModel[] = [];
|
|
||||||
let indexes: number[] = [];
|
|
||||||
|
|
||||||
|
|
||||||
p.conditions.forEach(c => {
|
|
||||||
let message: string;
|
|
||||||
let imageComponent = this.modelView.modelBuilder.image().withProps({
|
|
||||||
width: iconSize,
|
|
||||||
height: iconSize,
|
|
||||||
iconHeight: '15px',
|
|
||||||
iconWidth: '15px'
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
if (c.status === 'False') {
|
|
||||||
imageComponent.iconPath = IconPathHelper.fail;
|
|
||||||
message = c.message ?? c.reason ?? '';
|
|
||||||
} else {
|
|
||||||
imageComponent.iconPath = IconPathHelper.success;
|
|
||||||
|
|
||||||
if (c.type === PodConditionType.initialized) {
|
|
||||||
message = loc.podInitialized;
|
|
||||||
} else if (c.type === PodConditionType.ready) {
|
|
||||||
message = loc.podReady;
|
|
||||||
} else if (c.type === PodConditionType.containersReady) {
|
|
||||||
message = loc.containerReady;
|
|
||||||
} else if (c.type === PodConditionType.podScheduled) {
|
|
||||||
message = loc.podScheduled;
|
|
||||||
} else {
|
|
||||||
message = c.message ?? c.reason ?? '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const conditionContainer = this.modelView.modelBuilder.flexContainer().withProps({
|
|
||||||
CSSStyles: { 'alignItems': 'center', 'height': '15px' }
|
|
||||||
}).component();
|
|
||||||
conditionContainer.addItem(imageComponent, { CSSStyles: { 'margin-right': '0px' } });
|
|
||||||
conditionContainer.addItem(this.modelView.modelBuilder.text().withProps({
|
|
||||||
value: message,
|
|
||||||
}).component());
|
|
||||||
|
|
||||||
indexes.push(this.podsData.length);
|
|
||||||
this.podsData.push({
|
|
||||||
condition: c.type,
|
|
||||||
details: conditionContainer,
|
|
||||||
lastUpdate: c.lastTransitionTime
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (p.role.toUpperCase() !== loc.coordinator.toUpperCase()) {
|
|
||||||
podNames.push(p.name);
|
|
||||||
} else {
|
|
||||||
this.coordinatorData = podHealthModels;
|
|
||||||
this.coordinatorPodName = p.name;
|
|
||||||
podNames.unshift(p.name);
|
|
||||||
}
|
|
||||||
this.podConditionsTableIndexes.set(p.name, indexes);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.podConditionsTable.setDataValues(this.createPodConditionsDataValues(this.podsData));
|
|
||||||
|
|
||||||
return podNames;
|
|
||||||
}
|
|
||||||
|
|
||||||
private createPodConditionsDataValues(podInfo: PodHealthModel[]): azdata.DeclarativeTableCellValue[][] {
|
|
||||||
let podDataValues: (string | azdata.Component)[][] = podInfo.map(p => [p.condition, p.details, p.lastUpdate]);
|
|
||||||
return podDataValues.map(p => {
|
|
||||||
return p.map((value): azdata.DeclarativeTableCellValue => {
|
|
||||||
return { value: value };
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private findPodIssues(): string[] {
|
|
||||||
const podStatus = this._postgresModel.config?.status.podsStatus;
|
|
||||||
let issueCount = 0;
|
|
||||||
let podIssuesDetected: string[] = [];
|
|
||||||
|
|
||||||
podStatus?.forEach(p => {
|
|
||||||
p.conditions.forEach(c => {
|
|
||||||
if (c.status === 'False') {
|
|
||||||
issueCount++;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (issueCount > 0) {
|
|
||||||
podIssuesDetected.push(loc.numberOfIssuesDetected(p.name, issueCount));
|
|
||||||
issueCount = 0;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return podIssuesDetected;
|
|
||||||
}
|
|
||||||
|
|
||||||
private refreshPodSummarySection(): void {
|
|
||||||
let podSummaryTitle = this.modelView.modelBuilder.flexContainer().withProps({
|
|
||||||
CSSStyles: { 'alignItems': 'center', 'height': '15px', 'margin-top': '20px' }
|
|
||||||
}).component();
|
|
||||||
if (!this._postgresModel.config) {
|
|
||||||
podSummaryTitle.addItem(this.modelView.modelBuilder.loadingComponent().component(), { CSSStyles: { 'margin-right': '5px' } });
|
|
||||||
podSummaryTitle.addItem(this.modelView.modelBuilder.text().withProps({
|
|
||||||
value: loc.loading,
|
|
||||||
CSSStyles: { ...cssStyles.title }
|
|
||||||
}).component());
|
|
||||||
this.podSummaryContainer.addItem(podSummaryTitle);
|
|
||||||
} else {
|
|
||||||
let components: azdata.Component[] = [];
|
|
||||||
let imageComponent = this.modelView.modelBuilder.image().withProps({
|
|
||||||
iconPath: IconPathHelper.success,
|
|
||||||
width: iconSize,
|
|
||||||
height: iconSize,
|
|
||||||
iconHeight: '20px',
|
|
||||||
iconWidth: '20px'
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
let podIssues = this.findPodIssues();
|
|
||||||
if (podIssues.length === 0) {
|
|
||||||
imageComponent.iconPath = IconPathHelper.success;
|
|
||||||
podSummaryTitle.addItem(imageComponent, { CSSStyles: { 'margin-right': '5px' } });
|
|
||||||
podSummaryTitle.addItem(this.modelView.modelBuilder.text().withProps({
|
|
||||||
value: loc.available,
|
|
||||||
CSSStyles: { ...cssStyles.title, 'margin-left': '0px' }
|
|
||||||
}).component());
|
|
||||||
components.push(podSummaryTitle);
|
|
||||||
components.push(this.modelView.modelBuilder.text().withProps({
|
|
||||||
value: loc.noPodIssuesDetected,
|
|
||||||
CSSStyles: { ...cssStyles.text, 'margin-top': '20px' }
|
|
||||||
}).component());
|
|
||||||
} else {
|
|
||||||
imageComponent.iconPath = IconPathHelper.fail;
|
|
||||||
podSummaryTitle.addItem(imageComponent, { CSSStyles: { 'margin-right': '5px' } });
|
|
||||||
podSummaryTitle.addItem(this.modelView.modelBuilder.text().withProps({
|
|
||||||
value: loc.issuesDetected,
|
|
||||||
CSSStyles: { ...cssStyles.title }
|
|
||||||
}).component());
|
|
||||||
components.push(podSummaryTitle);
|
|
||||||
components.push(this.modelView.modelBuilder.text().withProps({
|
|
||||||
value: loc.podIssuesDetected,
|
|
||||||
CSSStyles: { ...cssStyles.text, 'margin-top': '20px 0px 10px 0px' }
|
|
||||||
}).component());
|
|
||||||
components.push(...podIssues.map(i => {
|
|
||||||
return this.modelView.modelBuilder.text().withProps({
|
|
||||||
value: i,
|
|
||||||
CSSStyles: { ...cssStyles.text, 'margin': '0px' }
|
|
||||||
}).component();
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
this.podSummaryContainer.addItems(components);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private refreshPodConditions(): void {
|
|
||||||
if (this._postgresModel.config) {
|
|
||||||
this.podConditionsTableIndexes = new Map();
|
|
||||||
this.podsData = [];
|
|
||||||
this.podDropDown.values = this.createPodList();
|
|
||||||
this.podConditionsTable.setFilter(this.podConditionsTableIndexes.get(this.coordinatorPodName!));
|
|
||||||
this.podConditionsLoading.loading = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleConfigUpdated() {
|
|
||||||
this.podSummaryContainer.clearItems();
|
|
||||||
this.refreshPodSummarySection();
|
|
||||||
this.refreshPodConditions();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import * as azdata from 'azdata';
|
|
||||||
import { Deferred } from '../../common/promise';
|
|
||||||
import * as loc from '../../localizedConstants';
|
|
||||||
import { cssStyles } from '../../constants';
|
|
||||||
import { InitializingComponent } from '../components/initializingComponent';
|
|
||||||
import { PostgresModel } from '../../models/postgresModel';
|
|
||||||
|
|
||||||
export const validExtensions = ['citus', 'pgaudit', 'pgautofailover', 'pg_cron', 'pg_partman', 'plv8', 'postgis', 'postgis_raster', 'postgis_sfcgal', 'postgis_tiger_geocoder', 'tdigest'];
|
|
||||||
|
|
||||||
export class AddPGExtensionsDialog extends InitializingComponent {
|
|
||||||
protected modelBuilder!: azdata.ModelBuilder;
|
|
||||||
|
|
||||||
protected extensionsListInputBox!: azdata.InputBoxComponent;
|
|
||||||
|
|
||||||
protected _completionPromise = new Deferred<string | undefined>();
|
|
||||||
|
|
||||||
constructor(protected _model: PostgresModel) {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
public showDialog(dialogTitle: string): azdata.window.Dialog {
|
|
||||||
const dialog = azdata.window.createModelViewDialog(dialogTitle);
|
|
||||||
dialog.cancelButton.onClick(() => this.handleCancel());
|
|
||||||
dialog.registerContent(async view => {
|
|
||||||
this.modelBuilder = view.modelBuilder;
|
|
||||||
|
|
||||||
const info = this.modelBuilder.text().withProps({
|
|
||||||
value: loc.extensionsAddFunction(validExtensions.join(', ')),
|
|
||||||
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
const link = this.modelBuilder.hyperlink().withProps({
|
|
||||||
label: loc.extensionsLearnMore,
|
|
||||||
url: 'https://docs.microsoft.com/azure/azure-arc/data/using-extensions-in-postgresql-hyperscale-server-group',
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
const infoAndLink = this.modelBuilder.flexContainer().withLayout({ flexWrap: 'wrap' }).component();
|
|
||||||
infoAndLink.addItem(info, { CSSStyles: { 'margin-right': '5px' } });
|
|
||||||
infoAndLink.addItem(link);
|
|
||||||
|
|
||||||
this.extensionsListInputBox = this.modelBuilder.inputBox()
|
|
||||||
.withProps({
|
|
||||||
value: '',
|
|
||||||
ariaLabel: loc.extensionsAddList,
|
|
||||||
enabled: true,
|
|
||||||
validationErrorMessage: loc.extensionsAddErrorrMessage(validExtensions.join(','))
|
|
||||||
}).withValidation((component) => {
|
|
||||||
if (!component.value) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let newExtensions = component.value.split(',');
|
|
||||||
return newExtensions.every(e => validExtensions.includes(e));
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
let formModel = this.modelBuilder.formContainer()
|
|
||||||
.withFormItems([{
|
|
||||||
components: [
|
|
||||||
{
|
|
||||||
component: infoAndLink
|
|
||||||
},
|
|
||||||
{
|
|
||||||
component: this.extensionsListInputBox,
|
|
||||||
title: loc.extensionsAddList,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
title: ''
|
|
||||||
}]).withLayout({ width: '100%' }).component();
|
|
||||||
await view.initializeModel(formModel);
|
|
||||||
this.extensionsListInputBox.focus();
|
|
||||||
this.initialized = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
dialog.registerCloseValidator(async () => await this.validate());
|
|
||||||
dialog.okButton.label = loc.loadExtensions;
|
|
||||||
dialog.cancelButton.label = loc.cancel;
|
|
||||||
azdata.window.openDialog(dialog);
|
|
||||||
return dialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async validate(): Promise<boolean> {
|
|
||||||
this._completionPromise.resolve(this.extensionsListInputBox.value);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleCancel(): void {
|
|
||||||
this._completionPromise.resolve(undefined);
|
|
||||||
}
|
|
||||||
|
|
||||||
public waitForClose(): Promise<string | undefined> {
|
|
||||||
return this._completionPromise.promise;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
interface PlatformReleaseInfo {
|
|
||||||
version: string; // "20.0.1"
|
|
||||||
link?: string; // "https://aka.ms/az-msi"
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AzReleaseInfo {
|
|
||||||
win32: PlatformReleaseInfo,
|
|
||||||
darwin: PlatformReleaseInfo,
|
|
||||||
linux: PlatformReleaseInfo
|
|
||||||
}
|
|
||||||
@@ -9,12 +9,12 @@
|
|||||||
* @param promise The promise to verify was rejected
|
* @param promise The promise to verify was rejected
|
||||||
* @param message The message to include in the error if the promise isn't rejected
|
* @param message The message to include in the error if the promise isn't rejected
|
||||||
*/
|
*/
|
||||||
export async function assertRejected(promise: Promise<any>, message: string): Promise<void> {
|
// export async function assertRejected(promise: Promise<any>, message: string): Promise<void> {
|
||||||
try {
|
// try {
|
||||||
await promise;
|
// await promise;
|
||||||
} catch {
|
// } catch {
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
throw new Error(message);
|
// throw new Error(message);
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|||||||
@@ -1,214 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import * as vscode from 'vscode';
|
|
||||||
import * as azdata from 'azdata';
|
|
||||||
import { AppContext } from '../../appContext';
|
|
||||||
import * as nls from 'vscode-nls';
|
|
||||||
const localize = nls.loadMessageBundle();
|
|
||||||
|
|
||||||
import { TreeNode } from '../treeNode';
|
|
||||||
import { AzureResourceMessageTreeNode } from '../messageTreeNode';
|
|
||||||
import { AzureResourceContainerTreeNodeBase } from './baseTreeNodes';
|
|
||||||
import { AzureResourceErrorMessageUtil, filterAccounts } from '../utils';
|
|
||||||
import { IAzureResourceTreeChangeHandler } from './treeChangeHandler';
|
|
||||||
import { IAzureResourceNodeWithProviderId, IAzureResourceSubscriptionService } from '../interfaces';
|
|
||||||
import { AzureResourceServiceNames } from '../constants';
|
|
||||||
import { AzureResourceService } from '../resourceService';
|
|
||||||
import { Logger } from '../../utils/Logger';
|
|
||||||
|
|
||||||
export class FlatAzureResourceTreeProvider implements vscode.TreeDataProvider<TreeNode>, IAzureResourceTreeChangeHandler {
|
|
||||||
public isSystemInitialized: boolean = false;
|
|
||||||
|
|
||||||
private _onDidChangeTreeData = new vscode.EventEmitter<TreeNode | undefined>();
|
|
||||||
|
|
||||||
private resourceLoader: ResourceLoader | undefined;
|
|
||||||
|
|
||||||
public constructor(private readonly appContext: AppContext,
|
|
||||||
private readonly authLibrary: string) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getChildren(element?: TreeNode): Promise<TreeNode[]> {
|
|
||||||
if (element) {
|
|
||||||
return element.getChildren(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.resourceLoader) {
|
|
||||||
this.resourceLoader = new ResourceLoader(this.appContext, this.authLibrary);
|
|
||||||
this.resourceLoader.onDidAddNewResource(e => this._onDidChangeTreeData.fire(e));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.resourceLoader.state === LoaderState.NotStarted) {
|
|
||||||
this.resourceLoader.start().catch(err => console.error('Error loading Azure Resources for FlatAzureResourceTreeProvider ', err));
|
|
||||||
return [AzureResourceMessageTreeNode.create(localize('azure.resource.tree.treeProvider.loadingLabel', "Loading ..."), undefined)];
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.resourceLoader.children;
|
|
||||||
}
|
|
||||||
|
|
||||||
public get onDidChangeTreeData(): vscode.Event<TreeNode | undefined> {
|
|
||||||
return this._onDidChangeTreeData.event;
|
|
||||||
}
|
|
||||||
|
|
||||||
public notifyNodeChanged(node: TreeNode): void {
|
|
||||||
this._onDidChangeTreeData.fire(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async refresh(node: TreeNode, isClearingCache: boolean): Promise<void> {
|
|
||||||
if (isClearingCache) {
|
|
||||||
if ((node instanceof AzureResourceContainerTreeNodeBase)) {
|
|
||||||
node.clearCache();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this._onDidChangeTreeData.fire(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
public getTreeItem(element: TreeNode): vscode.TreeItem | Thenable<vscode.TreeItem> {
|
|
||||||
return element.getTreeItem();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum LoaderState {
|
|
||||||
NotStarted,
|
|
||||||
Loading,
|
|
||||||
Complete
|
|
||||||
}
|
|
||||||
|
|
||||||
class ResourceLoader {
|
|
||||||
private _state: LoaderState = LoaderState.NotStarted;
|
|
||||||
|
|
||||||
private readonly resourceGroups = new Map<string, AzureResourceResourceTreeNode>();
|
|
||||||
|
|
||||||
private readonly subscriptionService: IAzureResourceSubscriptionService;
|
|
||||||
private readonly resourceService: AzureResourceService;
|
|
||||||
|
|
||||||
private readonly _onDidAddNewResource = new vscode.EventEmitter<TreeNode | undefined>();
|
|
||||||
public readonly onDidAddNewResource = this._onDidAddNewResource.event;
|
|
||||||
|
|
||||||
constructor(private readonly appContext: AppContext,
|
|
||||||
private readonly authLibrary: string) {
|
|
||||||
this.subscriptionService = appContext.getService<IAzureResourceSubscriptionService>(AzureResourceServiceNames.subscriptionService);
|
|
||||||
this.resourceService = appContext.getService<AzureResourceService>(AzureResourceServiceNames.resourceService);
|
|
||||||
}
|
|
||||||
|
|
||||||
get state(): LoaderState {
|
|
||||||
return this._state;
|
|
||||||
}
|
|
||||||
|
|
||||||
get children(): AzureResourceResourceTreeNode[] {
|
|
||||||
return Array.from(this.resourceGroups.values());
|
|
||||||
}
|
|
||||||
|
|
||||||
async start(): Promise<void> {
|
|
||||||
if (this.state === LoaderState.Loading) {
|
|
||||||
throw new Error('Resource Loader already loading');
|
|
||||||
}
|
|
||||||
|
|
||||||
let doRefresh = false;
|
|
||||||
|
|
||||||
// if we just fire every time we get an a new resource we crash the application
|
|
||||||
// this effectively buffers the event so that we don't cause hangs.
|
|
||||||
let interval = setInterval(() => {
|
|
||||||
if (doRefresh) {
|
|
||||||
doRefresh = false;
|
|
||||||
this._onDidAddNewResource.fire(undefined);
|
|
||||||
}
|
|
||||||
}, 500);
|
|
||||||
|
|
||||||
this._state = LoaderState.Loading;
|
|
||||||
|
|
||||||
const accounts = filterAccounts(await azdata.accounts.getAllAccounts(), this.authLibrary);
|
|
||||||
|
|
||||||
for (const account of accounts) {
|
|
||||||
for (const tenant of account.properties.tenants) {
|
|
||||||
for (const subscription of await this.subscriptionService.getSubscriptions(account, [tenant.id])) {
|
|
||||||
for (const providerId of await this.resourceService.listResourceProviderIds()) {
|
|
||||||
for (const group of await this.resourceService.getRootChildren(providerId, account, subscription)) {
|
|
||||||
const children = await this.resourceService.getChildren(providerId, group.resourceNode);
|
|
||||||
let groupNode: AzureResourceResourceTreeNode | undefined = this.resourceGroups.get(group.resourceProviderId);
|
|
||||||
if (groupNode) {
|
|
||||||
groupNode.pushItems(...children);
|
|
||||||
} else {
|
|
||||||
groupNode = new AzureResourceResourceTreeNode(group, this.appContext);
|
|
||||||
this.resourceGroups.set(group.resourceProviderId, groupNode);
|
|
||||||
groupNode.pushItems(...children);
|
|
||||||
}
|
|
||||||
doRefresh = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.verbose('finished loading all accounts and subscriptions');
|
|
||||||
|
|
||||||
clearInterval(interval);
|
|
||||||
|
|
||||||
this._state = LoaderState.Complete;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class AzureResourceResourceTreeNode extends TreeNode {
|
|
||||||
private _resourceService: AzureResourceService;
|
|
||||||
|
|
||||||
public constructor(
|
|
||||||
public readonly resourceNodeWithProviderId: IAzureResourceNodeWithProviderId,
|
|
||||||
private appContext: AppContext
|
|
||||||
) {
|
|
||||||
super();
|
|
||||||
this._resourceService = appContext.getService<AzureResourceService>(AzureResourceServiceNames.resourceService);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _children: IAzureResourceNodeWithProviderId[] = [];
|
|
||||||
|
|
||||||
pushItems(...items: IAzureResourceNodeWithProviderId[]): void {
|
|
||||||
this._children.push(...items);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getChildren(): Promise<TreeNode[]> {
|
|
||||||
// It is a leaf node.
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
if (this._children.length === 0) {
|
|
||||||
return [AzureResourceMessageTreeNode.create(localize('azure.resource.resourceTreeNode.noResourcesLabel', "No Resources found"), this)];
|
|
||||||
} else {
|
|
||||||
return this._children.map((child) => {
|
|
||||||
// To make tree node's id unique, otherwise, treeModel.js would complain 'item already registered'
|
|
||||||
child.resourceNode.treeItem.id = `${this.resourceNodeWithProviderId.resourceNode.treeItem.id}.${child.resourceNode.treeItem.id}`;
|
|
||||||
return new AzureResourceResourceTreeNode(child, this.appContext);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
return [AzureResourceMessageTreeNode.create(AzureResourceErrorMessageUtil.getErrorMessage(error), this)];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public getTreeItem(): vscode.TreeItem | Promise<vscode.TreeItem> {
|
|
||||||
return this._resourceService.getTreeItem(this.resourceNodeWithProviderId.resourceProviderId, this.resourceNodeWithProviderId.resourceNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
public getNodeInfo(): azdata.NodeInfo {
|
|
||||||
const treeItem = this.resourceNodeWithProviderId.resourceNode.treeItem;
|
|
||||||
|
|
||||||
return {
|
|
||||||
label: <any>treeItem.label,
|
|
||||||
isLeaf: treeItem.collapsibleState === vscode.TreeItemCollapsibleState.None ? true : false,
|
|
||||||
errorMessage: undefined,
|
|
||||||
metadata: undefined,
|
|
||||||
nodePath: this.generateNodePath(),
|
|
||||||
parentNodePath: this.parent?.generateNodePath() ?? '',
|
|
||||||
nodeStatus: undefined,
|
|
||||||
nodeType: treeItem.contextValue || '',
|
|
||||||
nodeSubType: undefined,
|
|
||||||
iconType: treeItem.contextValue
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public get nodePathValue(): string {
|
|
||||||
return this.resourceNodeWithProviderId.resourceNode.treeItem.id || '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import * as azdata from 'azdata';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mock CredentialsProvider to be used for testing
|
|
||||||
*/
|
|
||||||
export class CredentialsTestProvider implements azdata.CredentialProvider {
|
|
||||||
handle: number = 0;
|
|
||||||
|
|
||||||
public storedCredentials: { [K: string]: azdata.Credential } = {};
|
|
||||||
|
|
||||||
saveCredential(credentialId: string, password: string): Thenable<boolean> {
|
|
||||||
this.storedCredentials[credentialId] = {
|
|
||||||
credentialId: credentialId,
|
|
||||||
password: password
|
|
||||||
};
|
|
||||||
return Promise.resolve(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
readCredential(credentialId: string): Thenable<azdata.Credential> {
|
|
||||||
return Promise.resolve(this.storedCredentials[credentialId]);
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteCredential(credentialId: string): Thenable<boolean> {
|
|
||||||
let exists = this.storedCredentials[credentialId] !== undefined;
|
|
||||||
delete this.storedCredentials[credentialId];
|
|
||||||
return Promise.resolve(exists);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import { Transform } from 'stream';
|
|
||||||
import * as vscode from 'vscode';
|
|
||||||
import * as nls from 'vscode-nls';
|
|
||||||
|
|
||||||
const localize = nls.loadMessageBundle();
|
|
||||||
|
|
||||||
export class CancelableStream extends Transform {
|
|
||||||
constructor(private cancelationToken: vscode.CancellationTokenSource) {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override _transform(chunk: any, _encoding: string, callback: Function): void {
|
|
||||||
if (this.cancelationToken && this.cancelationToken.token.isCancellationRequested) {
|
|
||||||
callback(new Error(localize('streamCanceled', 'Stream operation canceled by the user')));
|
|
||||||
} else {
|
|
||||||
this.push(chunk);
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
// 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 = 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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
'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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import * as fs from 'fs';
|
|
||||||
|
|
||||||
import { IFileSource, IFile } from '../fileSources';
|
|
||||||
|
|
||||||
export class MockFileSource implements IFileSource {
|
|
||||||
filesToReturn: Map<string, IFile[]>;
|
|
||||||
constructor() {
|
|
||||||
this.filesToReturn = new Map<string, IFile[]>();
|
|
||||||
}
|
|
||||||
enumerateFiles(filePath: string): Promise<IFile[]> {
|
|
||||||
let files: IFile[] = this.filesToReturn.get(filePath);
|
|
||||||
return Promise.resolve(files);
|
|
||||||
}
|
|
||||||
|
|
||||||
mkdir(dirName: string, remoteBasePath: string): Promise<void> {
|
|
||||||
return Promise.resolve(undefined);
|
|
||||||
}
|
|
||||||
|
|
||||||
writeFile(localFile: IFile, remoteDir: string): Promise<string> {
|
|
||||||
return Promise.resolve(undefined);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(filePath: string): Promise<void> {
|
|
||||||
throw new Error('Method not implemented.');
|
|
||||||
}
|
|
||||||
|
|
||||||
readFile(filePath: string, maxBytes?: number): Promise<Buffer> {
|
|
||||||
throw new Error('Method not implemented.');
|
|
||||||
}
|
|
||||||
|
|
||||||
readFileLines(path: string, maxLines: number): Promise<Buffer> {
|
|
||||||
throw new Error("Method not implemented.");
|
|
||||||
}
|
|
||||||
|
|
||||||
createReadStream(filePath: string): fs.ReadStream {
|
|
||||||
throw new Error('Method not implemented.');
|
|
||||||
}
|
|
||||||
|
|
||||||
exists(filePath: string): Promise<boolean> {
|
|
||||||
throw new Error('Method not implemented.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import * as assert from 'assert';
|
|
||||||
|
|
||||||
export async function assertThrowsAsync(fn: Function, msg: string): Promise<void> {
|
|
||||||
let f = () => {
|
|
||||||
// Empty
|
|
||||||
};
|
|
||||||
try {
|
|
||||||
await fn();
|
|
||||||
} catch (e) {
|
|
||||||
f = () => { throw e; };
|
|
||||||
} finally {
|
|
||||||
assert.throws(f, undefined, msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import { Transform } from 'stream';
|
|
||||||
import * as vscode from 'vscode';
|
|
||||||
import * as nls from 'vscode-nls';
|
|
||||||
|
|
||||||
const localize = nls.loadMessageBundle();
|
|
||||||
|
|
||||||
export class CancelableStream extends Transform {
|
|
||||||
constructor(private cancelationToken: vscode.CancellationTokenSource) {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override _transform(chunk: any, encoding: string, callback: Function): void {
|
|
||||||
if (this.cancelationToken && this.cancelationToken.token.isCancellationRequested) {
|
|
||||||
callback(new Error(localize('streamCanceled', 'Stream operation canceled by the user')));
|
|
||||||
} else {
|
|
||||||
this.push(chunk);
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
// 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 = 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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
'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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
const _typeof = {
|
|
||||||
undefined: 'undefined'
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns whether the provided parameter is undefined or null.
|
|
||||||
*/
|
|
||||||
export function isUndefinedOrNull(obj: any): boolean {
|
|
||||||
return isUndefined(obj) || obj === null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns whether the provided parameter is undefined.
|
|
||||||
*/
|
|
||||||
export function isUndefined(obj: any): boolean {
|
|
||||||
return typeof (obj) === _typeof.undefined;
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import * as azdata from 'azdata';
|
|
||||||
import { MigrationTargetType } from './stateMachine';
|
|
||||||
|
|
||||||
export interface Base {
|
|
||||||
uuid: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface BaseRequest extends Base { }
|
|
||||||
|
|
||||||
export interface BaseResponse<T> extends Base {
|
|
||||||
error?: string;
|
|
||||||
response: T;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GatherInformationRequest extends BaseRequest {
|
|
||||||
connection: azdata.connection.Connection;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Checks {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SKURecommendation {
|
|
||||||
product: MigrationTargetType;
|
|
||||||
checks: Checks;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SKURecommendations {
|
|
||||||
recommendations: SKURecommendation[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GatherInformationResponse extends BaseResponse<SKURecommendations> {
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,163 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import * as azdata from 'azdata';
|
|
||||||
import * as vscode from 'vscode';
|
|
||||||
import { MigrationWizardPage } from '../models/migrationWizardPage';
|
|
||||||
import { MigrationSourceAuthenticationType, MigrationStateModel, StateChangeEvent } from '../models/stateMachine';
|
|
||||||
import * as constants from '../constants/strings';
|
|
||||||
import { createLabelTextComponent, createHeadingTextComponent, WIZARD_INPUT_COMPONENT_WIDTH } from './wizardController';
|
|
||||||
import { getSourceConnectionCredentials, getSourceConnectionProfile, getSourceConnectionUri } from '../api/sqlUtils';
|
|
||||||
|
|
||||||
export class SqlSourceConfigurationPage extends MigrationWizardPage {
|
|
||||||
private _view!: azdata.ModelView;
|
|
||||||
private _usernameInput!: azdata.InputBoxComponent;
|
|
||||||
private _password!: azdata.InputBoxComponent;
|
|
||||||
private _disposables: vscode.Disposable[] = [];
|
|
||||||
|
|
||||||
constructor(wizard: azdata.window.Wizard, migrationStateModel: MigrationStateModel) {
|
|
||||||
super(wizard, azdata.window.createWizardPage(constants.SOURCE_CONFIGURATION, 'MigrationModePage'), migrationStateModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async registerContent(view: azdata.ModelView): Promise<void> {
|
|
||||||
this.wizard.customButtons[0].enabled = true;
|
|
||||||
this._view = view;
|
|
||||||
const form = view.modelBuilder.formContainer()
|
|
||||||
.withFormItems(
|
|
||||||
[
|
|
||||||
await this.createSourceCredentialContainer(),
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
this._disposables.push(this._view.onClosed(e => {
|
|
||||||
this._disposables.forEach(
|
|
||||||
d => { try { d.dispose(); } catch { } });
|
|
||||||
}));
|
|
||||||
|
|
||||||
await view.initializeModel(form.component());
|
|
||||||
}
|
|
||||||
|
|
||||||
public async onPageEnter(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise<void> {
|
|
||||||
this.wizard.registerNavigationValidator(pageChangeInfo => true);
|
|
||||||
}
|
|
||||||
public async onPageLeave(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise<void> {
|
|
||||||
this.wizard.registerNavigationValidator(pageChangeInfo => true);
|
|
||||||
this.wizard.message = { text: '' };
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async handleStateChange(e: StateChangeEvent): Promise<void> {
|
|
||||||
}
|
|
||||||
|
|
||||||
private async createSourceCredentialContainer(): Promise<azdata.FormComponent> {
|
|
||||||
|
|
||||||
const connectionProfile = await getSourceConnectionProfile();
|
|
||||||
const queryProvider = azdata.dataprotocol.getProvider<azdata.QueryProvider>(connectionProfile.providerId, azdata.DataProviderType.QueryProvider);
|
|
||||||
const query = 'select SUSER_NAME()';
|
|
||||||
const results = await queryProvider.runQueryAndReturn(await getSourceConnectionUri(), query);
|
|
||||||
const username = results.rows[0][0].displayValue;
|
|
||||||
this.migrationStateModel._authenticationType = connectionProfile.authenticationType === azdata.connection.AuthenticationType.SqlLogin
|
|
||||||
? MigrationSourceAuthenticationType.Sql
|
|
||||||
: connectionProfile.authenticationType === azdata.connection.AuthenticationType.Integrated
|
|
||||||
? MigrationSourceAuthenticationType.Integrated
|
|
||||||
: undefined!;
|
|
||||||
|
|
||||||
const sourceCredText = await createHeadingTextComponent(this._view, constants.SOURCE_CREDENTIALS);
|
|
||||||
const enterYourCredText = createLabelTextComponent(
|
|
||||||
this._view,
|
|
||||||
constants.ENTER_YOUR_SQL_CREDS,
|
|
||||||
{
|
|
||||||
'width': '600px',
|
|
||||||
'font-size': '13px',
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const serverLabel = this._view.modelBuilder.text().withProps({
|
|
||||||
value: constants.SERVER,
|
|
||||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
|
||||||
CSSStyles: {
|
|
||||||
'font-size': '13px',
|
|
||||||
'font-weight': 'bold',
|
|
||||||
}
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
const server = this._view.modelBuilder.inputBox().withProps({
|
|
||||||
value: connectionProfile.serverName,
|
|
||||||
enabled: false,
|
|
||||||
width: WIZARD_INPUT_COMPONENT_WIDTH
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
const authenticationTypeLable = this._view.modelBuilder.text().withProps({
|
|
||||||
value: constants.AUTHENTICATION_TYPE,
|
|
||||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
|
||||||
CSSStyles: {
|
|
||||||
'font-size': '13px',
|
|
||||||
'font-weight': 'bold',
|
|
||||||
}
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
const authenticationTypeInput = this._view.modelBuilder.inputBox().withProps({
|
|
||||||
value: this.migrationStateModel._authenticationType === MigrationSourceAuthenticationType.Sql ? 'SQL Login' : 'Windows Authentication',
|
|
||||||
enabled: false,
|
|
||||||
width: WIZARD_INPUT_COMPONENT_WIDTH
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
const usernameLable = this._view.modelBuilder.text().withProps({
|
|
||||||
value: constants.USERNAME,
|
|
||||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
|
||||||
CSSStyles: {
|
|
||||||
'font-size': '13px',
|
|
||||||
'font-weight': 'bold',
|
|
||||||
}
|
|
||||||
}).component();
|
|
||||||
this._usernameInput = this._view.modelBuilder.inputBox().withProps({
|
|
||||||
value: username,
|
|
||||||
required: true,
|
|
||||||
enabled: false,
|
|
||||||
width: WIZARD_INPUT_COMPONENT_WIDTH
|
|
||||||
}).component();
|
|
||||||
this._disposables.push(this._usernameInput.onTextChanged(value => {
|
|
||||||
this.migrationStateModel._sqlServerUsername = value;
|
|
||||||
}));
|
|
||||||
|
|
||||||
const passwordLabel = this._view.modelBuilder.text().withProps({
|
|
||||||
value: constants.DATABASE_BACKUP_NETWORK_SHARE_PASSWORD_LABEL,
|
|
||||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
|
||||||
CSSStyles: {
|
|
||||||
'font-size': '13px',
|
|
||||||
'font-weight': 'bold',
|
|
||||||
}
|
|
||||||
}).component();
|
|
||||||
this._password = this._view.modelBuilder.inputBox().withProps({
|
|
||||||
value: (await getSourceConnectionCredentials()).password,
|
|
||||||
required: true,
|
|
||||||
inputType: 'password',
|
|
||||||
width: WIZARD_INPUT_COMPONENT_WIDTH
|
|
||||||
}).component();
|
|
||||||
this._disposables.push(this._password.onTextChanged(value => {
|
|
||||||
this.migrationStateModel._sqlServerPassword = value;
|
|
||||||
}));
|
|
||||||
|
|
||||||
const container = this._view.modelBuilder.flexContainer().withItems(
|
|
||||||
[
|
|
||||||
sourceCredText,
|
|
||||||
enterYourCredText,
|
|
||||||
serverLabel,
|
|
||||||
server,
|
|
||||||
authenticationTypeLable,
|
|
||||||
authenticationTypeInput,
|
|
||||||
usernameLable,
|
|
||||||
this._usernameInput,
|
|
||||||
passwordLabel,
|
|
||||||
this._password
|
|
||||||
]
|
|
||||||
).withLayout({
|
|
||||||
flexFlow: 'column'
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
return {
|
|
||||||
component: container
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
|
||||||
|
|
||||||
|
|
||||||
export class RowCache implements IDisposable {
|
|
||||||
dispose(): void {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IRow {
|
|
||||||
domNode: HTMLElement | null;
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,155 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
|
|
||||||
|
|
||||||
class DataWindow<T> {
|
|
||||||
private _data: T[] | undefined;
|
|
||||||
private _length: number = 0;
|
|
||||||
private _offsetFromDataSource: number = -1;
|
|
||||||
|
|
||||||
private dataReady?: CancelablePromise<void>;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private loadFunction: (offset: number, count: number) => Promise<T[]>
|
|
||||||
) { }
|
|
||||||
|
|
||||||
dispose() {
|
|
||||||
this._data = undefined;
|
|
||||||
if (this.dataReady) {
|
|
||||||
this.dataReady.cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get start(): number {
|
|
||||||
return this._offsetFromDataSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
get end(): number {
|
|
||||||
return this._offsetFromDataSource + this._length;
|
|
||||||
}
|
|
||||||
|
|
||||||
get length(): number {
|
|
||||||
return this._length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public contains(dataSourceIndex: number): boolean {
|
|
||||||
return dataSourceIndex >= this.start && dataSourceIndex < this.end;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getItem(index: number): Promise<T> {
|
|
||||||
return this.dataReady!.then(() => this._data![index - this._offsetFromDataSource]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public positionWindow(offset: number, length: number): void {
|
|
||||||
this._offsetFromDataSource = offset;
|
|
||||||
this._length = length;
|
|
||||||
this._data = undefined;
|
|
||||||
|
|
||||||
if (this.dataReady) {
|
|
||||||
this.dataReady.cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.dataReady = createCancelablePromise(token => {
|
|
||||||
return this.loadFunction(offset, length).then(data => {
|
|
||||||
if (!token.isCancellationRequested) {
|
|
||||||
this._data = data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class VirtualizedWindow<T> {
|
|
||||||
private _bufferWindowBefore: DataWindow<T>;
|
|
||||||
private _window: DataWindow<T>;
|
|
||||||
private _bufferWindowAfter: DataWindow<T>;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private readonly windowSize: number,
|
|
||||||
private _length: number,
|
|
||||||
loadFn: (offset: number, count: number) => Promise<T[]>
|
|
||||||
) {
|
|
||||||
|
|
||||||
this._bufferWindowBefore = new DataWindow(loadFn);
|
|
||||||
this._window = new DataWindow(loadFn);
|
|
||||||
this._bufferWindowAfter = new DataWindow(loadFn);
|
|
||||||
}
|
|
||||||
|
|
||||||
dispose() {
|
|
||||||
this._bufferWindowAfter.dispose();
|
|
||||||
this._bufferWindowBefore.dispose();
|
|
||||||
this._window.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
get length(): number {
|
|
||||||
return this._length;
|
|
||||||
}
|
|
||||||
|
|
||||||
set length(length: number) {
|
|
||||||
if (this.length !== length) {
|
|
||||||
const oldLength = this.length;
|
|
||||||
this._length = length;
|
|
||||||
if (this._window.length !== this.windowSize) {
|
|
||||||
this.resetWindowsAroundIndex(oldLength);
|
|
||||||
} else if (this._bufferWindowAfter.length !== this.windowSize) {
|
|
||||||
this._bufferWindowAfter.positionWindow(this._bufferWindowAfter.start, Math.min(this._bufferWindowAfter.start + this.windowSize, this.length));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public getIndex(index: number): Promise<T> {
|
|
||||||
|
|
||||||
if (index < this._bufferWindowBefore.start || index >= this._bufferWindowAfter.end) {
|
|
||||||
this.resetWindowsAroundIndex(index);
|
|
||||||
}
|
|
||||||
// scrolling up
|
|
||||||
else if (this._bufferWindowBefore.contains(index)) {
|
|
||||||
const beforeWindow = this._bufferWindowAfter;
|
|
||||||
this._bufferWindowAfter = this._window;
|
|
||||||
this._window = this._bufferWindowBefore;
|
|
||||||
this._bufferWindowBefore = beforeWindow;
|
|
||||||
// ensure we aren't buffer invalid data
|
|
||||||
const beforeStart = Math.max(0, this._window.start - this.windowSize);
|
|
||||||
// ensure if we got hinder in our start index that we update out length to not overlap
|
|
||||||
const beforeLength = this._window.start - beforeStart;
|
|
||||||
this._bufferWindowBefore.positionWindow(beforeStart, beforeLength);
|
|
||||||
}
|
|
||||||
// scroll down
|
|
||||||
else if (this._bufferWindowAfter.contains(index)) {
|
|
||||||
const afterWindow = this._bufferWindowBefore;
|
|
||||||
this._bufferWindowBefore = this._window;
|
|
||||||
this._window = this._bufferWindowAfter;
|
|
||||||
this._bufferWindowAfter = afterWindow;
|
|
||||||
// ensure we aren't buffer invalid data
|
|
||||||
const afterStart = this._window.end;
|
|
||||||
// ensure if we got hinder in our start index that we update out length to not overlap
|
|
||||||
const afterLength = afterStart + this.windowSize > this.length ? this.length - afterStart : this.windowSize;
|
|
||||||
this._bufferWindowAfter.positionWindow(afterStart, afterLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
// at this point we know the current window will have the index
|
|
||||||
return this._window.getItem(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
private resetWindowsAroundIndex(index: number): void {
|
|
||||||
|
|
||||||
let bufferWindowBeforeStart = Math.max(0, index - this.windowSize * 1.5);
|
|
||||||
let bufferWindowBeforeEnd = Math.max(0, index - this.windowSize / 2);
|
|
||||||
this._bufferWindowBefore.positionWindow(bufferWindowBeforeStart, bufferWindowBeforeEnd - bufferWindowBeforeStart);
|
|
||||||
|
|
||||||
let mainWindowStart = bufferWindowBeforeEnd;
|
|
||||||
let mainWindowEnd = Math.min(mainWindowStart + this.windowSize, this.length);
|
|
||||||
this._window.positionWindow(mainWindowStart, mainWindowEnd - mainWindowStart);
|
|
||||||
|
|
||||||
let bufferWindowAfterStart = mainWindowEnd;
|
|
||||||
let bufferWindowAfterEnd = Math.min(bufferWindowAfterStart + this.windowSize, this.length);
|
|
||||||
this._bufferWindowAfter.positionWindow(bufferWindowAfterStart, bufferWindowAfterEnd - bufferWindowAfterStart);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
export function defaultFormatter<T>(valueProperty: keyof T): Slick.Formatter<T> {
|
|
||||||
return (row: number, cell: number, value: any, columnDef: Slick.Column<T>, dataContext: Slick.SlickData): string => {
|
|
||||||
return value[valueProperty];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
export function isNumber(text: string): boolean {
|
|
||||||
return !isNaN(parseInt(text)) && !isNaN(parseFloat(text));
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import * as _ from 'sql/base/parts/tree/browser/tree';
|
|
||||||
|
|
||||||
export function isEqualOrParent(tree: _.ITree, element: any, candidateParent: any): boolean {
|
|
||||||
const nav = tree.getNavigator(element);
|
|
||||||
|
|
||||||
do {
|
|
||||||
if (element === candidateParent) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} while (element = nav.parent()); // eslint-disable-line no-cond-assign
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
@@ -11,7 +11,7 @@ import * as TreeDefaults from 'sql/base/parts/tree/browser/treeDefaults';
|
|||||||
import { Event, Emitter } from 'vs/base/common/event';
|
import { Event, Emitter } from 'vs/base/common/event';
|
||||||
import { timeout } from 'vs/base/common/async';
|
import { timeout } from 'vs/base/common/async';
|
||||||
|
|
||||||
export class FakeRenderer {
|
class FakeRenderer {
|
||||||
|
|
||||||
public getHeight(tree: _.ITree, element: any): number {
|
public getHeight(tree: _.ITree, element: any): number {
|
||||||
return 20;
|
return 20;
|
||||||
|
|||||||
@@ -1,205 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
|
||||||
import { Table, DefaultStyleController, ITableOptions } from 'sql/base/browser/ui/table/highPerf/tableWidget';
|
|
||||||
import { RawContextKey, IContextKey, ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
|
||||||
import { DisposableStore, IDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
|
||||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
|
||||||
import { createStyleSheet } from 'vs/base/browser/dom';
|
|
||||||
import { attachHighPerfTableStyler as attachTableStyler, defaultHighPerfTableStyles, IHighPerfTableStyleOverrides } from 'sql/platform/theme/common/styler';
|
|
||||||
import { InputFocusedContextKey } from 'vs/platform/contextkey/common/contextkeys';
|
|
||||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
|
||||||
import { ITableDataSource, ITableColumn } from 'sql/base/browser/ui/table/highPerf/table';
|
|
||||||
import { computeStyles } from 'vs/platform/theme/common/styler';
|
|
||||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
|
||||||
|
|
||||||
export const ITableService = createDecorator<ITableService>('tableService');
|
|
||||||
|
|
||||||
export type TableWidget = Table<any>;
|
|
||||||
|
|
||||||
export interface ITableService {
|
|
||||||
|
|
||||||
_serviceBrand: undefined;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the currently focused table widget if any.
|
|
||||||
*/
|
|
||||||
readonly lastFocusedTable: TableWidget | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IRegisteredTable {
|
|
||||||
widget: TableWidget;
|
|
||||||
extraContextKeys?: (IContextKey<boolean>)[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export class TableService implements ITableService {
|
|
||||||
|
|
||||||
_serviceBrand: undefined;
|
|
||||||
|
|
||||||
private disposables = new DisposableStore();
|
|
||||||
private tables: IRegisteredTable[] = [];
|
|
||||||
private _lastFocusedWidget: TableWidget | undefined = undefined;
|
|
||||||
|
|
||||||
get lastFocusedTable(): TableWidget | undefined {
|
|
||||||
return this._lastFocusedWidget;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(@IThemeService themeService: IThemeService) {
|
|
||||||
// create a shared default tree style sheet for performance reasons
|
|
||||||
const styleController = new DefaultStyleController(createStyleSheet(), '');
|
|
||||||
this.disposables.add(attachTableStyler(styleController, themeService));
|
|
||||||
}
|
|
||||||
|
|
||||||
register(widget: TableWidget, extraContextKeys?: (IContextKey<boolean>)[]): IDisposable {
|
|
||||||
if (this.tables.some(l => l.widget === widget)) {
|
|
||||||
throw new Error('Cannot register the same widget multiple times');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep in our tables table
|
|
||||||
const registeredTable: IRegisteredTable = { widget, extraContextKeys };
|
|
||||||
this.tables.push(registeredTable);
|
|
||||||
|
|
||||||
// Check for currently being focused
|
|
||||||
if (widget.getHTMLElement() === document.activeElement) {
|
|
||||||
this._lastFocusedWidget = widget;
|
|
||||||
}
|
|
||||||
|
|
||||||
return combinedDisposable(
|
|
||||||
widget.onDidFocus(() => this._lastFocusedWidget = widget),
|
|
||||||
toDisposable(() => this.tables.splice(this.tables.indexOf(registeredTable), 1)),
|
|
||||||
widget.onDidDispose(() => {
|
|
||||||
this.tables = this.tables.filter(l => l !== registeredTable);
|
|
||||||
if (this._lastFocusedWidget === widget) {
|
|
||||||
this._lastFocusedWidget = undefined;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
dispose(): void {
|
|
||||||
this.disposables.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const RawWorkbenchTableFocusContextKey = new RawContextKey<boolean>('tableFocus', true);
|
|
||||||
export const WorkbenchTableFocusContextKey = ContextKeyExpr.and(RawWorkbenchTableFocusContextKey, ContextKeyExpr.not(InputFocusedContextKey));
|
|
||||||
export const WorkbenchTableHasSelectionOrFocus = new RawContextKey<boolean>('tableHasSelectionOrFocus', false);
|
|
||||||
export const WorkbenchTableDoubleSelection = new RawContextKey<boolean>('tableDoubleSelection', false);
|
|
||||||
export const WorkbenchTableMultiSelection = new RawContextKey<boolean>('tableMultiSelection', false);
|
|
||||||
export const WorkbenchTableSupportsKeyboardNavigation = new RawContextKey<boolean>('tableSupportsKeyboardNavigation', true);
|
|
||||||
export const WorkbenchTableAutomaticKeyboardNavigationKey = 'tableAutomaticKeyboardNavigation';
|
|
||||||
export const WorkbenchTableAutomaticKeyboardNavigation = new RawContextKey<boolean>(WorkbenchTableAutomaticKeyboardNavigationKey, true);
|
|
||||||
export let didBindWorkbenchTableAutomaticKeyboardNavigation = false;
|
|
||||||
|
|
||||||
function createScopedContextKeyService(contextKeyService: IContextKeyService, widget: TableWidget): IContextKeyService {
|
|
||||||
const result = contextKeyService.createScoped(widget.getHTMLElement());
|
|
||||||
RawWorkbenchTableFocusContextKey.bindTo(result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const multiSelectModifierSettingKey = 'workbench.table.multiSelectModifier';
|
|
||||||
export const openModeSettingKey = 'workbench.table.openMode';
|
|
||||||
export const horizontalScrollingKey = 'workbench.table.horizontalScrolling';
|
|
||||||
export const keyboardNavigationSettingKey = 'workbench.table.keyboardNavigation';
|
|
||||||
export const automaticKeyboardNavigationSettingKey = 'workbench.table.automaticKeyboardNavigation';
|
|
||||||
|
|
||||||
function useAltAsMultipleSelectionModifier(configurationService: IConfigurationService): boolean {
|
|
||||||
return configurationService.getValue(multiSelectModifierSettingKey) === 'alt';
|
|
||||||
}
|
|
||||||
|
|
||||||
function toWorkbenchTableOptions<T>(options: ITableOptions<T>): [ITableOptions<T>, IDisposable] {
|
|
||||||
const disposables = new DisposableStore();
|
|
||||||
const result = { ...options };
|
|
||||||
|
|
||||||
return [result, disposables];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IWorkbenchTableOptions<T> extends ITableOptions<T> {
|
|
||||||
readonly overrideStyles?: IHighPerfTableStyleOverrides;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class WorkbenchTable<T> extends Table<T> {
|
|
||||||
|
|
||||||
readonly contextKeyService: IContextKeyService;
|
|
||||||
private readonly configurationService: IConfigurationService;
|
|
||||||
|
|
||||||
private tableHasSelectionOrFocus: IContextKey<boolean>;
|
|
||||||
private tableDoubleSelection: IContextKey<boolean>;
|
|
||||||
private tableMultiSelection: IContextKey<boolean>;
|
|
||||||
|
|
||||||
private _useAltAsMultipleSelectionModifier: boolean;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
user: string,
|
|
||||||
container: HTMLElement,
|
|
||||||
columns: ITableColumn<T, any>[],
|
|
||||||
dataSource: ITableDataSource<T>,
|
|
||||||
options: IWorkbenchTableOptions<T>,
|
|
||||||
@IContextKeyService contextKeyService: IContextKeyService,
|
|
||||||
@ITableService tableService: ITableService,
|
|
||||||
@IThemeService themeService: IThemeService,
|
|
||||||
@IConfigurationService configurationService: IConfigurationService
|
|
||||||
) {
|
|
||||||
const [workbenchTableOptions, workbenchTableOptionsDisposable] = toWorkbenchTableOptions(options);
|
|
||||||
|
|
||||||
super(user, container, columns, dataSource,
|
|
||||||
{
|
|
||||||
keyboardSupport: false,
|
|
||||||
...computeStyles(themeService.getColorTheme(), defaultHighPerfTableStyles),
|
|
||||||
...workbenchTableOptions
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
this.disposables.add(workbenchTableOptionsDisposable);
|
|
||||||
|
|
||||||
this.contextKeyService = createScopedContextKeyService(contextKeyService, this);
|
|
||||||
this.configurationService = configurationService;
|
|
||||||
|
|
||||||
this.tableHasSelectionOrFocus = WorkbenchTableHasSelectionOrFocus.bindTo(this.contextKeyService);
|
|
||||||
this.tableDoubleSelection = WorkbenchTableDoubleSelection.bindTo(this.contextKeyService);
|
|
||||||
this.tableMultiSelection = WorkbenchTableMultiSelection.bindTo(this.contextKeyService);
|
|
||||||
|
|
||||||
this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService);
|
|
||||||
|
|
||||||
this.disposables.add(this.contextKeyService);
|
|
||||||
this.disposables.add((tableService as TableService).register(this));
|
|
||||||
|
|
||||||
if (options.overrideStyles) {
|
|
||||||
this.disposables.add(attachTableStyler(this, themeService, options.overrideStyles));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.disposables.add(this.onSelectionChange(() => {
|
|
||||||
const selection = this.getSelection();
|
|
||||||
const focus = this.getFocus();
|
|
||||||
|
|
||||||
this.tableHasSelectionOrFocus.set(selection.length > 0 || focus.length > 0);
|
|
||||||
this.tableMultiSelection.set(selection.length > 1);
|
|
||||||
this.tableDoubleSelection.set(selection.length === 2);
|
|
||||||
}));
|
|
||||||
this.disposables.add(this.onFocusChange(() => {
|
|
||||||
const selection = this.getSelection();
|
|
||||||
const focus = this.getFocus();
|
|
||||||
|
|
||||||
this.tableHasSelectionOrFocus.set(selection.length > 0 || focus.length > 0);
|
|
||||||
}));
|
|
||||||
|
|
||||||
this.registerListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
private registerListeners(): void {
|
|
||||||
this.disposables.add(this.configurationService.onDidChangeConfiguration(e => {
|
|
||||||
if (e.affectsConfiguration(multiSelectModifierSettingKey)) {
|
|
||||||
this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(this.configurationService);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
get useAltAsMultipleSelectionModifier(): boolean {
|
|
||||||
return this._useAltAsMultipleSelectionModifier;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
registerSingleton(ITableService, TableService, true);
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import 'vs/css!./media/calloutDialog';
|
|
||||||
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
|
||||||
import { IDialogProperties, Modal, DialogWidth, DialogPosition } from 'sql/workbench/browser/modal/modal';
|
|
||||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
|
||||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
|
||||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
|
||||||
import { ILogService } from 'vs/platform/log/common/log';
|
|
||||||
import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
|
|
||||||
import { ILayoutService } from 'vs/platform/layout/browser/layoutService';
|
|
||||||
import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfiguration';
|
|
||||||
|
|
||||||
export abstract class CalloutDialog<T> extends Modal {
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
title: string,
|
|
||||||
width: DialogWidth,
|
|
||||||
dialogProperties: IDialogProperties,
|
|
||||||
dialogPosition: DialogPosition,
|
|
||||||
@IThemeService themeService: IThemeService,
|
|
||||||
@ILayoutService layoutService: ILayoutService,
|
|
||||||
@IAdsTelemetryService telemetryService: IAdsTelemetryService,
|
|
||||||
@IContextKeyService contextKeyService: IContextKeyService,
|
|
||||||
@IClipboardService clipboardService: IClipboardService,
|
|
||||||
@ILogService logService: ILogService,
|
|
||||||
@ITextResourcePropertiesService textResourcePropertiesService: ITextResourcePropertiesService
|
|
||||||
) {
|
|
||||||
super(
|
|
||||||
title,
|
|
||||||
TelemetryKeys.ModalDialogName.CalloutDialog,
|
|
||||||
telemetryService,
|
|
||||||
layoutService,
|
|
||||||
clipboardService,
|
|
||||||
themeService,
|
|
||||||
logService,
|
|
||||||
textResourcePropertiesService,
|
|
||||||
contextKeyService,
|
|
||||||
{
|
|
||||||
dialogStyle: 'callout',
|
|
||||||
dialogPosition: dialogPosition,
|
|
||||||
dialogProperties: dialogProperties,
|
|
||||||
width: width
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract open(): Promise<T>;
|
|
||||||
|
|
||||||
public cancel(): void {
|
|
||||||
this.hide('cancel');
|
|
||||||
this.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected layout(height?: number): void {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,144 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import { ProviderProperties } from 'sql/workbench/contrib/dashboard/browser/dashboardRegistry';
|
|
||||||
import * as nls from 'vs/nls';
|
|
||||||
import { mssqlProviderName } from 'sql/platform/connection/common/constants';
|
|
||||||
|
|
||||||
const azureEditionDisplayName = nls.localize('azureEdition', "Edition");
|
|
||||||
const azureType = nls.localize('azureType', "Type");
|
|
||||||
|
|
||||||
export const properties: Array<ProviderProperties> = [
|
|
||||||
{
|
|
||||||
provider: mssqlProviderName,
|
|
||||||
flavors: [
|
|
||||||
{
|
|
||||||
flavor: 'on_prem',
|
|
||||||
conditions: [
|
|
||||||
{
|
|
||||||
field: 'isCloud',
|
|
||||||
operator: '!=',
|
|
||||||
value: true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
databaseProperties: [
|
|
||||||
{
|
|
||||||
displayName: nls.localize('recoveryModel', "Recovery Model"),
|
|
||||||
value: 'recoveryModel'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: nls.localize('lastDatabaseBackup', "Last Database Backup"),
|
|
||||||
value: 'lastBackupDate',
|
|
||||||
ignore: [
|
|
||||||
'1/1/0001 12:00:00 AM'
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: nls.localize('lastLogBackup', "Last Log Backup"),
|
|
||||||
value: 'lastLogBackupDate',
|
|
||||||
ignore: [
|
|
||||||
'1/1/0001 12:00:00 AM'
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: nls.localize('compatibilityLevel', "Compatibility Level"),
|
|
||||||
value: 'compatibilityLevel'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: nls.localize('owner', "Owner"),
|
|
||||||
value: 'owner'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
serverProperties: [
|
|
||||||
{
|
|
||||||
displayName: nls.localize('version', "Version"),
|
|
||||||
value: 'serverVersion'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: nls.localize('edition', "Edition"),
|
|
||||||
value: 'serverEdition'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: nls.localize('computerName', "Computer Name"),
|
|
||||||
value: 'machineName'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: nls.localize('osVersion', "OS Version"),
|
|
||||||
value: 'osVersion'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
flavor: 'cloud',
|
|
||||||
conditions: [
|
|
||||||
{
|
|
||||||
field: 'isCloud',
|
|
||||||
operator: '==',
|
|
||||||
value: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'engineEditionId',
|
|
||||||
operator: '!=',
|
|
||||||
value: '11'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
databaseProperties: [
|
|
||||||
{
|
|
||||||
displayName: azureEditionDisplayName,
|
|
||||||
value: 'azureEdition'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: nls.localize('serviceLevelObjective', "Pricing Tier"),
|
|
||||||
value: 'serviceLevelObjective'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: nls.localize('compatibilityLevel', "Compatibility Level"),
|
|
||||||
value: 'compatibilityLevel'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: nls.localize('owner', "Owner"),
|
|
||||||
value: 'owner'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
serverProperties: [
|
|
||||||
{
|
|
||||||
displayName: nls.localize('version', "Version"),
|
|
||||||
value: 'serverVersion'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: azureType,
|
|
||||||
value: 'serverEdition'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
flavor: 'on_demand',
|
|
||||||
conditions: [
|
|
||||||
{
|
|
||||||
field: 'engineEditionId',
|
|
||||||
operator: '==',
|
|
||||||
value: '11'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
databaseProperties: [
|
|
||||||
{
|
|
||||||
displayName: nls.localize('compatibilityLevel', "Compatibility Level"),
|
|
||||||
value: 'compatibilityLevel'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
serverProperties: [
|
|
||||||
{
|
|
||||||
displayName: nls.localize('version', "Version"),
|
|
||||||
value: 'serverVersion'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: azureType,
|
|
||||||
value: 'serverEdition'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
];
|
|
||||||
@@ -1,141 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
import { Button } from 'sql/base/browser/ui/button/button';
|
|
||||||
import { IClipboardService } from 'sql/platform/clipboard/common/clipboardService';
|
|
||||||
import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
|
|
||||||
import { Modal } from 'sql/workbench/browser/modal/modal';
|
|
||||||
import { attachModalDialogStyler } from 'sql/workbench/common/styler';
|
|
||||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
|
||||||
import { ILayoutService } from 'vs/platform/layout/browser/layoutService';
|
|
||||||
import { ILogService } from 'vs/platform/log/common/log';
|
|
||||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
|
||||||
import * as DOM from 'vs/base/browser/dom';
|
|
||||||
import { attachInputBoxStyler } from 'sql/platform/theme/common/styler';
|
|
||||||
import { localize } from 'vs/nls';
|
|
||||||
import { IInputOptions, MessageType } from 'vs/base/browser/ui/inputbox/inputBox';
|
|
||||||
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox';
|
|
||||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
|
||||||
import { INotebookView } from 'sql/workbench/services/notebook/browser/notebookViews/notebookViews';
|
|
||||||
import { attachButtonStyler } from 'vs/platform/theme/common/styler';
|
|
||||||
import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfiguration';
|
|
||||||
|
|
||||||
export class ViewOptionsModal extends Modal {
|
|
||||||
private _submitButton: Button;
|
|
||||||
private _cancelButton: Button;
|
|
||||||
private _viewNameInput: InputBox;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private _view: INotebookView,
|
|
||||||
@ILogService logService: ILogService,
|
|
||||||
@IThemeService themeService: IThemeService,
|
|
||||||
@ILayoutService layoutService: ILayoutService,
|
|
||||||
@IClipboardService clipboardService: IClipboardService,
|
|
||||||
@IContextKeyService contextKeyService: IContextKeyService,
|
|
||||||
@IAdsTelemetryService telemetryService: IAdsTelemetryService,
|
|
||||||
@IContextViewService private _contextViewService: IContextViewService,
|
|
||||||
@ITextResourcePropertiesService textResourcePropertiesService: ITextResourcePropertiesService,
|
|
||||||
) {
|
|
||||||
super(
|
|
||||||
localize("viewOptionsModal.title", "Configure View"),
|
|
||||||
'ViewOptionsModal',
|
|
||||||
telemetryService,
|
|
||||||
layoutService,
|
|
||||||
clipboardService,
|
|
||||||
themeService,
|
|
||||||
logService,
|
|
||||||
textResourcePropertiesService,
|
|
||||||
contextKeyService,
|
|
||||||
{ hasErrors: true, hasSpinner: true }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected renderBody(container: HTMLElement): void {
|
|
||||||
const formWrapper = DOM.$<HTMLDivElement>('div#notebookviews-options-form');
|
|
||||||
formWrapper.style.padding = '10px';
|
|
||||||
|
|
||||||
DOM.append(container, formWrapper);
|
|
||||||
|
|
||||||
this._viewNameInput = this.createNameInput(formWrapper);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected layout(height: number): void {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected createNameInput(container: HTMLElement): InputBox {
|
|
||||||
return this.createInputBoxHelper(container, localize('viewOptionsModal.name', "View Name"), this._view.name, {
|
|
||||||
validationOptions: {
|
|
||||||
validation: (value: string) => {
|
|
||||||
if (!value) {
|
|
||||||
return ({ type: MessageType.ERROR, content: localize('viewOptionsModal.missingRequireField', "This field is required.") });
|
|
||||||
}
|
|
||||||
if (this._view.name !== value && !this._view.nameAvailable(value)) {
|
|
||||||
return ({ type: MessageType.ERROR, content: localize('viewOptionsModal.nameTaken', "This view name has already been taken.") });
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ariaLabel: localize('viewOptionsModal.name', "View Name")
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private createInputBoxHelper(container: HTMLElement, label: string, defaultValue: string = '', options?: IInputOptions): InputBox {
|
|
||||||
const inputContainer = DOM.append(container, DOM.$('.dialog-input-section'));
|
|
||||||
DOM.append(inputContainer, DOM.$('.dialog-label')).innerText = label;
|
|
||||||
const input = new InputBox(DOM.append(inputContainer, DOM.$('.dialog-input')), this._contextViewService, options);
|
|
||||||
input.value = defaultValue;
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override render() {
|
|
||||||
super.render();
|
|
||||||
|
|
||||||
this._submitButton = this.addFooterButton(localize('save', "Save"), () => this.onSubmitHandler());
|
|
||||||
this._cancelButton = this.addFooterButton(localize('cancel', "Cancel"), () => this.onCancelHandler());
|
|
||||||
|
|
||||||
this._register(attachInputBoxStyler(this._viewNameInput!, this._themeService));
|
|
||||||
this._register(attachButtonStyler(this._submitButton, this._themeService));
|
|
||||||
this._register(attachButtonStyler(this._cancelButton, this._themeService));
|
|
||||||
|
|
||||||
this._register(this._viewNameInput.onDidChange(v => this.validate()));
|
|
||||||
|
|
||||||
attachModalDialogStyler(this, this._themeService);
|
|
||||||
this.validate();
|
|
||||||
}
|
|
||||||
|
|
||||||
private validate() {
|
|
||||||
let valid = true;
|
|
||||||
|
|
||||||
if (!this._viewNameInput.validate()) {
|
|
||||||
valid = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._submitButton.enabled = valid;
|
|
||||||
}
|
|
||||||
|
|
||||||
private onSubmitHandler() {
|
|
||||||
this._view.name = this._viewNameInput.value;
|
|
||||||
this._view.save();
|
|
||||||
|
|
||||||
this.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private onCancelHandler() {
|
|
||||||
this.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
public close(): void {
|
|
||||||
return this.hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
public open(): void {
|
|
||||||
this.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override dispose(): void {
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -26,7 +26,7 @@ import { createandLoadNotebookModel } from 'sql/workbench/contrib/notebook/test/
|
|||||||
import { TestConfigurationService } from 'sql/platform/connection/test/common/testConfigurationService';
|
import { TestConfigurationService } from 'sql/platform/connection/test/common/testConfigurationService';
|
||||||
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||||
|
|
||||||
export class TestSerializationProvider implements azdata.SerializationProvider {
|
class TestSerializationProvider implements azdata.SerializationProvider {
|
||||||
providerId: string;
|
providerId: string;
|
||||||
constructor(providerId: string = 'providerId') { }
|
constructor(providerId: string = 'providerId') { }
|
||||||
|
|
||||||
|
|||||||
@@ -1,123 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import { nb } from 'azdata';
|
|
||||||
import { ICellModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
|
|
||||||
import { INotebookView } from 'sql/workbench/services/notebook/browser/notebookViews/notebookViews';
|
|
||||||
import { CellTypes } from 'sql/workbench/services/notebook/common/contracts';
|
|
||||||
|
|
||||||
class VisInfo<T> {
|
|
||||||
public width: number;
|
|
||||||
public height: number;
|
|
||||||
public orderRank: number;
|
|
||||||
public display: boolean;
|
|
||||||
public cell: T;
|
|
||||||
}
|
|
||||||
|
|
||||||
class DisplayCell<T> {
|
|
||||||
constructor(private _item: T) { }
|
|
||||||
|
|
||||||
get item(): T {
|
|
||||||
return this._item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class DisplayGroup<T> {
|
|
||||||
public width: number;
|
|
||||||
public height: number;
|
|
||||||
public orderRank: number;
|
|
||||||
public display: boolean;
|
|
||||||
private _displayCells: DisplayCell<T>[] = [];
|
|
||||||
private _visInfos: VisInfo<T>[] = [];
|
|
||||||
|
|
||||||
constructor() { }
|
|
||||||
|
|
||||||
addCell(cell: T, initialView: INotebookView) {
|
|
||||||
const dCell = new DisplayCell<T>(cell);
|
|
||||||
this._displayCells.push(dCell);
|
|
||||||
this._visInfos.push(this.evaluateCell(cell, initialView));
|
|
||||||
}
|
|
||||||
|
|
||||||
get visInfos(): VisInfo<T>[] {
|
|
||||||
return this._visInfos;
|
|
||||||
}
|
|
||||||
|
|
||||||
get displayCells(): DisplayCell<T>[] {
|
|
||||||
return this._displayCells;
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract evaluateCell(cell: T, view: INotebookView): VisInfo<T>;
|
|
||||||
}
|
|
||||||
|
|
||||||
class CellDisplayGroup extends DisplayGroup<ICellModel> {
|
|
||||||
evaluateCell(cell: ICellModel, view: INotebookView): VisInfo<ICellModel> {
|
|
||||||
let meta = view.getCellMetadata(cell);
|
|
||||||
let visInfo = new VisInfo<ICellModel>();
|
|
||||||
visInfo.cell = cell;
|
|
||||||
|
|
||||||
if (cell.cellType !== CellTypes.Code && !this.isHeader(cell)) {
|
|
||||||
visInfo.display = false;
|
|
||||||
return visInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cell.cellType === CellTypes.Code && (!cell.outputs || !cell.outputs.length)) {
|
|
||||||
visInfo.display = false;
|
|
||||||
return visInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
//For headers
|
|
||||||
if (this.isHeader(cell)) {
|
|
||||||
visInfo.height = 1;
|
|
||||||
}
|
|
||||||
//For graphs
|
|
||||||
if (this.hasGraph(cell)) {
|
|
||||||
visInfo.width = 6;
|
|
||||||
visInfo.height = 12;
|
|
||||||
}
|
|
||||||
//For tables
|
|
||||||
else if (this.hasTable(cell)) {
|
|
||||||
visInfo.height = Math.min(meta?.height, 6);
|
|
||||||
} else {
|
|
||||||
visInfo.height = Math.min(meta?.height, 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
visInfo.display = true;
|
|
||||||
return visInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
isHeader(cell: ICellModel): boolean {
|
|
||||||
return cell.cellType === 'markdown' && cell.source.length === 1 && cell.source[0].startsWith('#');
|
|
||||||
}
|
|
||||||
|
|
||||||
hasGraph(cell: ICellModel): boolean {
|
|
||||||
return !!cell.outputs.find((o: nb.ICellOutput) => o?.output_type === 'display_data' && (o as nb.IDisplayResult)?.data.hasOwnProperty('application/vnd.plotly.v1+json'));
|
|
||||||
}
|
|
||||||
|
|
||||||
hasTable(cell: ICellModel): boolean {
|
|
||||||
return !!cell.outputs.find((o: nb.ICellOutput) => o?.output_type === 'display_data' && (o as nb.IDisplayResult)?.data.hasOwnProperty('application/vnd.dataresource+json'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function generateLayout(initialView: INotebookView): void {
|
|
||||||
let displayGroup: CellDisplayGroup = new CellDisplayGroup();
|
|
||||||
|
|
||||||
const cells = initialView.cells;
|
|
||||||
|
|
||||||
cells.forEach((cell, idx) => {
|
|
||||||
displayGroup.addCell(cell, initialView);
|
|
||||||
});
|
|
||||||
|
|
||||||
displayGroup.visInfos.forEach((v) => {
|
|
||||||
if (!v.display) {
|
|
||||||
initialView.hideCell(v.cell);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (v.width || v.height) {
|
|
||||||
initialView.resizeCell(v.cell, v.width, v.height);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
initialView.compactCells();
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user