sample code to implement providers (#15929)

* sample code to implement providers

* context menu

* comments

* more comments

* move comments to README
This commit is contained in:
Alan Ren
2021-08-20 14:19:50 -07:00
committed by GitHub
parent ccaf111696
commit 1d7accadbf
7 changed files with 404 additions and 3 deletions

View File

@@ -1,4 +1,4 @@
This is a sample extension that will show some basic model-backed UI scenarios. The long-term goal is to use SQL Service querying (e.g. see if Agent and other services are running) and visualize in interesting ways. Additional suggestions for improving this sample are welcome.
This is a sample extension that will show some basic model-backed UI scenarios and how to contribute feature providers(e.g. Connection, Object Explorer) in ADS. Note: only implement the providers this way if your data service has native JavaScript SDK available, otherwise use [data protocol client](https://github.com/microsoft/sqlops-dataprotocolclient), please refer to [SQL Tools Service] (https://github.com/microsoft/sqltoolsservice) or [PG Tools Service](https://github.com/microsoft/pgtoolsservice) as examples.
## Run the following commands to produce an extension installation package
@@ -11,4 +11,4 @@ This is a sample extension that will show some basic model-backed UI scenarios.
- `yarn build` - to build the code
- Launch VSCode and open the azuredatastudio's code folder, run the 'Launch azuredatastudio' debug option (to work around the issue. The next step won't work without doing this first)
- Launch VSCode and open this folder, run the 'Debug in enlistment'
- Once ADS launches, you should be able to run the sqlservices commands, for example: sqlservices.openDialog
- Once ADS launches, you should be able to run the sqlservices commands, for example: sqlservices.openDialog

View File

@@ -82,7 +82,122 @@
]
}
}
]
],
"connectionProvider": {
"providerId": "TESTPROVIDER",
"languageMode": "sql",
"notebookKernelAlias": "Test Provider",
"displayName": "Test Provider",
"iconPath": [
{
"id": "myprovidericon",
"path": {
"light": "images/user.svg",
"dark": "images/user_inverse.svg"
},
"default": true
}
],
"connectionOptions": [
{
"specialValueType": "connectionName",
"isIdentity": true,
"name": "connectionName",
"displayName": "",
"description": "",
"groupName": "Source",
"valueType": "string",
"defaultValue": null,
"objectType": null,
"categoryValues": null,
"isRequired": false,
"isArray": false
},
{
"specialValueType": "serverName",
"isIdentity": true,
"name": "server",
"displayName": "Server name",
"description": "Server name",
"groupName": "Source",
"valueType": "string",
"defaultValue": null,
"objectType": null,
"categoryValues": null,
"isRequired": true,
"isArray": false
},
{
"specialValueType": "authType",
"isIdentity": true,
"name": "authenticationType",
"displayName": "Authentication type",
"description": "",
"groupName": "Security",
"valueType": "category",
"defaultValue": "SqlLogin",
"objectType": null,
"categoryValues": [
{
"displayName": "Basic",
"name": "SqlLogin"
}
],
"isRequired": true,
"isArray": false
},
{
"specialValueType": "userName",
"isIdentity": true,
"name": "user",
"displayName": "Username",
"description": "",
"groupName": "Security",
"valueType": "string",
"defaultValue": null,
"objectType": null,
"categoryValues": null,
"isRequired": true,
"isArray": false
},
{
"specialValueType": "password",
"isIdentity": true,
"name": "password",
"displayName": "Password",
"description": "",
"groupName": "Security",
"valueType": "password",
"defaultValue": null,
"objectType": null,
"categoryValues": null,
"isRequired": true,
"isArray": false
},
{
"specialValueType": "appName",
"isIdentity": false,
"name": "applicationName",
"displayName": "Application Name",
"description": "",
"groupName": "Context",
"valueType": "string",
"defaultValue": null,
"objectType": null,
"categoryValues": null,
"isRequired": false,
"isArray": false
}
]
},
"menus": {
"objectExplorer/item/context": [
{
"command": "sqlservices.openDialog",
"when": "nodeType =~ /^(Database|Server)$/ && connectionProvider == TESTPROVIDER"
}
]
}
},
"scripts": {
"build": "gulp build",

View File

@@ -0,0 +1,18 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
import { ProviderId } from './connectionProvider';
const IconId = 'myprovidericon';
/**
* This class implements the IconProvider interface that allows users to contribute icons for the root tree node instead of using the generic server icon.
*/
export class IconProvider implements azdata.IconProvider {
public readonly providerId: string = ProviderId;
public handle?: number;
getConnectionIconId(connection: azdata.IConnectionProfile, serverInfo: azdata.ServerInfo): Thenable<string | undefined> {
return Promise.resolve(IconId);
}
}

View File

@@ -0,0 +1,86 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
import * as vscode from 'vscode';
export const ProviderId: string = 'TESTPROVIDER';
/**
* This class implements the ConnectionProvider interface that allows users to connect to the data services, this will be used by various features in ADS, e.g. connection dialog and query editor.
*/
export class ConnectionProvider implements azdata.ConnectionProvider {
private onConnectionCompleteEmitter: vscode.EventEmitter<azdata.ConnectionInfoSummary> = new vscode.EventEmitter();
onConnectionComplete: vscode.Event<azdata.ConnectionInfoSummary> = this.onConnectionCompleteEmitter.event;
private onIntelliSenseCacheCompleteEmitter: vscode.EventEmitter<string> = new vscode.EventEmitter();
onIntelliSenseCacheComplete: vscode.Event<string> = this.onIntelliSenseCacheCompleteEmitter.event;
private onConnectionChangedEmitter: vscode.EventEmitter<azdata.ChangedConnectionInfo> = new vscode.EventEmitter();
onConnectionChanged: vscode.Event<azdata.ChangedConnectionInfo> = this.onConnectionChangedEmitter.event;
connect(connectionUri: string, connectionInfo: azdata.ConnectionInfo): Promise<boolean> {
this.onConnectionCompleteEmitter.fire({
connectionId: '123',
ownerUri: connectionUri,
messages: '',
errorMessage: '',
errorNumber: 0,
connectionSummary: {
serverName: '',
userName: ''
},
serverInfo: {
serverReleaseVersion: 1,
engineEditionId: 1,
serverVersion: '1.0',
serverLevel: '',
serverEdition: '',
isCloud: true,
azureVersion: 1,
osVersion: '',
options: {}
}
});
return Promise.resolve(true);
}
disconnect(connectionUri: string): Promise<boolean> {
return Promise.resolve(true);
}
cancelConnect(connectionUri: string): Promise<boolean> {
return Promise.resolve(true);
}
listDatabases(connectionUri: string): Promise<azdata.ListDatabasesResult> {
return Promise.resolve({
databaseNames: ['master', 'msdb']
});
}
changeDatabase(connectionUri: string, newDatabase: string): Promise<boolean> {
return Promise.resolve(true);
}
rebuildIntelliSenseCache(connectionUri: string): Promise<void> {
return Promise.resolve();
}
getConnectionString(connectionUri: string, includePassword: boolean): Promise<string> {
return Promise.resolve('conn_string');
}
buildConnectionInfo?(connectionString: string): Promise<azdata.ConnectionInfo> {
return Promise.resolve({
options: []
});
}
registerOnConnectionComplete(handler: (connSummary: azdata.ConnectionInfoSummary) => any): void {
this.onConnectionComplete((e) => {
handler(e);
});
}
registerOnIntelliSenseCacheComplete(handler: (connectionUri: string) => any): void {
console.log('IntellisenseCache complete');
}
registerOnConnectionChanged(handler: (changedConnInfo: azdata.ChangedConnectionInfo) => any): void {
console.log('Connection changed');
}
handle?: number;
providerId: string = ProviderId;
}

View File

@@ -0,0 +1,90 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
import * as vscode from 'vscode';
import { ProviderId } from './connectionProvider';
/**
* This class implements the ObjectExplorerProvider interface that is responsible for providing the connection tree view content.
*/
export class ObjectExplorerProvider implements azdata.ObjectExplorerProvider {
onSessionCreatedEmitter: vscode.EventEmitter<azdata.ObjectExplorerSession> = new vscode.EventEmitter<azdata.ObjectExplorerSession>();
onSessionCreated: vscode.Event<azdata.ObjectExplorerSession> = this.onSessionCreatedEmitter.event;
onSessionDisconnectedEmitter: vscode.EventEmitter<azdata.ObjectExplorerSession> = new vscode.EventEmitter<azdata.ObjectExplorerSession>();
onSessionDisconnected: vscode.Event<azdata.ObjectExplorerSession> = this.onSessionDisconnectedEmitter.event;
onExpandCompletedEmitter: vscode.EventEmitter<azdata.ObjectExplorerExpandInfo> = new vscode.EventEmitter<azdata.ObjectExplorerExpandInfo>();
onExpandCompleted: vscode.Event<azdata.ObjectExplorerExpandInfo> = this.onExpandCompletedEmitter.event;
createNewSession(connInfo: azdata.ConnectionInfo): Thenable<azdata.ObjectExplorerSessionResponse> {
setTimeout(() => {
this.onSessionCreatedEmitter.fire({
sessionId: '1',
success: true,
rootNode: {
nodePath: 'root',
nodeType: 'server',
label: 'abc',
isLeaf: false
}
});
}, 500);
return Promise.resolve({
sessionId: '1'
});
}
closeSession(closeSessionInfo: azdata.ObjectExplorerCloseSessionInfo): Thenable<azdata.ObjectExplorerCloseSessionResponse> {
return Promise.resolve({
sessionId: '1',
success: true
});
}
registerOnSessionCreated(handler: (response: azdata.ObjectExplorerSession) => any): void {
this.onSessionCreated((e) => {
handler(e);
});
}
registerOnSessionDisconnected?(handler: (response: azdata.ObjectExplorerSession) => any): void {
this.onSessionDisconnected((e) => {
handler(e);
});
}
expandNode(nodeInfo: azdata.ExpandNodeInfo): Thenable<boolean> {
setTimeout(() => {
this.onExpandCompletedEmitter.fire({
sessionId: nodeInfo.sessionId,
nodePath: nodeInfo.nodePath,
nodes: [
{
nodePath: 'root/1',
nodeType: 'Database',
label: 'abc1',
isLeaf: false
}, {
nodePath: 'root/2',
nodeType: 'Database',
label: 'abc2',
isLeaf: false
}
]
});
}, 500);
return Promise.resolve(true);
}
refreshNode(nodeInfo: azdata.ExpandNodeInfo): Thenable<boolean> {
return Promise.resolve(true);
}
findNodes(findNodesInfo: azdata.FindNodesInfo): Thenable<azdata.ObjectExplorerFindNodesResponse> {
throw new Error('Method not implemented.');
}
registerOnExpandCompleted(handler: (response: azdata.ObjectExplorerExpandInfo) => any): void {
this.onExpandCompleted((e) => {
handler(e);
});
}
handle?: number;
providerId: string = ProviderId;
}

View File

@@ -0,0 +1,82 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
import * as vscode from 'vscode';
export class ConnectionProvider implements azdata.ConnectionProvider {
private onConnectionCompleteEmitter: vscode.EventEmitter<azdata.ConnectionInfoSummary> = new vscode.EventEmitter();
onConnectionComplete: vscode.Event<azdata.ConnectionInfoSummary> = this.onConnectionCompleteEmitter.event;
private onIntelliSenseCacheCompleteEmitter: vscode.EventEmitter<string> = new vscode.EventEmitter();
onIntelliSenseCacheComplete: vscode.Event<string> = this.onIntelliSenseCacheCompleteEmitter.event;
private onConnectionChangedEmitter: vscode.EventEmitter<azdata.ChangedConnectionInfo> = new vscode.EventEmitter();
onConnectionChanged: vscode.Event<azdata.ChangedConnectionInfo> = this.onConnectionChangedEmitter.event;
connect(connectionUri: string, connectionInfo: azdata.ConnectionInfo): Promise<boolean> {
this.onConnectionCompleteEmitter.fire({
connectionId: '',
ownerUri: connectionUri,
messages: '',
errorMessage: '',
errorNumber: 0,
connectionSummary: {
serverName: '',
userName: ''
},
serverInfo: {
serverReleaseVersion: 1,
engineEditionId: 1,
serverVersion: '1.0',
serverLevel: '',
serverEdition: '',
isCloud: true,
azureVersion: 1,
osVersion: '',
options: {}
}
});
return Promise.resolve(true);
}
disconnect(connectionUri: string): Promise<boolean> {
return Promise.resolve(true);
}
cancelConnect(connectionUri: string): Promise<boolean> {
return Promise.resolve(true);
}
listDatabases(connectionUri: string): Promise<azdata.ListDatabasesResult> {
return Promise.resolve({
databaseNames: ['master', 'msdb']
});
}
changeDatabase(connectionUri: string, newDatabase: string): Promise<boolean> {
return Promise.resolve(true);
}
rebuildIntelliSenseCache(connectionUri: string): Promise<void> {
return Promise.resolve();
}
getConnectionString(connectionUri: string, includePassword: boolean): Promise<string> {
return Promise.resolve('conn_string');
}
buildConnectionInfo?(connectionString: string): Promise<azdata.ConnectionInfo> {
return Promise.resolve({
options: []
});
}
registerOnConnectionComplete(handler: (connSummary: azdata.ConnectionInfoSummary) => any): void {
this.onConnectionComplete((e) => {
handler(e);
});
}
registerOnIntelliSenseCacheComplete(handler: (connectionUri: string) => any): void {
console.log('IntellisenseCache complete');
}
registerOnConnectionChanged(handler: (changedConnInfo: azdata.ChangedConnectionInfo) => any): void {
console.log('Connection changed');
}
handle?: number;
providerId: string = 'testProvider';
}

View File

@@ -13,6 +13,9 @@ import * as fs from 'fs';
import * as path from 'path';
import { TreeNode, TreeDataProvider } from './treeDataProvider';
import * as dashboard from './modelViewDashboard';
import { ConnectionProvider } from '../providers/connectionProvider';
import { IconProvider } from '../providers/IconProvider';
import { ObjectExplorerProvider } from '../providers/objectExplorerProvider';
/**
* The main controller class that initializes the extension
@@ -37,6 +40,13 @@ export default class MainController implements vscode.Disposable {
}
public activate(): Promise<boolean> {
const connectionProvider = new ConnectionProvider();
const iconProvider = new IconProvider();
const objectExplorer = new ObjectExplorerProvider();
azdata.dataprotocol.registerConnectionProvider(connectionProvider);
azdata.dataprotocol.registerIconProvider(iconProvider);
azdata.dataprotocol.registerObjectExplorerProvider(objectExplorer);
const buttonHtml = fs.readFileSync(path.join(__dirname, 'button.html')).toString();
const counterHtml = fs.readFileSync(path.join(__dirname, 'counter.html')).toString();
this.registerSqlServicesModelView();