diff --git a/extensions/mssql/package.json b/extensions/mssql/package.json index 595d8460ad..fd97dd1bdd 100644 --- a/extensions/mssql/package.json +++ b/extensions/mssql/package.json @@ -400,16 +400,26 @@ "name": "%title.tasks%", "row": 0, "col": 0, - "colspan": 2, + "colspan": 1, "widget": { "tasks-widget": [ "mssqlCluster.task.newNotebook", "mssqlCluster.task.openNotebook", - "mssqlCluster.livy.task.submitSparkJob", - "mssqlCluster.livy.task.openSparkHistory", - "mssqlCluster.livy.task.openYarnHistory" + "mssqlCluster.livy.task.submitSparkJob" ] } + }, + { + "name": "%title.endpoints%", + "row": 0, + "col": 2, + "rowspan": 1.5, + "colspan": 2, + "widget": { + "modelview": { + "id": "bdc-endpoints" + } + } } ] } diff --git a/extensions/mssql/package.nls.json b/extensions/mssql/package.nls.json index 894bd4ede0..cf22c45790 100644 --- a/extensions/mssql/package.nls.json +++ b/extensions/mssql/package.nls.json @@ -29,6 +29,8 @@ "title.searchServers": "Search: Servers", "title.clearSearchServerResult": "Search: Clear Search Server Results", + "title.endpoints": "Service Endpoints", + "mssql.configuration.title": "MSSQL configuration", "mssql.query.displayBitAsNumber": "Should BIT columns be displayed as numbers (1 or 0)? If false, BIT columns will be displayed as 'true' or 'false'", "mssql.format.alignColumnDefinitionsInColumns": "Should column definitions be aligned?", diff --git a/extensions/mssql/src/main.ts b/extensions/mssql/src/main.ts index c6d15bad12..5c4797a347 100644 --- a/extensions/mssql/src/main.ts +++ b/extensions/mssql/src/main.ts @@ -134,6 +134,59 @@ export async function activate(context: vscode.ExtensionContext): Promise languageClient.stop() }); + azdata.ui.registerModelViewProvider('bdc-endpoints', async (view) => { + + const endpointsArray: Array = Object.assign([], view.serverInfo.options['clusterEndpoints']); + if (endpointsArray.length > 0) { + const managementProxyEp = endpointsArray.find(e => e.serviceName === 'management-proxy'); + if (managementProxyEp) { + endpointsArray.push(getCustomEndpoint(managementProxyEp, 'Grafana Dashboard', '/grafana')); + endpointsArray.push(getCustomEndpoint(managementProxyEp, 'Kibana Dashboard', '/kibana')); + } + + const gatewayEp = endpointsArray.find(e => e.serviceName === 'gateway'); + if (gatewayEp) { + endpointsArray.push(getCustomEndpoint(gatewayEp, 'Spark History', '/gateway/default/sparkhistory')); + endpointsArray.push(getCustomEndpoint(gatewayEp, 'Yarn History', '/gateway/default/yarn')); + } + + const container = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'column', width: '100%', height: '100%', alignItems: 'left' }).component(); + endpointsArray.forEach(endpointInfo => { + const endPointRow = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'row' }).component(); + const nameCell = view.modelBuilder.text().withProperties({ value: endpointInfo.serviceName }).component(); + endPointRow.addItem(nameCell, { CSSStyles: { 'width': '30%', 'font-weight': '600' } }); + if (endpointInfo.isHyperlink) { + const linkCell = view.modelBuilder.hyperlink().withProperties({ label: endpointInfo.hyperlink, url: endpointInfo.hyperlink, position: '' }).component(); + endPointRow.addItem(linkCell, { CSSStyles: { 'width': '70%', 'color': 'blue', 'text-decoration': 'underline', 'padding-top': '10px' } }); + } + else { + const endpointCell = view.modelBuilder.text().withProperties({ value: endpointInfo.ipAddress + ':' + endpointInfo.port }).component(); + endPointRow.addItem(endpointCell, { CSSStyles: { 'width': '70%' } }); + } + container.addItem(endPointRow, { CSSStyles: { 'padding-left': '10px', 'border-top': 'solid 1px #ccc', 'box-sizing': 'border-box' } }); + }); + const endpointsContainer = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'column', width: '100%', height: '100%', alignItems: 'left' }).component(); + endpointsContainer.addItem(container, { CSSStyles: { 'padding-top': '25px' } }); + + await view.initializeModel(endpointsContainer); + } + + }); + + function getCustomEndpoint(parentEndpoint: Utils.IEndpoint, serviceName: string, serviceUrl?: string): Utils.IEndpoint { + if (parentEndpoint) { + let endpoint: Utils.IEndpoint = { + serviceName: serviceName, + ipAddress: parentEndpoint.ipAddress, + port: parentEndpoint.port, + isHyperlink: serviceUrl ? true : false, + hyperlink: 'https://' + parentEndpoint.ipAddress + ':' + parentEndpoint.port + serviceUrl + }; + return endpoint; + } + return null; + } + let api: MssqlExtensionApi = { getMssqlObjectExplorerBrowser(): MssqlObjectExplorerBrowser { return { diff --git a/extensions/mssql/src/utils.ts b/extensions/mssql/src/utils.ts index 1dc0dd3107..4eb20e0e2b 100644 --- a/extensions/mssql/src/utils.ts +++ b/extensions/mssql/src/utils.ts @@ -213,7 +213,9 @@ export async function getClusterEndpoint(profileId: string, serviceName: string) let clusterEndpoint: IEndpoint = { serviceName: endpoints[index].serviceName, ipAddress: endpoints[index].ipAddress, - port: endpoints[index].port + port: endpoints[index].port, + isHyperlink: false, + hyperlink: null }; return clusterEndpoint; } @@ -222,6 +224,8 @@ export interface IEndpoint { serviceName: string; ipAddress: string; port: number; + isHyperlink: boolean; + hyperlink: string; } export function isValidNumber(maybeNumber: any) { diff --git a/src/sql/workbench/parts/dashboard/containers/dashboardGridContainer.component.html b/src/sql/workbench/parts/dashboard/containers/dashboardGridContainer.component.html index 424f01c43c..5e4ce3cb68 100644 --- a/src/sql/workbench/parts/dashboard/containers/dashboardGridContainer.component.html +++ b/src/sql/workbench/parts/dashboard/containers/dashboardGridContainer.component.html @@ -20,6 +20,9 @@ + + + diff --git a/src/sql/workbench/parts/dashboard/containers/dashboardGridContainer.component.ts b/src/sql/workbench/parts/dashboard/containers/dashboardGridContainer.component.ts index a38fd3d625..8603bf57ba 100644 --- a/src/sql/workbench/parts/dashboard/containers/dashboardGridContainer.component.ts +++ b/src/sql/workbench/parts/dashboard/containers/dashboardGridContainer.component.ts @@ -32,6 +32,13 @@ export interface GridWebviewConfig extends GridCellConfig { id?: string; }; } +export interface GridModelViewConfig extends GridCellConfig { + widget: { + modelview: { + id?: string; + } + }; +} @Component({ selector: 'dashboard-grid-container', @@ -78,6 +85,17 @@ export class DashboardGridContainer extends DashboardTab implements OnDestroy { return undefined; } + protected getModelViewContent(row: number, col: number): GridModelViewConfig { + const content = this.getContent(row, col); + if (content) { + const modelviewConfig = content; + if (modelviewConfig && modelviewConfig.widget.modelview) { + return modelviewConfig; + } + } + return undefined; + } + protected isWidget(row: number, col: number): boolean { const widgetConfig = this.getWidgetContent(row, col); @@ -97,6 +115,18 @@ export class DashboardGridContainer extends DashboardTab implements OnDestroy { return undefined; } + protected isModelView(row: number, col: number): boolean { + const modelView = this.getModelViewContent(row, col); + return modelView !== undefined; + } + + protected getModelViewId(row: number, col: number): string { + const widgetConfig = this.getModelViewContent(row, col); + if (widgetConfig && widgetConfig.widget.modelview) { + return widgetConfig.widget.modelview.id; + } + return undefined; + } protected getColspan(row: number, col: number): string { const content = this.getContent(row, col); let colspan: string = '1'; diff --git a/src/sql/workbench/parts/dashboard/dashboard.module.ts b/src/sql/workbench/parts/dashboard/dashboard.module.ts index b36d8da9be..7a8ca09654 100644 --- a/src/sql/workbench/parts/dashboard/dashboard.module.ts +++ b/src/sql/workbench/parts/dashboard/dashboard.module.ts @@ -99,9 +99,6 @@ const widgetComponents = [ /* Insights */ const insightComponents = Registry.as(Extensions.InsightContribution).getAllCtors(); -/* Model-backed components */ -const extensionComponents = Registry.as(ComponentExtensions.ComponentContribution).getAllCtors(); - // Setup routes for various child components const appRoutes: Routes = [ { path: 'database-dashboard', component: DatabaseDashboardPage }, @@ -116,6 +113,10 @@ const appRoutes: Routes = [ // Connection Dashboard main angular module export const DashboardModule = (params, selector: string, instantiationService: IInstantiationService): any => { + + /* Model-backed components */ + const extensionComponents = Registry.as(ComponentExtensions.ComponentContribution).getAllCtors(); + @NgModule({ declarations: [ ...baseComponents,