diff --git a/extensions/arc/src/models/controllerModel.ts b/extensions/arc/src/models/controllerModel.ts index eedfed3fae..6c5acf572e 100644 --- a/extensions/arc/src/models/controllerModel.ts +++ b/extensions/arc/src/models/controllerModel.ts @@ -6,7 +6,7 @@ import * as vscode from 'vscode'; import { Authentication, BasicAuth } from '../controller/auth'; import { EndpointsRouterApi, EndpointModel, RegistrationRouterApi, RegistrationResponse, TokenRouterApi, SqlInstanceRouterApi } from '../controller/generated/v1/api'; -import { parseEndpoint, parseInstanceName } from '../common/utils'; +import { getAzurecoreApi, parseEndpoint, parseInstanceName } from '../common/utils'; import { ResourceType } from '../constants'; import { ConnectToControllerDialog } from '../ui/dialogs/connectControllerDialog'; import { AzureArcTreeDataProvider } from '../ui/tree/azureArcTreeDataProvider'; @@ -29,6 +29,7 @@ export type ResourceInfo = { export interface Registration extends RegistrationResponse { externalIp?: string; externalPort?: string; + region?: string } export class ControllerModel { @@ -101,7 +102,9 @@ export class ControllerModel { }), this._tokenRouter.apiV1TokenPost().then(async response => { this._namespace = response.body.namespace!; - this._registrations = (await this._registrationRouter.apiV1RegistrationListResourcesNsGet(this._namespace)).body.map(mapRegistrationResponse); + const registrationResponse = await this._registrationRouter.apiV1RegistrationListResourcesNsGet(this._namespace); + this._registrations = await Promise.all(registrationResponse.body.map(mapRegistrationResponse)); + this._controllerRegistration = this._registrations.find(r => r.instanceType === ResourceType.dataControllers); this.registrationsLastUpdated = new Date(); this._onRegistrationsUpdated.fire(this._registrations); @@ -183,7 +186,12 @@ export class ControllerModel { * Maps a RegistrationResponse to a Registration, * @param response The RegistrationResponse to map */ -function mapRegistrationResponse(response: RegistrationResponse): Registration { +async function mapRegistrationResponse(response: RegistrationResponse): Promise { const parsedEndpoint = parseEndpoint(response.externalEndpoint); - return { ...response, externalIp: parsedEndpoint.ip, externalPort: parsedEndpoint.port }; + return { + ...response, + externalIp: parsedEndpoint.ip, + externalPort: parsedEndpoint.port, + region: (await getAzurecoreApi()).getRegionDisplayName(response.location) + }; } diff --git a/extensions/arc/src/models/miaaModel.ts b/extensions/arc/src/models/miaaModel.ts index 28c943e682..9a54d46ce4 100644 --- a/extensions/arc/src/models/miaaModel.ts +++ b/extensions/arc/src/models/miaaModel.ts @@ -27,13 +27,12 @@ export class MiaaModel extends ResourceModel { // The ID of the active connection used to query the server private _activeConnectionId: string | undefined = undefined; - private readonly _onPasswordUpdated = new vscode.EventEmitter(); private readonly _onStatusUpdated = new vscode.EventEmitter(); private readonly _onDatabasesUpdated = new vscode.EventEmitter(); - public onPasswordUpdated = this._onPasswordUpdated.event; public onStatusUpdated = this._onStatusUpdated.event; public onDatabasesUpdated = this._onDatabasesUpdated.event; - public passwordLastUpdated?: Date; + public statusLastUpdated?: Date; + public databasesLastUpdated?: Date; private _refreshPromise: Deferred | undefined = undefined; @@ -78,12 +77,14 @@ export class MiaaModel extends ResourceModel { try { const instanceRefresh = this._sqlInstanceRouter.apiV1HybridSqlNsNameGet(this.info.namespace, this.info.name).then(response => { this._status = response.body; + this.statusLastUpdated = new Date(); this._onStatusUpdated.fire(this._status); }).catch(err => { // If an error occurs show a message so the user knows something failed but still // fire the event so callers can know to update (e.g. so dashboards don't show the // loading icon forever) vscode.window.showErrorMessage(loc.fetchStatusFailed(this.info.name, err)); + this.statusLastUpdated = new Date(); this._onStatusUpdated.fire(undefined); throw err; }); @@ -111,6 +112,7 @@ export class MiaaModel extends ResourceModel { } else { this._databases = (databases).map(db => { return { name: db, status: '-' }; }); } + this.databasesLastUpdated = new Date(); this._onDatabasesUpdated.fire(this._databases); }); }); @@ -125,6 +127,7 @@ export class MiaaModel extends ResourceModel { } else { vscode.window.showErrorMessage(loc.fetchStatusFailed(this.info.name, err)); } + this.databasesLastUpdated = new Date(); this._onDatabasesUpdated.fire(this._databases); throw err; } diff --git a/extensions/arc/src/ui/dashboards/controller/controllerDashboardOverviewPage.ts b/extensions/arc/src/ui/dashboards/controller/controllerDashboardOverviewPage.ts index 6231b99998..49f81a2352 100644 --- a/extensions/arc/src/ui/dashboards/controller/controllerDashboardOverviewPage.ts +++ b/extensions/arc/src/ui/dashboards/controller/controllerDashboardOverviewPage.ts @@ -9,9 +9,7 @@ import * as loc from '../../../localizedConstants'; import { DashboardPage } from '../../components/dashboardPage'; import { IconPathHelper, cssStyles, iconSize, ResourceType, Endpoints } from '../../../constants'; import { ControllerModel } from '../../../models/controllerModel'; -import { resourceTypeToDisplayName, getAzurecoreApi, getResourceTypeIcon, getConnectionModeDisplayText, parseInstanceName } from '../../../common/utils'; -import { RegistrationResponse } from '../../../controller/generated/v1/model/registrationResponse'; -import { EndpointModel } from '../../../controller/generated/v1/api'; +import { resourceTypeToDisplayName, getResourceTypeIcon, getConnectionModeDisplayText, parseInstanceName } from '../../../common/utils'; export class ControllerDashboardOverviewPage extends DashboardPage { @@ -31,24 +29,12 @@ export class ControllerDashboardOverviewPage extends DashboardPage { instanceNamespace: '-', }; - private _endpointsRetrieved = false; - private _registrationsRetrieved = false; - constructor(modelView: azdata.ModelView, private _controllerModel: ControllerModel) { super(modelView); - this._controllerModel.onRegistrationsUpdated((_: RegistrationResponse[]) => { - this.eventuallyRunOnInitialized(() => { - this.handleRegistrationsUpdated().catch(e => console.log(e)); - }); - }); - this._controllerModel.onEndpointsUpdated(endpoints => { - this.eventuallyRunOnInitialized(() => { - this.handleEndpointsUpdated(endpoints); - }); - }); - this.refresh().catch(e => { - console.log(e); - }); + + this.disposables.push( + this._controllerModel.onRegistrationsUpdated(() => this.eventuallyRunOnInitialized(() => this.handleRegistrationsUpdated())), + this._controllerModel.onEndpointsUpdated(() => this.eventuallyRunOnInitialized(() => this.handleEndpointsUpdated()))); } public get title(): string { @@ -68,27 +54,11 @@ export class ControllerDashboardOverviewPage extends DashboardPage { } public get container(): azdata.Component { - - const rootContainer = this.modelView.modelBuilder.flexContainer() - .withLayout({ flexFlow: 'column' }) - .component(); - - const contentContainer = this.modelView.modelBuilder.divContainer().component(); - rootContainer.addItem(contentContainer, { CSSStyles: { 'margin': '10px 20px 0px 20px' } }); - + // Create loaded components this._propertiesContainer = this.modelView.modelBuilder.propertiesContainer().component(); - this._propertiesLoadingComponent = this.modelView.modelBuilder.loadingComponent().withItem(this._propertiesContainer).component(); - - contentContainer.addItem(this._propertiesLoadingComponent); - - const arcResourcesTitle = this.modelView.modelBuilder.text() - .withProperties({ value: loc.arcResources }) - .component(); - - contentContainer.addItem(arcResourcesTitle, { - CSSStyles: cssStyles.title - }); + this._propertiesLoadingComponent = this.modelView.modelBuilder.loadingComponent().component(); + this._arcResourcesLoadingComponent = this.modelView.modelBuilder.loadingComponent().component(); this._arcResourcesTable = this.modelView.modelBuilder.declarativeTable().withProperties({ data: [], columns: [ @@ -127,11 +97,33 @@ export class ControllerDashboardOverviewPage extends DashboardPage { ariaLabel: loc.arcResources }).component(); - const arcResourcesTableContainer = this.modelView.modelBuilder.divContainer() - .withItems([this._arcResourcesTable]) + // Update loaded components with data + this.handleRegistrationsUpdated(); + this.handleEndpointsUpdated(); + + // Assign the loading component after it has data + this._propertiesLoadingComponent.component = this._propertiesContainer; + this._arcResourcesLoadingComponent.component = this._arcResourcesTable; + + // Assemble the container + const rootContainer = this.modelView.modelBuilder.flexContainer() + .withLayout({ flexFlow: 'column' }) .component(); - this._arcResourcesLoadingComponent = this.modelView.modelBuilder.loadingComponent().withItem(arcResourcesTableContainer).component(); + const contentContainer = this.modelView.modelBuilder.divContainer().component(); + rootContainer.addItem(contentContainer, { CSSStyles: { 'margin': '10px 20px 0px 20px' } }); + + // Properties + contentContainer.addItem(this._propertiesLoadingComponent); + + // Resources + const arcResourcesTitle = this.modelView.modelBuilder.text() + .withProperties({ value: loc.arcResources }) + .component(); + + contentContainer.addItem(arcResourcesTitle, { + CSSStyles: cssStyles.title + }); contentContainer.addItem(this._arcResourcesLoadingComponent); this.initialized = true; @@ -193,19 +185,18 @@ export class ControllerDashboardOverviewPage extends DashboardPage { ).component(); } - private async handleRegistrationsUpdated(): Promise { + private handleRegistrationsUpdated(): void { const reg = this._controllerModel.controllerRegistration; this.controllerProperties.instanceName = reg?.instanceName || this.controllerProperties.instanceName; this.controllerProperties.resourceGroupName = reg?.resourceGroupName || this.controllerProperties.resourceGroupName; - this.controllerProperties.location = (await getAzurecoreApi()).getRegionDisplayName(reg?.location) || this.controllerProperties.location; + this.controllerProperties.location = reg?.region || this.controllerProperties.location; this.controllerProperties.subscriptionId = reg?.subscriptionId || this.controllerProperties.subscriptionId; this.controllerProperties.connectionMode = getConnectionModeDisplayText(reg?.connectionMode) || this.controllerProperties.connectionMode; this.controllerProperties.instanceNamespace = reg?.instanceNamespace || this.controllerProperties.instanceNamespace; - this._registrationsRetrieved = true; this.refreshDisplayedProperties(); this._arcResourcesTable.data = this._controllerModel.registrations - .filter(r => r.instanceType !== ResourceType.dataControllers) + .filter(r => r.instanceType !== ResourceType.dataControllers && !r.isDeleted) .map(r => { const iconPath = getResourceTypeIcon(r.instanceType ?? ''); const imageComponent = this.modelView.modelBuilder.image() @@ -226,55 +217,51 @@ export class ControllerDashboardOverviewPage extends DashboardPage { }); return [imageComponent, nameLink, resourceTypeToDisplayName(r.instanceType), loc.numVCores(r.vCores)]; }); - this._arcResourcesLoadingComponent.loading = false; + this._arcResourcesLoadingComponent.loading = !this._controllerModel.registrationsLastUpdated; } - private handleEndpointsUpdated(endpoints: EndpointModel[]): void { - const controllerEndpoint = endpoints.find(endpoint => endpoint.name === Endpoints.controller); + private handleEndpointsUpdated(): void { + const controllerEndpoint = this._controllerModel.getEndpoint(Endpoints.controller); this.controllerProperties.controllerEndpoint = controllerEndpoint?.endpoint || this.controllerProperties.controllerEndpoint; - this._endpointsRetrieved = true; this.refreshDisplayedProperties(); } private refreshDisplayedProperties(): void { - // Only update once we've retrieved all the necessary properties - if (this._endpointsRetrieved && this._registrationsRetrieved) { - this._propertiesContainer.propertyItems = [ - { - displayName: loc.name, - value: this.controllerProperties.instanceName - }, - { - displayName: loc.resourceGroup, - value: this.controllerProperties.resourceGroupName - }, - { - displayName: loc.region, - value: this.controllerProperties.location - }, - { - displayName: loc.subscriptionId, - value: this.controllerProperties.subscriptionId - }, - { - displayName: loc.type, - value: loc.dataControllersType - }, - { - displayName: loc.controllerEndpoint, - value: this.controllerProperties.controllerEndpoint - }, - { - displayName: loc.connectionMode, - value: this.controllerProperties.connectionMode - }, - { - displayName: loc.namespace, - value: this.controllerProperties.instanceNamespace - } - ]; - this._propertiesLoadingComponent.loading = false; - } + this._propertiesContainer.propertyItems = [ + { + displayName: loc.name, + value: this.controllerProperties.instanceName + }, + { + displayName: loc.resourceGroup, + value: this.controllerProperties.resourceGroupName + }, + { + displayName: loc.region, + value: this.controllerProperties.location + }, + { + displayName: loc.subscriptionId, + value: this.controllerProperties.subscriptionId + }, + { + displayName: loc.type, + value: loc.dataControllersType + }, + { + displayName: loc.controllerEndpoint, + value: this.controllerProperties.controllerEndpoint + }, + { + displayName: loc.connectionMode, + value: this.controllerProperties.connectionMode + }, + { + displayName: loc.namespace, + value: this.controllerProperties.instanceNamespace + } + ]; + this._propertiesLoadingComponent.loading = !this._controllerModel.registrationsLastUpdated && !this._controllerModel.endpointsLastUpdated; } } diff --git a/extensions/arc/src/ui/dashboards/miaa/miaaConnectionStringsPage.ts b/extensions/arc/src/ui/dashboards/miaa/miaaConnectionStringsPage.ts index 22bc3ab20c..36b1718d04 100644 --- a/extensions/arc/src/ui/dashboards/miaa/miaaConnectionStringsPage.ts +++ b/extensions/arc/src/ui/dashboards/miaa/miaaConnectionStringsPage.ts @@ -8,20 +8,17 @@ import * as loc from '../../../localizedConstants'; import { IconPathHelper, cssStyles, ResourceType } from '../../../constants'; import { KeyValueContainer, KeyValue, InputKeyValue, MultilineInputKeyValue } from '../../components/keyValueContainer'; import { DashboardPage } from '../../components/dashboardPage'; -import { ControllerModel, Registration } from '../../../models/controllerModel'; +import { ControllerModel } from '../../../models/controllerModel'; import { MiaaModel } from '../../../models/miaaModel'; export class MiaaConnectionStringsPage extends DashboardPage { private _keyValueContainer!: KeyValueContainer; - private _instanceRegistration: Registration | undefined; constructor(modelView: azdata.ModelView, private _controllerModel: ControllerModel, private _miaaModel: MiaaModel) { super(modelView); - this.disposables.push(this._controllerModel.onRegistrationsUpdated(_ => { - this._instanceRegistration = this._controllerModel.getRegistration(ResourceType.sqlManagedInstances, this._miaaModel.info.namespace, this._miaaModel.info.name); - this.eventuallyRunOnInitialized(() => this.updateConnectionStrings()); - })); + this.disposables.push(this._controllerModel.onRegistrationsUpdated(_ => + this.eventuallyRunOnInitialized(() => this.updateConnectionStrings()))); } protected get title(): string { @@ -55,11 +52,9 @@ export class MiaaConnectionStringsPage extends DashboardPage { this.modelView.modelBuilder.flexContainer().withItems([info]).withLayout({ flexWrap: 'wrap' }).component(), { CSSStyles: { display: 'inline-flex', 'margin-bottom': '25px' } }); - this._keyValueContainer = new KeyValueContainer(this.modelView.modelBuilder, []); + this._keyValueContainer = new KeyValueContainer(this.modelView.modelBuilder, this.getConnectionStrings()); this.disposables.push(this._keyValueContainer); content.addItem(this._keyValueContainer.container); - - this.updateConnectionStrings(); this.initialized = true; return root; } @@ -68,16 +63,17 @@ export class MiaaConnectionStringsPage extends DashboardPage { return this.modelView.modelBuilder.toolbarContainer().component(); } - private updateConnectionStrings(): void { - if (!this._instanceRegistration) { - return; + private getConnectionStrings(): KeyValue[] { + const instanceRegistration = this._controllerModel.getRegistration(ResourceType.sqlManagedInstances, this._miaaModel.info.namespace, this._miaaModel.info.name); + if (!instanceRegistration) { + return []; } - const ip = this._instanceRegistration.externalIp; - const port = this._instanceRegistration.externalPort; + const ip = instanceRegistration.externalIp; + const port = instanceRegistration.externalPort; const username = this._miaaModel.username; - const pairs: KeyValue[] = [ + return [ new InputKeyValue(this.modelView.modelBuilder, 'ADO.NET', `Server=tcp:${ip},${port};Persist Security Info=False;User ID=${username};Password={your_password_here};MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;`), new InputKeyValue(this.modelView.modelBuilder, 'C++ (libpq)', `host=${ip} port=${port} user=${username} password={your_password_here} sslmode=require`), new InputKeyValue(this.modelView.modelBuilder, 'JDBC', `jdbc:sqlserver://${ip}:${port};user=${username};password={your_password_here};encrypt=true;trustServerCertificate=false;loginTimeout=30;`), @@ -91,6 +87,9 @@ $conn = sqlsrv_connect($serverName, $connectionInfo);`), new InputKeyValue(this.modelView.modelBuilder, 'Ruby', `host=${ip}; user=${username} password={your_password_here} port=${port} sslmode=require`), new InputKeyValue(this.modelView.modelBuilder, 'Web App', `Database=master; Data Source=${ip}; User Id=${username}; Password={your_password_here}`) ]; - this._keyValueContainer.refresh(pairs); + } + + private updateConnectionStrings(): void { + this._keyValueContainer.refresh(this.getConnectionStrings()); } } diff --git a/extensions/arc/src/ui/dashboards/miaa/miaaDashboardOverviewPage.ts b/extensions/arc/src/ui/dashboards/miaa/miaaDashboardOverviewPage.ts index b406a83cba..1595a81aaa 100644 --- a/extensions/arc/src/ui/dashboards/miaa/miaaDashboardOverviewPage.ts +++ b/extensions/arc/src/ui/dashboards/miaa/miaaDashboardOverviewPage.ts @@ -7,12 +7,10 @@ import * as azdata from 'azdata'; import * as vscode from 'vscode'; import * as loc from '../../../localizedConstants'; import { DashboardPage } from '../../components/dashboardPage'; -import { IconPathHelper, cssStyles, ResourceType } from '../../../constants'; -import { ControllerModel, Registration } from '../../../models/controllerModel'; -import { getAzurecoreApi, promptForResourceDeletion, getDatabaseStateDisplayText } from '../../../common/utils'; -import { MiaaModel, DatabaseModel } from '../../../models/miaaModel'; -import { HybridSqlNsNameGetResponse } from '../../../controller/generated/v1/model/hybridSqlNsNameGetResponse'; -import { EndpointModel } from '../../../controller/generated/v1/api'; +import { IconPathHelper, cssStyles, ResourceType, Endpoints } from '../../../constants'; +import { ControllerModel } from '../../../models/controllerModel'; +import { promptForResourceDeletion, getDatabaseStateDisplayText } from '../../../common/utils'; +import { MiaaModel } from '../../../models/miaaModel'; export class MiaaDashboardOverviewPage extends DashboardPage { @@ -41,14 +39,10 @@ export class MiaaDashboardOverviewPage extends DashboardPage { super(modelView); this._instanceProperties.miaaAdmin = this._miaaModel.username || this._instanceProperties.miaaAdmin; this.disposables.push( - this._controllerModel.onRegistrationsUpdated((_: Registration[]) => { - this.eventuallyRunOnInitialized(() => { - this.handleRegistrationsUpdated().catch(e => console.log(e)); - }); - }), - this._controllerModel.onEndpointsUpdated(endpoints => this.eventuallyRunOnInitialized(() => this.handleEndpointsUpdated(endpoints))), - this._miaaModel.onStatusUpdated(status => this.eventuallyRunOnInitialized(() => this.handleMiaaStatusUpdated(status))), - this._miaaModel.onDatabasesUpdated(databases => this.eventuallyRunOnInitialized(() => this.handleDatabasesUpdated(databases))) + this._controllerModel.onRegistrationsUpdated(() => this.handleRegistrationsUpdated()), + this._controllerModel.onEndpointsUpdated(() => this.eventuallyRunOnInitialized(() => this.handleEndpointsUpdated())), + this._miaaModel.onStatusUpdated(() => this.eventuallyRunOnInitialized(() => this.handleMiaaStatusUpdated())), + this._miaaModel.onDatabasesUpdated(() => this.eventuallyRunOnInitialized(() => this.handleDatabasesUpdated())) ); } @@ -69,26 +63,65 @@ export class MiaaDashboardOverviewPage extends DashboardPage { } public get container(): azdata.Component { + // Create loaded components + this._propertiesContainer = this.modelView.modelBuilder.propertiesContainer().component(); + this._propertiesLoading = this.modelView.modelBuilder.loadingComponent().component(); + this._kibanaLink = this.modelView.modelBuilder.hyperlink().component(); + this._kibanaLoading = this.modelView.modelBuilder.loadingComponent().component(); + + this._grafanaLink = this.modelView.modelBuilder.hyperlink().component(); + this._grafanaLoading = this.modelView.modelBuilder.loadingComponent().component(); + + this._databasesTableLoading = this.modelView.modelBuilder.loadingComponent().component(); + this._databasesTable = this.modelView.modelBuilder.declarativeTable().withProperties({ + width: '100%', + columns: [ + { + displayName: loc.name, + valueType: azdata.DeclarativeDataType.string, + isReadOnly: true, + width: '80%', + headerCssStyles: cssStyles.tableHeader, + rowCssStyles: cssStyles.tableRow + }, + { + displayName: loc.status, + valueType: azdata.DeclarativeDataType.string, + isReadOnly: true, + width: '20%', + headerCssStyles: cssStyles.tableHeader, + rowCssStyles: cssStyles.tableRow + } + ], + data: [] + }).component(); + + // Update loaded components with data + this.handleRegistrationsUpdated(); + this.handleMiaaStatusUpdated(); + this.handleEndpointsUpdated(); + this.handleDatabasesUpdated(); + + // Assign the loading component after it has data + this._propertiesLoading.component = this._propertiesContainer; + this._kibanaLoading.component = this._kibanaLink; + this._grafanaLoading.component = this._grafanaLink; + this._databasesTableLoading.component = this._databasesTable; + + // Assemble the container const rootContainer = this.modelView.modelBuilder.flexContainer() .withLayout({ flexFlow: 'column' }) .withProperties({ CSSStyles: { 'margin': '18px' } }) .component(); // Properties - this._propertiesContainer = this.modelView.modelBuilder.propertiesContainer().component(); - this._propertiesLoading = this.modelView.modelBuilder.loadingComponent().withItem(this._propertiesContainer).component(); rootContainer.addItem(this._propertiesLoading, { CSSStyles: cssStyles.text }); // Service endpoints const titleCSS = { ...cssStyles.title, 'margin-block-start': '2em', 'margin-block-end': '0' }; rootContainer.addItem(this.modelView.modelBuilder.text().withProperties({ value: loc.serviceEndpoints, CSSStyles: titleCSS }).component()); - this._kibanaLink = this.modelView.modelBuilder.hyperlink().component(); - this._grafanaLink = this.modelView.modelBuilder.hyperlink().component(); - this._kibanaLoading = this.modelView.modelBuilder.loadingComponent().withItem(this._kibanaLink).component(); - this._grafanaLoading = this.modelView.modelBuilder.loadingComponent().withItem(this._grafanaLink).component(); - const endpointsTable = this.modelView.modelBuilder.declarativeTable().withProperties({ width: '100%', columns: [ @@ -132,30 +165,6 @@ export class MiaaDashboardOverviewPage extends DashboardPage { // Databases rootContainer.addItem(this.modelView.modelBuilder.text().withProperties({ value: loc.databases, CSSStyles: titleCSS }).component()); - this._databasesTable = this.modelView.modelBuilder.declarativeTable().withProperties({ - width: '100%', - columns: [ - { - displayName: loc.name, - valueType: azdata.DeclarativeDataType.string, - isReadOnly: true, - width: '80%', - headerCssStyles: cssStyles.tableHeader, - rowCssStyles: cssStyles.tableRow - }, - { - displayName: loc.status, - valueType: azdata.DeclarativeDataType.string, - isReadOnly: true, - width: '20%', - headerCssStyles: cssStyles.tableHeader, - rowCssStyles: cssStyles.tableRow - } - ], - data: [] - }).component(); - - this._databasesTableLoading = this.modelView.modelBuilder.loadingComponent().withItem(this._databasesTable).component(); rootContainer.addItem(this._databasesTableLoading, { CSSStyles: { 'margin-bottom': '20px' } }); this.initialized = true; @@ -230,12 +239,12 @@ export class MiaaDashboardOverviewPage extends DashboardPage { ).component(); } - private async handleRegistrationsUpdated(): Promise { + private handleRegistrationsUpdated(): void { const reg = this._controllerModel.getRegistration(ResourceType.sqlManagedInstances, this._miaaModel.info.namespace, this._miaaModel.info.name); if (reg) { this._instanceProperties.resourceGroup = reg.resourceGroupName || '-'; this._instanceProperties.dataController = this._controllerModel.controllerRegistration?.instanceName || '-'; - this._instanceProperties.region = (await getAzurecoreApi()).getRegionDisplayName(reg.location); + this._instanceProperties.region = reg.region || '-'; this._instanceProperties.subscriptionId = reg.subscriptionId || '-'; this._instanceProperties.vCores = reg.vCores || ''; this._instanceProperties.host = reg.externalEndpoint || '-'; @@ -243,31 +252,34 @@ export class MiaaDashboardOverviewPage extends DashboardPage { } } - private async handleMiaaStatusUpdated(status: HybridSqlNsNameGetResponse | undefined): Promise { - this._instanceProperties.status = status?.status || '-'; + private handleMiaaStatusUpdated(): void { + this._instanceProperties.status = this._miaaModel.status; this.refreshDisplayedProperties(); } - private handleEndpointsUpdated(endpoints: EndpointModel[]): void { + private handleEndpointsUpdated(): void { + const kibanaEndpoint = this._controllerModel.getEndpoint(Endpoints.logsui); 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 = kibanaEndpoint ? `${kibanaEndpoint.endpoint}/app/kibana#/discover?_a=(query:(language:kuery,query:'${kibanaQuery}'))` : ''; this._kibanaLink.label = 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.info.name}-0`; + const grafanaEndpoint = this._controllerModel.getEndpoint(Endpoints.metricsui); + const grafanaQuery = `var-hostname=${this._miaaModel.info.name}-0`; + const grafanaUrl = grafanaEndpoint ? `${grafanaEndpoint.endpoint}/d/wZx3OUdmz/azure-sql-db-managed-instance-metrics?${grafanaQuery}` : ''; this._grafanaLink.label = grafanaUrl; this._grafanaLink.url = grafanaUrl; - this._kibanaLoading!.loading = false; - this._grafanaLoading!.loading = false; + this._kibanaLoading!.loading = !this._controllerModel.endpointsLastUpdated; + this._grafanaLoading!.loading = !this._controllerModel.endpointsLastUpdated; } - private handleDatabasesUpdated(databases: DatabaseModel[]): void { + private handleDatabasesUpdated(): void { // If we were able to get the databases it means we have a good connection so update the username too this._instanceProperties.miaaAdmin = this._miaaModel.username || this._instanceProperties.miaaAdmin; this.refreshDisplayedProperties(); - this._databasesTable.data = databases.map(d => [d.name, getDatabaseStateDisplayText(d.status)]); - this._databasesTableLoading.loading = false; + this._databasesTable.data = this._miaaModel.databases.map(d => [d.name, getDatabaseStateDisplayText(d.status)]); + this._databasesTableLoading.loading = !this._miaaModel.databasesLastUpdated; } private refreshDisplayedProperties(): void { @@ -306,7 +318,9 @@ export class MiaaDashboardOverviewPage extends DashboardPage { } ]; - this._propertiesLoading.loading = false; + this._propertiesLoading.loading = + !this._controllerModel.registrationsLastUpdated && + !this._miaaModel.statusLastUpdated && + !this._miaaModel.databasesLastUpdated; } - } diff --git a/extensions/arc/src/ui/dashboards/postgres/postgresOverviewPage.ts b/extensions/arc/src/ui/dashboards/postgres/postgresOverviewPage.ts index 8ad035a15f..8939279c05 100644 --- a/extensions/arc/src/ui/dashboards/postgres/postgresOverviewPage.ts +++ b/extensions/arc/src/ui/dashboards/postgres/postgresOverviewPage.ts @@ -6,7 +6,7 @@ import * as vscode from 'vscode'; import * as azdata from 'azdata'; import * as loc from '../../../localizedConstants'; -import { IconPathHelper, cssStyles, ResourceType } from '../../../constants'; +import { IconPathHelper, cssStyles, ResourceType, Endpoints } from '../../../constants'; import { V1Pod, DuskyObjectModelsDatabaseServiceArcPayload } from '../../../controller/generated/dusky/api'; import { DashboardPage } from '../../components/dashboardPage'; import { ControllerModel } from '../../../models/controllerModel'; @@ -311,13 +311,13 @@ export class PostgresOverviewPage extends DashboardPage { private getKibanaLink(): string { const kibanaQuery = `kubernetes_namespace:"${this._postgresModel.namespace}" and cluster_name:"${this._postgresModel.name}"`; - return `${this._controllerModel.getEndpoint('logsui')?.endpoint}/app/kibana#/discover?_a=(query:(language:kuery,query:'${kibanaQuery}'))`; + return `${this._controllerModel.getEndpoint(Endpoints.logsui)?.endpoint}/app/kibana#/discover?_a=(query:(language:kuery,query:'${kibanaQuery}'))`; } private getGrafanaLink(): string { const grafanaQuery = `var-Namespace=${this._postgresModel.namespace}&var-Name=${this._postgresModel.name}`; - return `${this._controllerModel.getEndpoint('metricsui')?.endpoint}/d/postgres-metrics?${grafanaQuery}`; + return `${this._controllerModel.getEndpoint(Endpoints.metricsui)?.endpoint}/d/postgres-metrics?${grafanaQuery}`; } private getNodes(): string[][] {