mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -05:00
Add connection profile persistence to MIAA dashboard (#11061)
* wip * fixes * fix pg model * more updates * Add resourceType check
This commit is contained in:
@@ -29,7 +29,7 @@ export const newSupportRequest = localize('arc.newSupportRequest', "New support
|
|||||||
export const diagnoseAndSolveProblems = localize('arc.diagnoseAndSolveProblems', "Diagnose and solve problems");
|
export const diagnoseAndSolveProblems = localize('arc.diagnoseAndSolveProblems', "Diagnose and solve problems");
|
||||||
export const supportAndTroubleshooting = localize('arc.supportAndTroubleshooting', "Support + troubleshooting");
|
export const supportAndTroubleshooting = localize('arc.supportAndTroubleshooting', "Support + troubleshooting");
|
||||||
|
|
||||||
export const createNew = localize('arc.createNew', "Create New");
|
export const newInstance = localize('arc.createNew', "New Instance");
|
||||||
export const deleteText = localize('arc.delete', "Delete");
|
export const deleteText = localize('arc.delete', "Delete");
|
||||||
export const resetPassword = localize('arc.resetPassword', "Reset Password");
|
export const resetPassword = localize('arc.resetPassword', "Reset Password");
|
||||||
export const openInAzurePortal = localize('arc.openInAzurePortal', "Open in Azure Portal");
|
export const openInAzurePortal = localize('arc.openInAzurePortal', "Open in Azure Portal");
|
||||||
@@ -116,10 +116,9 @@ export function resourceDeleted(name: string): string { return localize('arc.res
|
|||||||
export function couldNotFindAzureResource(name: string): string { return localize('arc.couldNotFindAzureResource', "Could not find Azure resource for {0}", name); }
|
export function couldNotFindAzureResource(name: string): string { return localize('arc.couldNotFindAzureResource', "Could not find Azure resource for {0}", name); }
|
||||||
export function copiedToClipboard(name: string): string { return localize('arc.copiedToClipboard', "{0} copied to clipboard", name); }
|
export function copiedToClipboard(name: string): string { return localize('arc.copiedToClipboard', "{0} copied to clipboard", name); }
|
||||||
export function clickTheTroubleshootButton(resourceType: string): string { return localize('arc.clickTheTroubleshootButton', "Click the troubleshoot button to open the Azure Arc {0} troubleshooting notebook.", resourceType); }
|
export function clickTheTroubleshootButton(resourceType: string): string { return localize('arc.clickTheTroubleshootButton', "Click the troubleshoot button to open the Azure Arc {0} troubleshooting notebook.", resourceType); }
|
||||||
export function numVCores(vCores: string): string {
|
export function numVCores(vCores: string | undefined): string {
|
||||||
const numCores = +vCores;
|
if (vCores && +vCores > 0) {
|
||||||
if (numCores && numCores > 0) {
|
return localize('arc.numVCores', "{0} vCores", vCores);
|
||||||
return localize('arc.numVCores', "{0} vCores", numCores);
|
|
||||||
} else {
|
} else {
|
||||||
return '-';
|
return '-';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,15 @@ import { AzureArcTreeDataProvider } from '../ui/tree/azureArcTreeDataProvider';
|
|||||||
export type ControllerInfo = {
|
export type ControllerInfo = {
|
||||||
url: string,
|
url: string,
|
||||||
username: string,
|
username: string,
|
||||||
rememberPassword: boolean
|
rememberPassword: boolean,
|
||||||
|
resources: ResourceInfo[]
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ResourceInfo = {
|
||||||
|
namespace: string,
|
||||||
|
name: string,
|
||||||
|
resourceType: ResourceType | string,
|
||||||
|
connectionId?: string
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface Registration extends RegistrationResponse {
|
export interface Registration extends RegistrationResponse {
|
||||||
|
|||||||
@@ -8,15 +8,22 @@ import * as vscode from 'vscode';
|
|||||||
import { SqlInstanceRouterApi } from '../controller/generated/v1/api/sqlInstanceRouterApi';
|
import { SqlInstanceRouterApi } from '../controller/generated/v1/api/sqlInstanceRouterApi';
|
||||||
import { HybridSqlNsNameGetResponse } from '../controller/generated/v1/model/hybridSqlNsNameGetResponse';
|
import { HybridSqlNsNameGetResponse } from '../controller/generated/v1/model/hybridSqlNsNameGetResponse';
|
||||||
import { Authentication } from '../controller/generated/v1/api';
|
import { Authentication } from '../controller/generated/v1/api';
|
||||||
|
import { ResourceModel } from './resourceModel';
|
||||||
|
import { ResourceInfo, Registration } from './controllerModel';
|
||||||
|
import { AzureArcTreeDataProvider } from '../ui/tree/azureArcTreeDataProvider';
|
||||||
|
import { Deferred } from '../common/promise';
|
||||||
|
|
||||||
export type DatabaseModel = { name: string, status: string };
|
export type DatabaseModel = { name: string, status: string };
|
||||||
|
|
||||||
export class MiaaModel {
|
export class MiaaModel extends ResourceModel {
|
||||||
|
|
||||||
private _sqlInstanceRouter: SqlInstanceRouterApi;
|
private _sqlInstanceRouter: SqlInstanceRouterApi;
|
||||||
private _status: HybridSqlNsNameGetResponse | undefined;
|
private _status: HybridSqlNsNameGetResponse | undefined;
|
||||||
private _databases: DatabaseModel[] = [];
|
private _databases: DatabaseModel[] = [];
|
||||||
|
// The saved connection information
|
||||||
private _connectionProfile: azdata.IConnectionProfile | undefined = undefined;
|
private _connectionProfile: azdata.IConnectionProfile | undefined = undefined;
|
||||||
|
// The ID of the active connection used to query the server
|
||||||
|
private _activeConnectionId: string | undefined = undefined;
|
||||||
|
|
||||||
private readonly _onPasswordUpdated = new vscode.EventEmitter<string>();
|
private readonly _onPasswordUpdated = new vscode.EventEmitter<string>();
|
||||||
private readonly _onStatusUpdated = new vscode.EventEmitter<HybridSqlNsNameGetResponse>();
|
private readonly _onStatusUpdated = new vscode.EventEmitter<HybridSqlNsNameGetResponse>();
|
||||||
@@ -26,23 +33,12 @@ export class MiaaModel {
|
|||||||
public onDatabasesUpdated = this._onDatabasesUpdated.event;
|
public onDatabasesUpdated = this._onDatabasesUpdated.event;
|
||||||
public passwordLastUpdated?: Date;
|
public passwordLastUpdated?: Date;
|
||||||
|
|
||||||
constructor(controllerUrl: string, auth: Authentication, private _namespace: string, private _name: string) {
|
private _refreshPromise: Deferred<void> | undefined = undefined;
|
||||||
|
|
||||||
|
constructor(controllerUrl: string, controllerAuth: Authentication, info: ResourceInfo, registration: Registration, private _treeDataProvider: AzureArcTreeDataProvider) {
|
||||||
|
super(info, registration);
|
||||||
this._sqlInstanceRouter = new SqlInstanceRouterApi(controllerUrl);
|
this._sqlInstanceRouter = new SqlInstanceRouterApi(controllerUrl);
|
||||||
this._sqlInstanceRouter.setDefaultAuthentication(auth);
|
this._sqlInstanceRouter.setDefaultAuthentication(controllerAuth);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The name of this instance
|
|
||||||
*/
|
|
||||||
public get name(): string {
|
|
||||||
return this._name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The namespace of this instance
|
|
||||||
*/
|
|
||||||
public get namespace(): string {
|
|
||||||
return this._namespace;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -72,48 +68,118 @@ export class MiaaModel {
|
|||||||
|
|
||||||
/** Refreshes the model */
|
/** Refreshes the model */
|
||||||
public async refresh(): Promise<void> {
|
public async refresh(): Promise<void> {
|
||||||
const instanceRefresh = this._sqlInstanceRouter.apiV1HybridSqlNsNameGet(this._namespace, this._name).then(response => {
|
// Only allow one refresh to be happening at a time
|
||||||
this._status = response.body;
|
if (this._refreshPromise) {
|
||||||
this._onStatusUpdated.fire(this._status);
|
return this._refreshPromise.promise;
|
||||||
});
|
}
|
||||||
const promises: Thenable<any>[] = [instanceRefresh];
|
this._refreshPromise = new Deferred();
|
||||||
await this.getConnection();
|
try {
|
||||||
if (this._connectionProfile) {
|
const instanceRefresh = this._sqlInstanceRouter.apiV1HybridSqlNsNameGet(this.info.namespace, this.info.name).then(response => {
|
||||||
const provider = azdata.dataprotocol.getProvider<azdata.MetadataProvider>(this._connectionProfile.providerName, azdata.DataProviderType.MetadataProvider);
|
this._status = response.body;
|
||||||
const databasesRefresh = azdata.connection.getUriForConnection(this._connectionProfile.id).then(ownerUri => {
|
this._onStatusUpdated.fire(this._status);
|
||||||
provider.getDatabases(ownerUri).then(databases => {
|
});
|
||||||
if (databases.length > 0 && typeof (databases[0]) === 'object') {
|
const promises: Thenable<any>[] = [instanceRefresh];
|
||||||
this._databases = (<azdata.DatabaseInfo[]>databases).map(db => { return { name: db.options['name'], status: db.options['state'] }; });
|
await this.getConnectionProfile();
|
||||||
} else {
|
if (this._connectionProfile) {
|
||||||
this._databases = (<string[]>databases).map(db => { return { name: db, status: '-' }; });
|
// We haven't connected yet so do so now and then store the ID for the active connection
|
||||||
}
|
if (!this._activeConnectionId) {
|
||||||
this._onDatabasesUpdated.fire(this._databases);
|
const result = await azdata.connection.connect(this._connectionProfile, false, false);
|
||||||
});
|
if (!result.connected) {
|
||||||
});
|
throw new Error(result.errorMessage);
|
||||||
promises.push(databasesRefresh);
|
}
|
||||||
|
this._activeConnectionId = result.connectionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
const provider = azdata.dataprotocol.getProvider<azdata.MetadataProvider>(this._connectionProfile.providerName, azdata.DataProviderType.MetadataProvider);
|
||||||
|
const databasesRefresh = azdata.connection.getUriForConnection(this._activeConnectionId).then(ownerUri => {
|
||||||
|
provider.getDatabases(ownerUri).then(databases => {
|
||||||
|
if (!databases) {
|
||||||
|
throw new Error('Could not fetch databases');
|
||||||
|
}
|
||||||
|
if (databases.length > 0 && typeof (databases[0]) === 'object') {
|
||||||
|
this._databases = (<azdata.DatabaseInfo[]>databases).map(db => { return { name: db.options['name'], status: db.options['state'] }; });
|
||||||
|
} else {
|
||||||
|
this._databases = (<string[]>databases).map(db => { return { name: db, status: '-' }; });
|
||||||
|
}
|
||||||
|
this._onDatabasesUpdated.fire(this._databases);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
promises.push(databasesRefresh);
|
||||||
|
}
|
||||||
|
await Promise.all(promises);
|
||||||
|
} finally {
|
||||||
|
this._refreshPromise = undefined;
|
||||||
}
|
}
|
||||||
await Promise.all(promises);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getConnection(): Promise<void> {
|
/**
|
||||||
|
* Loads the saved connection profile associated with this model. Will prompt for one if
|
||||||
|
* we don't have one or can't find it (it was deleted)
|
||||||
|
*/
|
||||||
|
private async getConnectionProfile(): Promise<void> {
|
||||||
if (this._connectionProfile) {
|
if (this._connectionProfile) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const connection = await azdata.connection.openConnectionDialog(['MSSQL']);
|
let connection: azdata.connection.ConnectionProfile | azdata.connection.Connection | undefined;
|
||||||
this._connectionProfile = {
|
|
||||||
serverName: connection.options['serverName'],
|
if (this.info.connectionId) {
|
||||||
databaseName: connection.options['databaseName'],
|
try {
|
||||||
authenticationType: connection.options['authenticationType'],
|
const connections = await azdata.connection.getConnections();
|
||||||
providerName: 'MSSQL',
|
const existingConnection = connections.find(conn => conn.connectionId === this.info.connectionId);
|
||||||
connectionName: '',
|
if (existingConnection) {
|
||||||
userName: connection.options['user'],
|
const credentials = await azdata.connection.getCredentials(this.info.connectionId);
|
||||||
password: connection.options['password'],
|
if (credentials) {
|
||||||
savePassword: false,
|
existingConnection.options['password'] = credentials.password;
|
||||||
groupFullName: undefined,
|
connection = existingConnection;
|
||||||
saveProfile: true,
|
} else {
|
||||||
id: connection.connectionId,
|
// We need the password so prompt the user for it
|
||||||
groupId: undefined,
|
const connectionProfile = {
|
||||||
options: connection.options
|
serverName: existingConnection.options['serverName'],
|
||||||
};
|
databaseName: existingConnection.options['databaseName'],
|
||||||
|
authenticationType: existingConnection.options['authenticationType'],
|
||||||
|
providerName: 'MSSQL',
|
||||||
|
connectionName: '',
|
||||||
|
userName: existingConnection.options['user'],
|
||||||
|
password: '',
|
||||||
|
savePassword: false,
|
||||||
|
groupFullName: undefined,
|
||||||
|
saveProfile: true,
|
||||||
|
id: '',
|
||||||
|
groupId: undefined,
|
||||||
|
options: existingConnection.options
|
||||||
|
};
|
||||||
|
connection = await azdata.connection.openConnectionDialog(['MSSQL'], connectionProfile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// ignore - the connection may not necessarily exist anymore and in that case we'll just reprompt for a connection
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!connection) {
|
||||||
|
// Weren't able to load the existing connection so prompt user for new one
|
||||||
|
connection = await azdata.connection.openConnectionDialog(['MSSQL']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connection) {
|
||||||
|
this._connectionProfile = {
|
||||||
|
serverName: connection.options['serverName'] || connection.options['server'],
|
||||||
|
databaseName: connection.options['databaseName'] || connection.options['database'],
|
||||||
|
authenticationType: connection.options['authenticationType'],
|
||||||
|
providerName: 'MSSQL',
|
||||||
|
connectionName: '',
|
||||||
|
userName: connection.options['user'],
|
||||||
|
password: connection.options['password'],
|
||||||
|
savePassword: false,
|
||||||
|
groupFullName: undefined,
|
||||||
|
saveProfile: true,
|
||||||
|
id: connection.connectionId,
|
||||||
|
groupId: undefined,
|
||||||
|
options: connection.options
|
||||||
|
};
|
||||||
|
this.info.connectionId = connection.connectionId;
|
||||||
|
await this._treeDataProvider.saveControllers();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import * as vscode from 'vscode';
|
|||||||
import * as loc from '../localizedConstants';
|
import * as loc from '../localizedConstants';
|
||||||
import { DuskyObjectModelsDatabaseService, DatabaseRouterApi, DuskyObjectModelsDatabase, V1Status, V1Pod } from '../controller/generated/dusky/api';
|
import { DuskyObjectModelsDatabaseService, DatabaseRouterApi, DuskyObjectModelsDatabase, V1Status, V1Pod } from '../controller/generated/dusky/api';
|
||||||
import { Authentication } from '../controller/auth';
|
import { Authentication } from '../controller/auth';
|
||||||
|
import { ResourceInfo, Registration } from './controllerModel';
|
||||||
|
import { ResourceModel } from './resourceModel';
|
||||||
|
|
||||||
export enum PodRole {
|
export enum PodRole {
|
||||||
Monitor,
|
Monitor,
|
||||||
@@ -14,7 +16,7 @@ export enum PodRole {
|
|||||||
Shard
|
Shard
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PostgresModel {
|
export class PostgresModel extends ResourceModel {
|
||||||
private _databaseRouter: DatabaseRouterApi;
|
private _databaseRouter: DatabaseRouterApi;
|
||||||
private _service?: DuskyObjectModelsDatabaseService;
|
private _service?: DuskyObjectModelsDatabaseService;
|
||||||
private _pods?: V1Pod[];
|
private _pods?: V1Pod[];
|
||||||
@@ -25,24 +27,25 @@ export class PostgresModel {
|
|||||||
public serviceLastUpdated?: Date;
|
public serviceLastUpdated?: Date;
|
||||||
public podsLastUpdated?: Date;
|
public podsLastUpdated?: Date;
|
||||||
|
|
||||||
constructor(controllerUrl: string, auth: Authentication, private _namespace: string, private _name: string) {
|
constructor(controllerUrl: string, auth: Authentication, info: ResourceInfo, registration: Registration) {
|
||||||
|
super(info, registration);
|
||||||
this._databaseRouter = new DatabaseRouterApi(controllerUrl);
|
this._databaseRouter = new DatabaseRouterApi(controllerUrl);
|
||||||
this._databaseRouter.setDefaultAuthentication(auth);
|
this._databaseRouter.setDefaultAuthentication(auth);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the service's Kubernetes namespace */
|
/** Returns the service's Kubernetes namespace */
|
||||||
public get namespace(): string {
|
public get namespace(): string {
|
||||||
return this._namespace;
|
return this.info.namespace;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the service's name */
|
/** Returns the service's name */
|
||||||
public get name(): string {
|
public get name(): string {
|
||||||
return this._name;
|
return this.info.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the service's fully qualified name in the format namespace.name */
|
/** Returns the service's fully qualified name in the format namespace.name */
|
||||||
public get fullName(): string {
|
public get fullName(): string {
|
||||||
return `${this._namespace}.${this._name}`;
|
return `${this.info.namespace}.${this.info.name}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the service's spec */
|
/** Returns the service's spec */
|
||||||
@@ -58,12 +61,12 @@ export class PostgresModel {
|
|||||||
/** Refreshes the model */
|
/** Refreshes the model */
|
||||||
public async refresh() {
|
public async refresh() {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
this._databaseRouter.getDuskyDatabaseService(this._namespace, this._name).then(response => {
|
this._databaseRouter.getDuskyDatabaseService(this.info.namespace, this.info.name).then(response => {
|
||||||
this._service = response.body;
|
this._service = response.body;
|
||||||
this.serviceLastUpdated = new Date();
|
this.serviceLastUpdated = new Date();
|
||||||
this._onServiceUpdated.fire(this._service);
|
this._onServiceUpdated.fire(this._service);
|
||||||
}),
|
}),
|
||||||
this._databaseRouter.getDuskyPods(this._namespace, this._name).then(response => {
|
this._databaseRouter.getDuskyPods(this.info.namespace, this.info.name).then(response => {
|
||||||
this._pods = response.body;
|
this._pods = response.body;
|
||||||
this.podsLastUpdated = new Date();
|
this.podsLastUpdated = new Date();
|
||||||
this._onPodsUpdated.fire(this._pods!);
|
this._onPodsUpdated.fire(this._pods!);
|
||||||
@@ -77,7 +80,7 @@ export class PostgresModel {
|
|||||||
*/
|
*/
|
||||||
public async update(func: (service: DuskyObjectModelsDatabaseService) => void): Promise<DuskyObjectModelsDatabaseService> {
|
public async update(func: (service: DuskyObjectModelsDatabaseService) => void): Promise<DuskyObjectModelsDatabaseService> {
|
||||||
// Get the latest spec of the service in case it has changed
|
// Get the latest spec of the service in case it has changed
|
||||||
const service = (await this._databaseRouter.getDuskyDatabaseService(this._namespace, this._name)).body;
|
const service = (await this._databaseRouter.getDuskyDatabaseService(this.info.namespace, this.info.name)).body;
|
||||||
service.status = undefined; // can't update the status
|
service.status = undefined; // can't update the status
|
||||||
func(service);
|
func(service);
|
||||||
|
|
||||||
@@ -89,7 +92,7 @@ export class PostgresModel {
|
|||||||
|
|
||||||
/** Deletes the service */
|
/** Deletes the service */
|
||||||
public async delete(): Promise<V1Status> {
|
public async delete(): Promise<V1Status> {
|
||||||
return (await this._databaseRouter.deleteDuskyDatabaseService(this._namespace, this._name)).body;
|
return (await this._databaseRouter.deleteDuskyDatabaseService(this.info.namespace, this.info.name)).body;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a SQL database in the service */
|
/** Creates a SQL database in the service */
|
||||||
|
|||||||
26
extensions/arc/src/models/resourceModel.ts
Normal file
26
extensions/arc/src/models/resourceModel.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 { ResourceInfo, Registration } from './controllerModel';
|
||||||
|
|
||||||
|
export abstract class ResourceModel {
|
||||||
|
|
||||||
|
private readonly _onRegistrationUpdated = new vscode.EventEmitter<Registration>();
|
||||||
|
public onRegistrationUpdated = this._onRegistrationUpdated.event;
|
||||||
|
|
||||||
|
constructor(public info: ResourceInfo, private _registration: Registration) { }
|
||||||
|
|
||||||
|
public get registration(): Registration {
|
||||||
|
return this._registration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set registration(newValue: Registration) {
|
||||||
|
this._registration = newValue;
|
||||||
|
this._onRegistrationUpdated.fire(this._registration);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract refresh(): Promise<void>;
|
||||||
|
}
|
||||||
@@ -44,7 +44,7 @@ describe('AzureArcTreeDataProvider tests', function (): void {
|
|||||||
treeDataProvider['_loading'] = false;
|
treeDataProvider['_loading'] = false;
|
||||||
let children = await treeDataProvider.getChildren();
|
let children = await treeDataProvider.getChildren();
|
||||||
should(children.length).equal(0, 'There initially shouldn\'t be any children');
|
should(children.length).equal(0, 'There initially shouldn\'t be any children');
|
||||||
const controllerModel = new ControllerModel(treeDataProvider, { url: '127.0.0.1', username: 'sa', rememberPassword: true });
|
const controllerModel = new ControllerModel(treeDataProvider, { url: '127.0.0.1', username: 'sa', rememberPassword: true, resources: [] });
|
||||||
await treeDataProvider.addOrUpdateController(controllerModel, '');
|
await treeDataProvider.addOrUpdateController(controllerModel, '');
|
||||||
should(children.length).equal(1, 'Controller node should be added correctly');
|
should(children.length).equal(1, 'Controller node should be added correctly');
|
||||||
await treeDataProvider.addOrUpdateController(controllerModel, '');
|
await treeDataProvider.addOrUpdateController(controllerModel, '');
|
||||||
@@ -55,11 +55,11 @@ describe('AzureArcTreeDataProvider tests', function (): void {
|
|||||||
treeDataProvider['_loading'] = false;
|
treeDataProvider['_loading'] = false;
|
||||||
let children = await treeDataProvider.getChildren();
|
let children = await treeDataProvider.getChildren();
|
||||||
should(children.length).equal(0, 'There initially shouldn\'t be any children');
|
should(children.length).equal(0, 'There initially shouldn\'t be any children');
|
||||||
const controllerModel = new ControllerModel(treeDataProvider, { url: '127.0.0.1', username: 'sa', rememberPassword: true });
|
const controllerModel = new ControllerModel(treeDataProvider, { url: '127.0.0.1', username: 'sa', rememberPassword: true, resources: [] });
|
||||||
await treeDataProvider.addOrUpdateController(controllerModel, '');
|
await treeDataProvider.addOrUpdateController(controllerModel, '');
|
||||||
should(children.length).equal(1, 'Controller node should be added correctly');
|
should(children.length).equal(1, 'Controller node should be added correctly');
|
||||||
should((<ControllerTreeNode>children[0]).model.info.rememberPassword).be.true('Info was not set correctly initially');
|
should((<ControllerTreeNode>children[0]).model.info.rememberPassword).be.true('Info was not set correctly initially');
|
||||||
const controllerModel2 = new ControllerModel(treeDataProvider, { url: '127.0.0.1', username: 'sa', rememberPassword: false });
|
const controllerModel2 = new ControllerModel(treeDataProvider, { url: '127.0.0.1', username: 'sa', rememberPassword: false, resources: [] });
|
||||||
await treeDataProvider.addOrUpdateController(controllerModel2, '');
|
await treeDataProvider.addOrUpdateController(controllerModel2, '');
|
||||||
should(children.length).equal(1, 'Shouldn\'t add duplicate controller node');
|
should(children.length).equal(1, 'Shouldn\'t add duplicate controller node');
|
||||||
should((<ControllerTreeNode>children[0]).model.info.rememberPassword).be.false('Info was not updated correctly');
|
should((<ControllerTreeNode>children[0]).model.info.rememberPassword).be.false('Info was not updated correctly');
|
||||||
@@ -84,8 +84,8 @@ describe('AzureArcTreeDataProvider tests', function (): void {
|
|||||||
describe('removeController', function (): void {
|
describe('removeController', function (): void {
|
||||||
it('removing a controller should work as expected', async function (): Promise<void> {
|
it('removing a controller should work as expected', async function (): Promise<void> {
|
||||||
treeDataProvider['_loading'] = false;
|
treeDataProvider['_loading'] = false;
|
||||||
const controllerModel = new ControllerModel(treeDataProvider, { url: '127.0.0.1', username: 'sa', rememberPassword: true });
|
const controllerModel = new ControllerModel(treeDataProvider, { url: '127.0.0.1', username: 'sa', rememberPassword: true, resources: [] });
|
||||||
const controllerModel2 = new ControllerModel(treeDataProvider, { url: '127.0.0.2', username: 'cloudsa', rememberPassword: true });
|
const controllerModel2 = new ControllerModel(treeDataProvider, { url: '127.0.0.2', username: 'cloudsa', rememberPassword: true, resources: [] });
|
||||||
await treeDataProvider.addOrUpdateController(controllerModel, '');
|
await treeDataProvider.addOrUpdateController(controllerModel, '');
|
||||||
await treeDataProvider.addOrUpdateController(controllerModel2, '');
|
await treeDataProvider.addOrUpdateController(controllerModel2, '');
|
||||||
const children = <ControllerTreeNode[]>(await treeDataProvider.getChildren());
|
const children = <ControllerTreeNode[]>(await treeDataProvider.getChildren());
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ export class ControllerDashboardOverviewPage extends DashboardPage {
|
|||||||
headerCssStyles: cssStyles.tableHeader,
|
headerCssStyles: cssStyles.tableHeader,
|
||||||
rowCssStyles: cssStyles.tableRow
|
rowCssStyles: cssStyles.tableRow
|
||||||
}, {
|
}, {
|
||||||
displayName: loc.computeAndStorage,
|
displayName: loc.compute,
|
||||||
valueType: azdata.DeclarativeDataType.string,
|
valueType: azdata.DeclarativeDataType.string,
|
||||||
width: '34%',
|
width: '34%',
|
||||||
isReadOnly: true,
|
isReadOnly: true,
|
||||||
@@ -140,12 +140,12 @@ export class ControllerDashboardOverviewPage extends DashboardPage {
|
|||||||
|
|
||||||
public get toolbarContainer(): azdata.ToolbarContainer {
|
public get toolbarContainer(): azdata.ToolbarContainer {
|
||||||
|
|
||||||
const createNewButton = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
const newInstance = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
||||||
label: loc.createNew,
|
label: loc.newInstance,
|
||||||
iconPath: IconPathHelper.add
|
iconPath: IconPathHelper.add
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
createNewButton.onDidClick(async () => {
|
newInstance.onDidClick(async () => {
|
||||||
await vscode.commands.executeCommand('azdata.resource.deploy');
|
await vscode.commands.executeCommand('azdata.resource.deploy');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -166,7 +166,7 @@ export class ControllerDashboardOverviewPage extends DashboardPage {
|
|||||||
|
|
||||||
return this.modelView.modelBuilder.toolbarContainer().withToolbarItems(
|
return this.modelView.modelBuilder.toolbarContainer().withToolbarItems(
|
||||||
[
|
[
|
||||||
{ component: createNewButton, toolbarSeparatorAfter: true },
|
{ component: newInstance, toolbarSeparatorAfter: true },
|
||||||
{ component: openInAzurePortalButton }
|
{ component: openInAzurePortalButton }
|
||||||
]
|
]
|
||||||
).component();
|
).component();
|
||||||
@@ -196,7 +196,7 @@ export class ControllerDashboardOverviewPage extends DashboardPage {
|
|||||||
iconWidth: iconSize
|
iconWidth: iconSize
|
||||||
})
|
})
|
||||||
.component();
|
.component();
|
||||||
return [imageComponent, r.instanceName, resourceTypeToDisplayName(r.instanceType), r.vCores];
|
return [imageComponent, r.instanceName, resourceTypeToDisplayName(r.instanceType), loc.numVCores(r.vCores)];
|
||||||
});
|
});
|
||||||
this._arcResourcesLoadingComponent.loading = false;
|
this._arcResourcesLoadingComponent.loading = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ export class MiaaConnectionStringsPage extends DashboardPage {
|
|||||||
|
|
||||||
constructor(modelView: azdata.ModelView, private _controllerModel: ControllerModel, private _miaaModel: MiaaModel) {
|
constructor(modelView: azdata.ModelView, private _controllerModel: ControllerModel, private _miaaModel: MiaaModel) {
|
||||||
super(modelView);
|
super(modelView);
|
||||||
this._controllerModel.onRegistrationsUpdated(registrations => {
|
this._controllerModel.onRegistrationsUpdated(_ => {
|
||||||
this._instanceRegistration = registrations.find(reg => reg.instanceType === ResourceType.sqlManagedInstances && reg.instanceName === this._miaaModel.name);
|
this._instanceRegistration = this._controllerModel.getRegistration(ResourceType.sqlManagedInstances, this._miaaModel.info.namespace, this._miaaModel.info.name);
|
||||||
this.eventuallyRunOnInitialized(() => this.updateConnectionStrings());
|
this.eventuallyRunOnInitialized(() => this.updateConnectionStrings());
|
||||||
});
|
});
|
||||||
this.refresh().catch(err => console.error(err));
|
this.refresh().catch(err => console.error(err));
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export class MiaaDashboardOverviewPage extends DashboardPage {
|
|||||||
subscriptionId: '-',
|
subscriptionId: '-',
|
||||||
miaaAdmin: '-',
|
miaaAdmin: '-',
|
||||||
host: '-',
|
host: '-',
|
||||||
vCores: '-'
|
vCores: ''
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(modelView: azdata.ModelView, private _controllerModel: ControllerModel, private _miaaModel: MiaaModel) {
|
constructor(modelView: azdata.ModelView, private _controllerModel: ControllerModel, private _miaaModel: MiaaModel) {
|
||||||
@@ -166,11 +166,6 @@ export class MiaaDashboardOverviewPage extends DashboardPage {
|
|||||||
|
|
||||||
public get toolbarContainer(): azdata.ToolbarContainer {
|
public get toolbarContainer(): azdata.ToolbarContainer {
|
||||||
|
|
||||||
const createDatabaseButton = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
|
||||||
label: loc.newDatabase,
|
|
||||||
iconPath: IconPathHelper.add
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
const deleteButton = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
const deleteButton = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
||||||
label: loc.deleteText,
|
label: loc.deleteText,
|
||||||
iconPath: IconPathHelper.delete
|
iconPath: IconPathHelper.delete
|
||||||
@@ -179,55 +174,48 @@ export class MiaaDashboardOverviewPage extends DashboardPage {
|
|||||||
deleteButton.onDidClick(async () => {
|
deleteButton.onDidClick(async () => {
|
||||||
deleteButton.enabled = false;
|
deleteButton.enabled = false;
|
||||||
try {
|
try {
|
||||||
if (await promptForResourceDeletion(this._miaaModel.namespace, this._miaaModel.name)) {
|
if (await promptForResourceDeletion(this._miaaModel.info.namespace, this._miaaModel.info.name)) {
|
||||||
await this._controllerModel.miaaDelete(this._miaaModel.namespace, this._miaaModel.name);
|
await this._controllerModel.miaaDelete(this._miaaModel.info.namespace, this._miaaModel.info.name);
|
||||||
vscode.window.showInformationMessage(loc.resourceDeleted(this._miaaModel.name));
|
vscode.window.showInformationMessage(loc.resourceDeleted(this._miaaModel.info.name));
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
vscode.window.showErrorMessage(loc.resourceDeletionFailed(this._miaaModel.name, error));
|
vscode.window.showErrorMessage(loc.resourceDeletionFailed(this._miaaModel.info.name, error));
|
||||||
} finally {
|
} finally {
|
||||||
deleteButton.enabled = true;
|
deleteButton.enabled = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const resetPasswordButton = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
|
||||||
label: loc.resetPassword,
|
|
||||||
iconPath: IconPathHelper.edit
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
const openInAzurePortalButton = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
const openInAzurePortalButton = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
||||||
label: loc.openInAzurePortal,
|
label: loc.openInAzurePortal,
|
||||||
iconPath: IconPathHelper.openInTab
|
iconPath: IconPathHelper.openInTab
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
openInAzurePortalButton.onDidClick(async () => {
|
openInAzurePortalButton.onDidClick(async () => {
|
||||||
const r = this._controllerModel.getRegistration(ResourceType.sqlManagedInstances, this._miaaModel.namespace, this._miaaModel.name);
|
const r = this._controllerModel.getRegistration(ResourceType.sqlManagedInstances, this._miaaModel.info.namespace, this._miaaModel.info.name);
|
||||||
if (r) {
|
if (r) {
|
||||||
vscode.env.openExternal(vscode.Uri.parse(
|
vscode.env.openExternal(vscode.Uri.parse(
|
||||||
`https://portal.azure.com/#resource/subscriptions/${r.subscriptionId}/resourceGroups/${r.resourceGroupName}/providers/Microsoft.AzureData/${ResourceType.sqlManagedInstances}/${r.instanceName}`));
|
`https://portal.azure.com/#resource/subscriptions/${r.subscriptionId}/resourceGroups/${r.resourceGroupName}/providers/Microsoft.AzureData/${ResourceType.sqlManagedInstances}/${r.instanceName}`));
|
||||||
} else {
|
} else {
|
||||||
vscode.window.showErrorMessage(loc.couldNotFindRegistration(this._miaaModel.namespace, this._miaaModel.name));
|
vscode.window.showErrorMessage(loc.couldNotFindRegistration(this._miaaModel.info.namespace, this._miaaModel.info.name));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return this.modelView.modelBuilder.toolbarContainer().withToolbarItems(
|
return this.modelView.modelBuilder.toolbarContainer().withToolbarItems(
|
||||||
[
|
[
|
||||||
{ component: createDatabaseButton },
|
{ component: deleteButton, toolbarSeparatorAfter: true },
|
||||||
{ component: deleteButton },
|
|
||||||
{ component: resetPasswordButton, toolbarSeparatorAfter: true },
|
|
||||||
{ component: openInAzurePortalButton }
|
{ component: openInAzurePortalButton }
|
||||||
]
|
]
|
||||||
).component();
|
).component();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleRegistrationsUpdated(): Promise<void> {
|
private async handleRegistrationsUpdated(): Promise<void> {
|
||||||
const reg = this._controllerModel.getRegistration(ResourceType.sqlManagedInstances, this._miaaModel.namespace, this._miaaModel.name);
|
const reg = this._controllerModel.getRegistration(ResourceType.sqlManagedInstances, this._miaaModel.info.namespace, this._miaaModel.info.name);
|
||||||
if (reg) {
|
if (reg) {
|
||||||
this._instanceProperties.resourceGroup = reg.resourceGroupName || '-';
|
this._instanceProperties.resourceGroup = reg.resourceGroupName || '-';
|
||||||
this._instanceProperties.dataController = this._controllerModel.controllerRegistration?.instanceName || '-';
|
this._instanceProperties.dataController = this._controllerModel.controllerRegistration?.instanceName || '-';
|
||||||
this._instanceProperties.region = (await getAzurecoreApi()).getRegionDisplayName(reg.location);
|
this._instanceProperties.region = (await getAzurecoreApi()).getRegionDisplayName(reg.location);
|
||||||
this._instanceProperties.subscriptionId = reg.subscriptionId || '-';
|
this._instanceProperties.subscriptionId = reg.subscriptionId || '-';
|
||||||
this._instanceProperties.vCores = reg.vCores || '-';
|
this._instanceProperties.vCores = reg.vCores || '';
|
||||||
this._instanceProperties.host = reg.externalEndpoint || '-';
|
this._instanceProperties.host = reg.externalEndpoint || '-';
|
||||||
this.refreshDisplayedProperties();
|
this.refreshDisplayedProperties();
|
||||||
}
|
}
|
||||||
@@ -239,12 +227,12 @@ export class MiaaDashboardOverviewPage extends DashboardPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private handleEndpointsUpdated(endpoints: EndpointModel[]): void {
|
private handleEndpointsUpdated(endpoints: EndpointModel[]): void {
|
||||||
const kibanaQuery = `kubernetes_namespace:"${this._miaaModel.namespace}" and instance_name :"${this._miaaModel.name}"`;
|
const kibanaQuery = `kubernetes_namespace:"${this._miaaModel.info.namespace}" and instance_name :"${this._miaaModel.info.name}"`;
|
||||||
const kibanaUrl = `${endpoints.find(e => e.name === 'logsui')?.endpoint}/app/kibana#/discover?_a=(query:(language:kuery,query:'${kibanaQuery}'))`;
|
const kibanaUrl = `${endpoints.find(e => e.name === 'logsui')?.endpoint}/app/kibana#/discover?_a=(query:(language:kuery,query:'${kibanaQuery}'))`;
|
||||||
this._kibanaLink.label = kibanaUrl;
|
this._kibanaLink.label = kibanaUrl;
|
||||||
this._kibanaLink.url = kibanaUrl;
|
this._kibanaLink.url = kibanaUrl;
|
||||||
|
|
||||||
const grafanaUrl = `${endpoints.find(e => e.name === 'metricsui')?.endpoint}/d/wZx3OUdmz/azure-sql-db-managed-instance-metrics?var-hostname=${this._miaaModel.name}-0`;
|
const grafanaUrl = `${endpoints.find(e => e.name === 'metricsui')?.endpoint}/d/wZx3OUdmz/azure-sql-db-managed-instance-metrics?var-hostname=${this._miaaModel.info.name}-0`;
|
||||||
this._grafanaLink.label = grafanaUrl;
|
this._grafanaLink.label = grafanaUrl;
|
||||||
this._grafanaLink.url = grafanaUrl;
|
this._grafanaLink.url = grafanaUrl;
|
||||||
|
|
||||||
|
|||||||
@@ -87,7 +87,12 @@ export class ConnectToControllerDialog {
|
|||||||
if (!this.urlInputBox.value || !this.usernameInputBox.value || !this.passwordInputBox.value) {
|
if (!this.urlInputBox.value || !this.usernameInputBox.value || !this.passwordInputBox.value) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const controllerInfo: ControllerInfo = { url: this.urlInputBox.value, username: this.usernameInputBox.value, rememberPassword: this.rememberPwCheckBox.checked ?? false };
|
const controllerInfo: ControllerInfo = {
|
||||||
|
url: this.urlInputBox.value,
|
||||||
|
username: this.usernameInputBox.value,
|
||||||
|
rememberPassword: this.rememberPwCheckBox.checked ?? false,
|
||||||
|
resources: []
|
||||||
|
};
|
||||||
const controllerModel = new ControllerModel(this._treeDataProvider, controllerInfo, this.passwordInputBox.value);
|
const controllerModel = new ControllerModel(this._treeDataProvider, controllerInfo, this.passwordInputBox.value);
|
||||||
try {
|
try {
|
||||||
// Validate that we can connect to the controller
|
// Validate that we can connect to the controller
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ export class AzureArcTreeDataProvider implements vscode.TreeDataProvider<TreeNod
|
|||||||
if (controllerNode) {
|
if (controllerNode) {
|
||||||
controllerNode.model.info = model.info;
|
controllerNode.model.info = model.info;
|
||||||
} else {
|
} else {
|
||||||
this._controllerNodes.push(new ControllerTreeNode(model, this._context));
|
this._controllerNodes.push(new ControllerTreeNode(model, this._context, this));
|
||||||
}
|
}
|
||||||
await this.updatePassword(model, password);
|
await this.updatePassword(model, password);
|
||||||
if (refreshTree) {
|
if (refreshTree) {
|
||||||
@@ -95,7 +95,7 @@ export class AzureArcTreeDataProvider implements vscode.TreeDataProvider<TreeNod
|
|||||||
const controllerMementos: ControllerInfo[] = this._context.globalState.get(mementoToken) || [];
|
const controllerMementos: ControllerInfo[] = this._context.globalState.get(mementoToken) || [];
|
||||||
this._controllerNodes = controllerMementos.map(memento => {
|
this._controllerNodes = controllerMementos.map(memento => {
|
||||||
const controllerModel = new ControllerModel(this, memento);
|
const controllerModel = new ControllerModel(this, memento);
|
||||||
return new ControllerTreeNode(controllerModel, this._context);
|
return new ControllerTreeNode(controllerModel, this._context, this);
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
this._loading = false;
|
this._loading = false;
|
||||||
@@ -103,8 +103,9 @@ export class AzureArcTreeDataProvider implements vscode.TreeDataProvider<TreeNod
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async saveControllers(): Promise<void> {
|
public async saveControllers(): Promise<void> {
|
||||||
await this._context.globalState.update(mementoToken, this._controllerNodes.map(node => node.model.info));
|
const controllerInfo = this._controllerNodes.map(node => node.model.info);
|
||||||
|
await this._context.globalState.update(mementoToken, controllerInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,23 +8,25 @@ import { TreeNode } from './treeNode';
|
|||||||
import { MiaaTreeNode } from './miaaTreeNode';
|
import { MiaaTreeNode } from './miaaTreeNode';
|
||||||
import { ResourceType } from '../../constants';
|
import { ResourceType } from '../../constants';
|
||||||
import { PostgresTreeNode } from './postgresTreeNode';
|
import { PostgresTreeNode } from './postgresTreeNode';
|
||||||
import { ControllerModel, Registration } from '../../models/controllerModel';
|
import { ControllerModel, Registration, ResourceInfo } from '../../models/controllerModel';
|
||||||
import { ControllerDashboard } from '../dashboards/controller/controllerDashboard';
|
import { ControllerDashboard } from '../dashboards/controller/controllerDashboard';
|
||||||
import { PostgresModel } from '../../models/postgresModel';
|
import { PostgresModel } from '../../models/postgresModel';
|
||||||
import { parseInstanceName } from '../../common/utils';
|
import { parseInstanceName } from '../../common/utils';
|
||||||
import { MiaaModel } from '../../models/miaaModel';
|
import { MiaaModel } from '../../models/miaaModel';
|
||||||
import { Deferred } from '../../common/promise';
|
import { Deferred } from '../../common/promise';
|
||||||
import { RefreshTreeNode } from './refreshTreeNode';
|
import { RefreshTreeNode } from './refreshTreeNode';
|
||||||
|
import { ResourceTreeNode } from './resourceTreeNode';
|
||||||
|
import { AzureArcTreeDataProvider } from './azureArcTreeDataProvider';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The TreeNode for displaying an Azure Arc Controller
|
* The TreeNode for displaying an Azure Arc Controller
|
||||||
*/
|
*/
|
||||||
export class ControllerTreeNode extends TreeNode {
|
export class ControllerTreeNode extends TreeNode {
|
||||||
|
|
||||||
private _children: TreeNode[] = [];
|
private _children: ResourceTreeNode[] = [];
|
||||||
private _childrenRefreshPromise = new Deferred();
|
private _childrenRefreshPromise = new Deferred();
|
||||||
|
|
||||||
constructor(public model: ControllerModel, private _context: vscode.ExtensionContext) {
|
constructor(public model: ControllerModel, private _context: vscode.ExtensionContext, private _treeDataProvider: AzureArcTreeDataProvider) {
|
||||||
super(model.info.url, vscode.TreeItemCollapsibleState.Collapsed, ResourceType.dataControllers);
|
super(model.info.url, vscode.TreeItemCollapsibleState.Collapsed, ResourceType.dataControllers);
|
||||||
model.onRegistrationsUpdated(registrations => this.refreshChildren(registrations));
|
model.onRegistrationsUpdated(registrations => this.refreshChildren(registrations));
|
||||||
}
|
}
|
||||||
@@ -51,21 +53,52 @@ export class ControllerTreeNode extends TreeNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private refreshChildren(registrations: Registration[]): void {
|
private refreshChildren(registrations: Registration[]): void {
|
||||||
this._children = <TreeNode[]>registrations.map(registration => {
|
const newChildren: ResourceTreeNode[] = [];
|
||||||
|
registrations.forEach(registration => {
|
||||||
if (!registration.instanceNamespace || !registration.instanceName) {
|
if (!registration.instanceNamespace || !registration.instanceName) {
|
||||||
console.warn('Registration is missing required namespace and name values, skipping');
|
console.warn('Registration is missing required namespace and name values, skipping');
|
||||||
return undefined;
|
return;
|
||||||
}
|
}
|
||||||
switch (registration.instanceType) {
|
|
||||||
case ResourceType.postgresInstances:
|
const resourceInfo: ResourceInfo = {
|
||||||
const postgresModel = new PostgresModel(this.model.info.url, this.model.auth!, registration.instanceNamespace, parseInstanceName(registration.instanceName));
|
namespace: registration.instanceNamespace,
|
||||||
return new PostgresTreeNode(postgresModel, this.model, this._context);
|
name: parseInstanceName(registration.instanceName),
|
||||||
case ResourceType.sqlManagedInstances:
|
resourceType: registration.instanceType ?? ''
|
||||||
const miaaModel = new MiaaModel(this.model.info.url, this.model.auth!, registration.instanceNamespace, parseInstanceName(registration.instanceName));
|
};
|
||||||
return new MiaaTreeNode(miaaModel, this.model);
|
|
||||||
|
let node = this._children.find(n =>
|
||||||
|
n.model?.info?.name === resourceInfo.name &&
|
||||||
|
n.model?.info?.namespace === resourceInfo.namespace &&
|
||||||
|
n.model?.info?.resourceType === resourceInfo.resourceType);
|
||||||
|
|
||||||
|
// If we don't have this child already then create a new node for it
|
||||||
|
if (!node) {
|
||||||
|
// If we had a stored connectionId copy that over
|
||||||
|
resourceInfo.connectionId = this.model.info.resources.find(info =>
|
||||||
|
info.namespace === resourceInfo.namespace &&
|
||||||
|
info.name === resourceInfo.name &&
|
||||||
|
info.resourceType === resourceInfo.resourceType)?.connectionId;
|
||||||
|
|
||||||
|
switch (registration.instanceType) {
|
||||||
|
case ResourceType.postgresInstances:
|
||||||
|
const postgresModel = new PostgresModel(this.model.info.url, this.model.auth!, resourceInfo, registration);
|
||||||
|
node = new PostgresTreeNode(postgresModel, this.model, this._context);
|
||||||
|
break;
|
||||||
|
case ResourceType.sqlManagedInstances:
|
||||||
|
const miaaModel = new MiaaModel(this.model.info.url, this.model.auth!, resourceInfo, registration, this._treeDataProvider);
|
||||||
|
node = new MiaaTreeNode(miaaModel, this.model);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return undefined;
|
if (node) {
|
||||||
}).filter(item => item); // filter out invalid nodes (controllers or ones without required properties)
|
newChildren.push(node);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this._children = newChildren;
|
||||||
|
|
||||||
|
// Update our model info too
|
||||||
|
this.model.info.resources = <ResourceInfo[]>this._children.map(c => c.model?.info).filter(c => c);
|
||||||
|
this._treeDataProvider.saveControllers();
|
||||||
this._childrenRefreshPromise.resolve();
|
this._childrenRefreshPromise.resolve();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,14 +15,14 @@ import { MiaaDashboard } from '../dashboards/miaa/miaaDashboard';
|
|||||||
*/
|
*/
|
||||||
export class MiaaTreeNode extends TreeNode {
|
export class MiaaTreeNode extends TreeNode {
|
||||||
|
|
||||||
constructor(private _model: MiaaModel, private _controllerModel: ControllerModel) {
|
constructor(public model: MiaaModel, private _controllerModel: ControllerModel) {
|
||||||
super(_model.name, vscode.TreeItemCollapsibleState.None, ResourceType.sqlManagedInstances);
|
super(model.info.name, vscode.TreeItemCollapsibleState.None, ResourceType.sqlManagedInstances);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async openDashboard(): Promise<void> {
|
public async openDashboard(): Promise<void> {
|
||||||
const miaaDashboard = new MiaaDashboard(this._controllerModel, this._model);
|
const miaaDashboard = new MiaaDashboard(this._controllerModel, this.model);
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
miaaDashboard.showDashboard(),
|
miaaDashboard.showDashboard(),
|
||||||
this._model.refresh()]);
|
this.model.refresh()]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,18 +5,18 @@
|
|||||||
|
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { ResourceType } from '../../constants';
|
import { ResourceType } from '../../constants';
|
||||||
import { TreeNode } from './treeNode';
|
|
||||||
import { PostgresModel } from '../../models/postgresModel';
|
import { PostgresModel } from '../../models/postgresModel';
|
||||||
import { ControllerModel } from '../../models/controllerModel';
|
import { ControllerModel } from '../../models/controllerModel';
|
||||||
import { PostgresDashboard } from '../dashboards/postgres/postgresDashboard';
|
import { PostgresDashboard } from '../dashboards/postgres/postgresDashboard';
|
||||||
|
import { ResourceTreeNode } from './resourceTreeNode';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The TreeNode for displaying an Postgres Server group
|
* The TreeNode for displaying an Postgres Server group
|
||||||
*/
|
*/
|
||||||
export class PostgresTreeNode extends TreeNode {
|
export class PostgresTreeNode extends ResourceTreeNode {
|
||||||
|
|
||||||
constructor(private _model: PostgresModel, private _controllerModel: ControllerModel, private _context: vscode.ExtensionContext) {
|
constructor(private _model: PostgresModel, private _controllerModel: ControllerModel, private _context: vscode.ExtensionContext) {
|
||||||
super(_model.name, vscode.TreeItemCollapsibleState.None, ResourceType.postgresInstances);
|
super(_model.name, vscode.TreeItemCollapsibleState.None, ResourceType.postgresInstances, _model);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async openDashboard(): Promise<void> {
|
public async openDashboard(): Promise<void> {
|
||||||
|
|||||||
17
extensions/arc/src/ui/tree/resourceTreeNode.ts
Normal file
17
extensions/arc/src/ui/tree/resourceTreeNode.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 { ResourceModel } from '../../models/resourceModel';
|
||||||
|
import { TreeNode } from './treeNode';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A TreeNode belonging to a child of a Controller
|
||||||
|
*/
|
||||||
|
export abstract class ResourceTreeNode extends TreeNode {
|
||||||
|
constructor(label: string, collapsibleState: vscode.TreeItemCollapsibleState, resourceType?: string, public model?: ResourceModel) {
|
||||||
|
super(label, collapsibleState, resourceType);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user