Add styling and component column type to declarative table (#8476)

* Initial wip

* wip

* Working implementation

* Make widths a bit nicer and remove sqlops addition

* Add sqlops back in

* Fix timing issue with tables

* Undo change to sql.bat and remove loading component when done
This commit is contained in:
Charles Gagnon
2019-11-27 08:06:41 -08:00
committed by GitHub
parent 0e9797c394
commit 3135b8525b
12 changed files with 648 additions and 333 deletions

View File

@@ -6,7 +6,7 @@
import * as azdata from 'azdata';
import * as nls from 'vscode-nls';
import { BdcDashboardModel } from './bdcDashboardModel';
import { BdcStatusModel, InstanceStatusModel } from '../controller/apiGenerated';
import { BdcStatusModel, InstanceStatusModel, ResourceStatusModel } from '../controller/apiGenerated';
import { getHealthStatusDisplayText, getHealthStatusIcon, getStateDisplayText, Service } from '../utils';
import { cssStyles } from '../constants';
import { isNullOrUndefined } from 'util';
@@ -15,38 +15,35 @@ import { BdcDashboardPage } from './bdcDashboardPage';
const localize = nls.loadMessageBundle();
const healthAndStatusIconColumnWidth = 25;
const healthAndStatusInstanceNameColumnWidth = 100;
const healthAndStatusStateColumnWidth = 150;
const healthAndStatusHealthColumnWidth = 100;
const metricsAndLogsInstanceNameColumnWidth = 125;
const metricsAndLogsNodeMetricsColumnWidth = 80;
const metricsAndLogsSqlMetricsColumnWidth = 80;
const metricsAndLogsLogsColumnWidth = 75;
const viewText = localize('bdc.dashboard.viewHyperlink', "View");
const notAvailableText = localize('bdc.dashboard.notAvailable', "N/A");
export class BdcDashboardResourceStatusPage extends BdcDashboardPage {
private resourceStatusModel: ResourceStatusModel;
private rootContainer: azdata.FlexContainer;
private instanceHealthStatusRowsContainer: azdata.FlexContainer;
private metricsAndLogsRowsContainer: azdata.FlexContainer;
private instanceHealthStatusTable: azdata.DeclarativeTableComponent;
private metricsAndLogsRowsTable: azdata.DeclarativeTableComponent;
private lastUpdatedLabel: azdata.TextComponent;
constructor(private model: BdcDashboardModel, private modelView: azdata.ModelView, private serviceName: string, private resourceName: string) {
super();
this.model.onDidUpdateBdcStatus(bdcStatus => this.eventuallyRunOnInitialized(() => this.handleBdcStatusUpdate(bdcStatus)));
this.rootContainer = this.createContainer(modelView);
}
public get container(): azdata.FlexContainer {
// Lazily create the container only when needed
if (!this.rootContainer) {
// We do this here so that we can have the resource model to use for populating the data
// in the tables. This is to get around a timing issue with ModelView tables
this.updateResourceStatusModel(this.model.bdcStatus);
this.createContainer();
}
return this.rootContainer;
}
private createContainer(view: azdata.ModelView): azdata.FlexContainer {
const rootContainer = view.modelBuilder.flexContainer().withLayout(
private createContainer(): void {
this.rootContainer = this.modelView.modelBuilder.flexContainer().withLayout(
{
flexFlow: 'column',
width: '100%',
@@ -57,11 +54,11 @@ export class BdcDashboardResourceStatusPage extends BdcDashboardPage {
// # INSTANCE HEALTH AND STATUS #
// ##############################
const healthStatusHeaderContainer = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'row', height: '20px' }).component();
rootContainer.addItem(healthStatusHeaderContainer, { CSSStyles: { 'padding-left': '10px', 'padding-top': '15px' } });
const healthStatusHeaderContainer = this.modelView.modelBuilder.flexContainer().withLayout({ flexFlow: 'row', height: '20px' }).component();
this.rootContainer.addItem(healthStatusHeaderContainer, { CSSStyles: { 'padding-left': '10px', 'padding-top': '15px' } });
// Header label
const healthStatusHeaderLabel = view.modelBuilder.text()
const healthStatusHeaderLabel = this.modelView.modelBuilder.text()
.withProperties<azdata.TextComponentProperties>({
value: localize('bdc.dashboard.healthStatusDetailsHeader', "Health Status Details"),
CSSStyles: { 'margin-block-start': '0px', 'margin-block-end': '10px' }
@@ -71,180 +68,306 @@ export class BdcDashboardResourceStatusPage extends BdcDashboardPage {
healthStatusHeaderContainer.addItem(healthStatusHeaderLabel, { CSSStyles: { ...cssStyles.title } });
// Last updated label
this.lastUpdatedLabel = view.modelBuilder.text()
this.lastUpdatedLabel = this.modelView.modelBuilder.text()
.withProperties({
value: localize('bdc.dashboard.lastUpdated', "Last Updated : {0}", '-'),
value: this.getLastUpdatedText(),
CSSStyles: { ...cssStyles.lastUpdatedText }
}).component();
healthStatusHeaderContainer.addItem(this.lastUpdatedLabel, { CSSStyles: { 'margin-left': '45px' } });
// Header row
const instanceHealthStatusHeaderRow = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'row' }).component();
const instanceHealthAndStatusNameHeader = view.modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: localize('bdc.dashboard.instanceHeader', "Instance") }).component();
// Instance name cell covers both icon + service name so width stretches both cells
instanceHealthStatusHeaderRow.addItem(instanceHealthAndStatusNameHeader, { CSSStyles: { 'width': `${healthAndStatusIconColumnWidth + healthAndStatusInstanceNameColumnWidth}px`, 'min-width': `${healthAndStatusIconColumnWidth + healthAndStatusInstanceNameColumnWidth}px`, ...cssStyles.tableHeader } });
const instanceHealthAndStatusState = view.modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: localize('bdc.dashboard.stateHeader', "State") }).component();
instanceHealthStatusHeaderRow.addItem(instanceHealthAndStatusState, { CSSStyles: { 'width': `${healthAndStatusStateColumnWidth}px`, 'min-width': `${healthAndStatusStateColumnWidth}px`, ...cssStyles.tableHeader } });
const instanceHealthAndStatusHealthStatus = view.modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: localize('bdc.dashboard.healthStatusHeader', "Health Status") }).component();
instanceHealthStatusHeaderRow.addItem(instanceHealthAndStatusHealthStatus, { CSSStyles: { 'width': `${healthAndStatusHealthColumnWidth}px`, 'min-width': `${healthAndStatusHealthColumnWidth}px`, ...cssStyles.tableHeader } });
rootContainer.addItem(instanceHealthStatusHeaderRow, { flex: '0 0 auto', CSSStyles: { 'padding-left': '10px', 'box-sizing': 'border-box' } });
this.instanceHealthStatusRowsContainer = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'column' }).component();
rootContainer.addItem(this.instanceHealthStatusRowsContainer, { flex: '0 0 auto' });
this.instanceHealthStatusTable = this.modelView.modelBuilder.declarativeTable()
.withProperties<azdata.DeclarativeTableProperties>(
{
columns: [
{ // status icon
displayName: '',
valueType: azdata.DeclarativeDataType.component,
isReadOnly: true,
width: 25,
headerCssStyles: {
'border': 'none',
'background-color': '#FFFFFF'
},
rowCssStyles: {
'border-top': 'solid 1px #ccc',
'border-bottom': 'solid 1px #ccc',
'border-left': 'none',
'border-right': 'none'
},
},
{ // instance
displayName: localize('bdc.dashboard.instanceHeader', "Instance"),
valueType: azdata.DeclarativeDataType.string,
isReadOnly: true,
width: 100,
headerCssStyles: {
'border': 'none',
'background-color': '#FFFFFF',
...cssStyles.tableHeader
},
rowCssStyles: {
'border-top': 'solid 1px #ccc',
'border-bottom': 'solid 1px #ccc',
'border-left': 'none',
'border-right': 'none'
},
},
{ // state
displayName: localize('bdc.dashboard.stateHeader', "State"),
valueType: azdata.DeclarativeDataType.string,
isReadOnly: true,
width: 150,
headerCssStyles: {
'border': 'none',
'background-color': '#FFFFFF',
...cssStyles.tableHeader
},
rowCssStyles: {
'border-top': 'solid 1px #ccc',
'border-bottom': 'solid 1px #ccc',
'border-left': 'none',
'border-right': 'none'
},
},
{ // health status
displayName: localize('bdc.dashboard.healthStatusHeader', "Health Status"),
valueType: azdata.DeclarativeDataType.string,
isReadOnly: true,
width: 100,
headerCssStyles: {
'border': 'none',
'background-color': '#FFFFFF',
'text-align': 'left',
...cssStyles.tableHeader
},
rowCssStyles: {
'border-top': 'solid 1px #ccc',
'border-bottom': 'solid 1px #ccc',
'border-left': 'none',
'border-right': 'none'
}
},
{ // view details button
displayName: '',
valueType: azdata.DeclarativeDataType.component,
isReadOnly: true,
width: 150,
headerCssStyles: {
'border': 'none',
'background-color': '#FFFFFF'
},
rowCssStyles: {
'border-top': 'solid 1px #ccc',
'border-bottom': 'solid 1px #ccc',
'border-left': 'none',
'border-right': 'none'
},
},
],
data: this.createHealthStatusRows()
}).component();
this.rootContainer.addItem(this.instanceHealthStatusTable, { flex: '0 0 auto' });
// ####################
// # METRICS AND LOGS #
// ####################
// Title label
const endpointsLabel = view.modelBuilder.text()
const endpointsLabel = this.modelView.modelBuilder.text()
.withProperties<azdata.TextComponentProperties>({ value: localize('bdc.dashboard.metricsAndLogsLabel', "Metrics and Logs"), CSSStyles: { 'margin-block-start': '20px', 'margin-block-end': '0px' } })
.component();
rootContainer.addItem(endpointsLabel, { CSSStyles: { 'padding-left': '10px', ...cssStyles.title } });
this.rootContainer.addItem(endpointsLabel, { CSSStyles: { 'padding-left': '10px', ...cssStyles.title } });
let metricsAndLogsColumns: azdata.DeclarativeTableColumn[] =
[
{ // instance
displayName: localize('bdc.dashboard.instanceHeader', "Instance"),
valueType: azdata.DeclarativeDataType.string,
isReadOnly: true,
width: 125,
headerCssStyles: {
'border': 'none',
'background-color': '#FFFFFF',
...cssStyles.tableHeader
},
rowCssStyles: {
'border-top': 'solid 1px #ccc',
'border-bottom': 'solid 1px #ccc',
'border-left': 'none',
'border-right': 'none'
},
},
{ // node metrics
displayName: localize('bdc.dashboard.nodeMetricsHeader', "Node Metrics"),
valueType: azdata.DeclarativeDataType.component,
isReadOnly: true,
width: 100,
headerCssStyles: {
'border': 'none',
'background-color': '#FFFFFF',
...cssStyles.tableHeader
},
rowCssStyles: {
'border-top': 'solid 1px #ccc',
'border-bottom': 'solid 1px #ccc',
'border-left': 'none',
'border-right': 'none'
},
}
];
// Header row
const metricsAndLogsHeaderRow = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'row' }).component();
const nameCell = view.modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: localize('bdc.dashboard.instanceHeader', "Instance") }).component();
metricsAndLogsHeaderRow.addItem(nameCell, { CSSStyles: { 'width': `${metricsAndLogsInstanceNameColumnWidth}px`, 'min-width': `${metricsAndLogsInstanceNameColumnWidth}px`, ...cssStyles.tableHeader } });
const nodeMetricsCell = view.modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: localize('bdc.dashboard.nodeMetricsHeader', "Node Metrics") }).component();
metricsAndLogsHeaderRow.addItem(nodeMetricsCell, { CSSStyles: { 'width': `${metricsAndLogsNodeMetricsColumnWidth}px`, 'min-width': `${metricsAndLogsNodeMetricsColumnWidth}px`, ...cssStyles.tableHeader } });
// Only show SQL metrics column for SQL resource instances
if (this.serviceName.toLowerCase() === Service.sql) {
const sqlMetricsCell = view.modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: localize('bdc.dashboard.sqlMetricsHeader', "SQL Metrics") }).component();
metricsAndLogsHeaderRow.addItem(sqlMetricsCell, { CSSStyles: { 'width': `${metricsAndLogsSqlMetricsColumnWidth}px`, 'min-width': `${metricsAndLogsSqlMetricsColumnWidth}px`, ...cssStyles.tableHeader } });
metricsAndLogsColumns.push(
{ // sql metrics
displayName: localize('bdc.dashboard.sqlMetricsHeader', "SQL Metrics"),
valueType: azdata.DeclarativeDataType.component,
isReadOnly: true,
width: 100,
headerCssStyles: {
'border': 'none',
'background-color': '#FFFFFF',
'text-align': 'left',
...cssStyles.tableHeader
},
rowCssStyles: {
'border-top': 'solid 1px #ccc',
'border-bottom': 'solid 1px #ccc',
'border-left': 'none',
'border-right': 'none'
}
});
}
const healthStatusCell = view.modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: localize('bdc.dashboard.logsHeader', "Logs") }).component();
metricsAndLogsHeaderRow.addItem(healthStatusCell, { CSSStyles: { 'width': `${metricsAndLogsLogsColumnWidth}px`, 'min-width': `${metricsAndLogsLogsColumnWidth}px`, ...cssStyles.tableHeader } });
rootContainer.addItem(metricsAndLogsHeaderRow, { flex: '0 0 auto', CSSStyles: { 'padding-left': '10px', 'box-sizing': 'border-box' } });
this.metricsAndLogsRowsContainer = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'column' }).component();
rootContainer.addItem(this.metricsAndLogsRowsContainer, { flex: '0 0 auto' });
metricsAndLogsColumns.push(
{ // logs
displayName: localize('bdc.dashboard.logsHeader', "Logs"),
valueType: azdata.DeclarativeDataType.component,
isReadOnly: true,
width: 100,
headerCssStyles: {
'border': 'none',
'background-color': '#FFFFFF',
'text-align': 'left',
...cssStyles.tableHeader
},
rowCssStyles: {
'border-top': 'solid 1px #ccc',
'border-bottom': 'solid 1px #ccc',
'border-left': 'none',
'border-right': 'none'
},
});
this.metricsAndLogsRowsTable = this.modelView.modelBuilder.declarativeTable()
.withProperties<azdata.DeclarativeTableProperties>(
{
columns: metricsAndLogsColumns,
data: this.createMetricsAndLogsRows()
}).component();
this.rootContainer.addItem(this.metricsAndLogsRowsTable, { flex: '0 0 auto' });
this.initialized = true;
this.handleBdcStatusUpdate(this.model.bdcStatus);
return rootContainer;
}
private handleBdcStatusUpdate(bdcStatus?: BdcStatusModel): void {
private updateResourceStatusModel(bdcStatus?: BdcStatusModel): void {
// If we can't find the resource model for this resource then just
// default to keeping what we had originally
if (!bdcStatus) {
return;
}
const service = bdcStatus.services ? bdcStatus.services.find(s => s.serviceName === this.serviceName) : undefined;
const resource = service ? service.resources.find(r => r.resourceName === this.resourceName) : undefined;
this.resourceStatusModel = service ? service.resources.find(r => r.resourceName === this.resourceName) : this.resourceStatusModel;
}
if (!resource || isNullOrUndefined(resource.instances)) {
private handleBdcStatusUpdate(bdcStatus?: BdcStatusModel): void {
this.updateResourceStatusModel(bdcStatus);
if (!this.resourceStatusModel || isNullOrUndefined(this.resourceStatusModel.instances)) {
return;
}
this.lastUpdatedLabel.value =
localize('bdc.dashboard.lastUpdated', "Last Updated : {0}",
this.model.bdcStatusLastUpdated ?
`${this.model.bdcStatusLastUpdated.toLocaleDateString()} ${this.model.bdcStatusLastUpdated.toLocaleTimeString()}`
: '-');
this.lastUpdatedLabel.value = this.getLastUpdatedText();
this.instanceHealthStatusRowsContainer.clearItems();
this.metricsAndLogsRowsContainer.clearItems();
this.instanceHealthStatusTable.data = this.createHealthStatusRows();
resource.instances.forEach(i => {
const instanceHealthStatusRow = createInstanceHealthStatusRow(this.modelView.modelBuilder, i);
this.instanceHealthStatusRowsContainer.addItem(instanceHealthStatusRow, { CSSStyles: { 'padding-left': '10px', 'border-top': 'solid 1px #ccc', 'box-sizing': 'border-box', 'user-select': 'text' } });
let metricsAndLogsRow = createMetricsAndLogsRow(this.modelView.modelBuilder, i, this.serviceName);
this.metricsAndLogsRowsContainer.addItem(metricsAndLogsRow, { CSSStyles: { 'padding-left': '10px', 'border-top': 'solid 1px #ccc', 'box-sizing': 'border-box', 'user-select': 'text' } });
});
}
}
/**
* Creates a row with the name, state and health status for a particular instance on this resource
*
* @param modelBuilder The builder used to create the component
* @param instanceStatus The status object for the instance this row is for
*/
function createInstanceHealthStatusRow(modelBuilder: azdata.ModelBuilder, instanceStatus: InstanceStatusModel): azdata.FlexContainer {
const instanceHealthStatusRow = modelBuilder.flexContainer().withLayout({ flexFlow: 'row', alignItems: 'center', height: '30px' }).component();
const statusIconCell = modelBuilder.text()
.withProperties<azdata.TextComponentProperties>({
value: getHealthStatusIcon(instanceStatus.healthStatus),
ariaRole: 'img',
title: getHealthStatusDisplayText(instanceStatus.healthStatus),
CSSStyles: { 'user-select': 'none' }
}).component();
instanceHealthStatusRow.addItem(statusIconCell, { CSSStyles: { 'width': `${healthAndStatusIconColumnWidth}px`, 'min-width': `${healthAndStatusIconColumnWidth}px` } });
const nameCell = modelBuilder.text().withProperties({ value: instanceStatus.instanceName, CSSStyles: { ...cssStyles.text } }).component();
instanceHealthStatusRow.addItem(nameCell, { CSSStyles: { 'width': `${healthAndStatusInstanceNameColumnWidth}px`, 'min-width': `${healthAndStatusInstanceNameColumnWidth}px`, ...cssStyles.text } });
const stateText = getStateDisplayText(instanceStatus.state);
const stateCell = modelBuilder.text().withProperties({ value: stateText, title: stateText, CSSStyles: { ...cssStyles.overflowEllipsisText } }).component();
instanceHealthStatusRow.addItem(stateCell, { CSSStyles: { 'width': `${healthAndStatusStateColumnWidth}px`, 'min-width': `${healthAndStatusStateColumnWidth}px` } });
const healthStatusText = getHealthStatusDisplayText(instanceStatus.healthStatus);
const healthStatusCell = modelBuilder.text().withProperties({ value: healthStatusText, title: healthStatusText, CSSStyles: { ...cssStyles.overflowEllipsisText } }).component();
instanceHealthStatusRow.addItem(healthStatusCell, { CSSStyles: { 'width': `${healthAndStatusHealthColumnWidth}px`, 'min-width': `${healthAndStatusHealthColumnWidth}px` } });
if (instanceStatus.healthStatus !== 'healthy' && instanceStatus.details && instanceStatus.details.length > 0) {
instanceHealthStatusRow.addItem(createViewDetailsButton(modelBuilder, instanceStatus.details), { flex: '0 0 auto' });
}
return instanceHealthStatusRow;
}
/**
* Creates a row with the name, link to the metrics and a link to the logs for a particular instance on this resource
* @param modelBuilder The builder used to create the component
* @param instanceStatus The status object for the instance this row is for
* @param serviceName The name of the service this resource instance belongs to
*/
function createMetricsAndLogsRow(modelBuilder: azdata.ModelBuilder, instanceStatus: InstanceStatusModel, serviceName: string): azdata.FlexContainer {
const metricsAndLogsRow = modelBuilder.flexContainer().withLayout({ flexFlow: 'row', alignItems: 'center', height: '30px' }).component();
const nameCell = modelBuilder.text().withProperties({ value: instanceStatus.instanceName, CSSStyles: { ...cssStyles.text } }).component();
metricsAndLogsRow.addItem(nameCell, { CSSStyles: { 'width': `${metricsAndLogsInstanceNameColumnWidth}px`, 'min-width': `${metricsAndLogsInstanceNameColumnWidth}px`, ...cssStyles.text } });
// Not all instances have all logs available - in that case just display N/A instead of a link
if (isNullOrUndefined(instanceStatus.dashboards) || isNullOrUndefined(instanceStatus.dashboards.nodeMetricsUrl)) {
const metricsCell = modelBuilder.text().withProperties({ value: notAvailableText, CSSStyles: { ...cssStyles.text } }).component();
metricsAndLogsRow.addItem(metricsCell, { CSSStyles: { 'width': `${metricsAndLogsNodeMetricsColumnWidth}px`, 'min-width': `${metricsAndLogsNodeMetricsColumnWidth}px`, ...cssStyles.text } });
} else {
const nodeMetricsCell = modelBuilder.hyperlink().withProperties<azdata.HyperlinkComponentProperties>({
label: viewText,
url: instanceStatus.dashboards.nodeMetricsUrl,
title: instanceStatus.dashboards.nodeMetricsUrl,
CSSStyles: { ...cssStyles.text, ...cssStyles.hyperlink }
})
.component();
metricsAndLogsRow.addItem(nodeMetricsCell, { CSSStyles: { 'width': `${metricsAndLogsNodeMetricsColumnWidth}px`, 'min-width': `${metricsAndLogsNodeMetricsColumnWidth}px` } });
this.metricsAndLogsRowsTable.data = this.createMetricsAndLogsRows();
}
// Only show SQL metrics column for SQL resource instances
if (serviceName === Service.sql) {
private createMetricsAndLogsRows(): any[][] {
return this.resourceStatusModel ? this.resourceStatusModel.instances.map(instanceStatus => this.createMetricsAndLogsRow(instanceStatus)) : [];
}
private createHealthStatusRows(): any[][] {
return this.resourceStatusModel ? this.resourceStatusModel.instances.map(instanceStatus => this.createHealthStatusRow(instanceStatus)) : [];
}
private createMetricsAndLogsRow(instanceStatus: InstanceStatusModel): any[] {
const row: any[] = [instanceStatus.instanceName];
// Not all instances have all logs available - in that case just display N/A instead of a link
if (isNullOrUndefined(instanceStatus.dashboards) || isNullOrUndefined(instanceStatus.dashboards.sqlMetricsUrl)) {
const metricsCell = modelBuilder.text().withProperties({ value: notAvailableText, CSSStyles: { ...cssStyles.text } }).component();
metricsAndLogsRow.addItem(metricsCell, { CSSStyles: { 'width': `${metricsAndLogsSqlMetricsColumnWidth}px`, 'min-width': `${metricsAndLogsSqlMetricsColumnWidth}px`, ...cssStyles.text } });
if (isNullOrUndefined(instanceStatus.dashboards) || isNullOrUndefined(instanceStatus.dashboards.nodeMetricsUrl)) {
row.push(this.modelView.modelBuilder.text().withProperties({ value: notAvailableText, CSSStyles: { ...cssStyles.text } }).component());
} else {
const sqlMetricsCell = modelBuilder.hyperlink().withProperties<azdata.HyperlinkComponentProperties>({
row.push(this.modelView.modelBuilder.hyperlink().withProperties<azdata.HyperlinkComponentProperties>({
label: viewText,
url: instanceStatus.dashboards.sqlMetricsUrl,
title: instanceStatus.dashboards.sqlMetricsUrl,
url: instanceStatus.dashboards.nodeMetricsUrl,
title: instanceStatus.dashboards.nodeMetricsUrl,
CSSStyles: { ...cssStyles.text, ...cssStyles.hyperlink }
})
.component();
metricsAndLogsRow.addItem(sqlMetricsCell, { CSSStyles: { 'width': `${metricsAndLogsSqlMetricsColumnWidth}px`, 'min-width': `${metricsAndLogsSqlMetricsColumnWidth}px` } });
}).component());
}
// Only show SQL metrics column for SQL resource instances
if (this.serviceName === Service.sql) {
// Not all instances have all logs available - in that case just display N/A instead of a link
if (isNullOrUndefined(instanceStatus.dashboards) || isNullOrUndefined(instanceStatus.dashboards.sqlMetricsUrl)) {
row.push(this.modelView.modelBuilder.text().withProperties({ value: notAvailableText, CSSStyles: { ...cssStyles.text } }).component());
} else {
row.push(this.modelView.modelBuilder.hyperlink().withProperties<azdata.HyperlinkComponentProperties>({
label: viewText,
url: instanceStatus.dashboards.sqlMetricsUrl,
title: instanceStatus.dashboards.sqlMetricsUrl,
CSSStyles: { ...cssStyles.text, ...cssStyles.hyperlink }
}).component());
}
}
if (isNullOrUndefined(instanceStatus.dashboards) || isNullOrUndefined(instanceStatus.dashboards.logsUrl)) {
row.push(this.modelView.modelBuilder.text().withProperties({ value: notAvailableText, CSSStyles: { ...cssStyles.text } }).component());
} else {
row.push(this.modelView.modelBuilder.hyperlink().withProperties<azdata.HyperlinkComponentProperties>({
label: viewText,
url: instanceStatus.dashboards.logsUrl,
title: instanceStatus.dashboards.logsUrl,
CSSStyles: { ...cssStyles.text, ...cssStyles.hyperlink }
}).component());
}
return row;
}
if (isNullOrUndefined(instanceStatus.dashboards) || isNullOrUndefined(instanceStatus.dashboards.logsUrl)) {
const logsCell = modelBuilder.text().withProperties({ value: notAvailableText, CSSStyles: { ...cssStyles.text } }).component();
metricsAndLogsRow.addItem(logsCell, { CSSStyles: { 'width': `${metricsAndLogsLogsColumnWidth}px`, 'min-width': `${metricsAndLogsLogsColumnWidth}px`, ...cssStyles.text } });
} else {
const logsCell = modelBuilder.hyperlink().withProperties<azdata.HyperlinkComponentProperties>({
label: viewText,
url: instanceStatus.dashboards.logsUrl,
title: instanceStatus.dashboards.logsUrl,
CSSStyles: { ...cssStyles.text, ...cssStyles.hyperlink }
})
.component();
metricsAndLogsRow.addItem(logsCell, { CSSStyles: { 'width': `${metricsAndLogsLogsColumnWidth}px`, 'min-width': `${metricsAndLogsLogsColumnWidth}px` } });
private createHealthStatusRow(instanceStatus: InstanceStatusModel): any[] {
const statusIconCell = this.modelView.modelBuilder.text()
.withProperties<azdata.TextComponentProperties>({
value: getHealthStatusIcon(instanceStatus.healthStatus),
ariaRole: 'img',
title: getHealthStatusDisplayText(instanceStatus.healthStatus),
CSSStyles: { 'user-select': 'none', ...cssStyles.text }
}).component();
const viewDetailsButton = instanceStatus.healthStatus !== 'healthy' && instanceStatus.details && instanceStatus.details.length > 0 ? createViewDetailsButton(this.modelView.modelBuilder, instanceStatus.details) : undefined;
return [
statusIconCell,
instanceStatus.instanceName,
getStateDisplayText(instanceStatus.state),
getHealthStatusDisplayText(instanceStatus.healthStatus),
viewDetailsButton];
}
return metricsAndLogsRow;
private getLastUpdatedText(): string {
return localize('bdc.dashboard.lastUpdated', "Last Updated : {0}",
this.model.bdcStatusLastUpdated ?
`${this.model.bdcStatusLastUpdated.toLocaleDateString()} ${this.model.bdcStatusLastUpdated.toLocaleTimeString()}`
: '-');
}
}