Add Create Proj from DB to VS Code context menus (#16584)

* Add Create Proj from DB to VS Code context menus

* Fix error

* add comment

* use constant
This commit is contained in:
Charles Gagnon
2021-08-06 08:59:35 -07:00
committed by GitHub
parent e6f356accc
commit b8da94f9ef
5 changed files with 84 additions and 16 deletions

View File

@@ -351,6 +351,11 @@
"command": "sqlDatabaseProjects.openContainingFolder", "command": "sqlDatabaseProjects.openContainingFolder",
"when": "view == dataworkspace.views.main && viewItem == databaseProject.itemType.project", "when": "view == dataworkspace.views.main && viewItem == databaseProject.itemType.project",
"group": "9_dbProjectsLast@8" "group": "9_dbProjectsLast@8"
},
{
"command": "sqlDatabaseProjects.createProjectFromDatabase",
"when": "!azdataAvailable && view == objectExplorer && viewItem =~ /^(disconnectedServer|Server|Database)$/",
"group": "sqldbproj@1"
} }
], ],
"objectExplorer/item/context": [ "objectExplorer/item/context": [

View File

@@ -5,6 +5,7 @@
import type * as azdataType from 'azdata'; import type * as azdataType from 'azdata';
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import * as vscodeMssql from 'vscode-mssql';
import * as templates from '../templates/templates'; import * as templates from '../templates/templates';
import * as path from 'path'; import * as path from 'path';
@@ -50,7 +51,7 @@ export default class MainController implements vscode.Disposable {
vscode.commands.registerCommand('sqlDatabaseProjects.build', async (node: WorkspaceTreeItem) => { await this.projectsController.buildProject(node); }); vscode.commands.registerCommand('sqlDatabaseProjects.build', async (node: WorkspaceTreeItem) => { await this.projectsController.buildProject(node); });
vscode.commands.registerCommand('sqlDatabaseProjects.publish', async (node: WorkspaceTreeItem) => { this.projectsController.publishProject(node); }); vscode.commands.registerCommand('sqlDatabaseProjects.publish', async (node: WorkspaceTreeItem) => { this.projectsController.publishProject(node); });
vscode.commands.registerCommand('sqlDatabaseProjects.schemaCompare', async (node: WorkspaceTreeItem) => { await this.projectsController.schemaCompare(node); }); vscode.commands.registerCommand('sqlDatabaseProjects.schemaCompare', async (node: WorkspaceTreeItem) => { await this.projectsController.schemaCompare(node); });
vscode.commands.registerCommand('sqlDatabaseProjects.createProjectFromDatabase', async (profile: azdataType.IConnectionProfile) => { await this.projectsController.createProjectFromDatabase(profile); }); vscode.commands.registerCommand('sqlDatabaseProjects.createProjectFromDatabase', async (context: azdataType.IConnectionProfile | vscodeMssql.ITreeNodeInfo | undefined) => { await this.projectsController.createProjectFromDatabase(context); });
vscode.commands.registerCommand('sqlDatabaseProjects.newScript', async (node: WorkspaceTreeItem) => { await this.projectsController.addItemPromptFromNode(node, templates.script); }); vscode.commands.registerCommand('sqlDatabaseProjects.newScript', async (node: WorkspaceTreeItem) => { await this.projectsController.addItemPromptFromNode(node, templates.script); });
vscode.commands.registerCommand('sqlDatabaseProjects.newPreDeploymentScript', async (node: WorkspaceTreeItem) => { await this.projectsController.addItemPromptFromNode(node, templates.preDeployScript); }); vscode.commands.registerCommand('sqlDatabaseProjects.newPreDeploymentScript', async (node: WorkspaceTreeItem) => { await this.projectsController.addItemPromptFromNode(node, templates.preDeployScript); });

View File

@@ -863,10 +863,10 @@ export class ProjectsController {
* Creates a new SQL database project from the existing database, * Creates a new SQL database project from the existing database,
* prompting the user for a name, file path location and extract target * prompting the user for a name, file path location and extract target
*/ */
public async createProjectFromDatabase(context: azdataType.IConnectionProfile | any): Promise<CreateProjectFromDatabaseDialog | undefined> { public async createProjectFromDatabase(context: azdataType.IConnectionProfile | mssqlVscode.ITreeNodeInfo | undefined): Promise<CreateProjectFromDatabaseDialog | undefined> {
const profile = this.getConnectionProfileFromContext(context); const profile = this.getConnectionProfileFromContext(context);
if (utils.getAzdataApi()) { if (utils.getAzdataApi()) {
let createProjectFromDatabaseDialog = this.getCreateProjectFromDatabaseDialog(profile); let createProjectFromDatabaseDialog = this.getCreateProjectFromDatabaseDialog(profile as azdataType.IConnectionProfile);
createProjectFromDatabaseDialog.createProjectFromDatabaseCallback = async (model) => await this.createProjectFromDatabaseCallback(model); createProjectFromDatabaseDialog.createProjectFromDatabaseCallback = async (model) => await this.createProjectFromDatabaseCallback(model);
@@ -874,7 +874,15 @@ export class ProjectsController {
return createProjectFromDatabaseDialog; return createProjectFromDatabaseDialog;
} else { } else {
const model = await createNewProjectFromDatabaseWithQuickpick(); if (context) {
// The profile we get from VS Code is for the overall server connection and isn't updated based on the database node
// the command was launched from like it is in ADS. So get the actual database name from the MSSQL extension and
// update the connection info here.
const treeNodeContext = context as mssqlVscode.ITreeNodeInfo;
const databaseName = (await utils.getVscodeMssqlApi()).getDatabaseNameFromTreeNode(treeNodeContext);
(profile as mssqlVscode.IConnectionInfo).database = databaseName;
}
const model = await createNewProjectFromDatabaseWithQuickpick(profile as mssqlVscode.IConnectionInfo);
if (model) { if (model) {
await this.createProjectFromDatabaseCallback(model); await this.createProjectFromDatabaseCallback(model);
} }
@@ -925,14 +933,14 @@ export class ProjectsController {
} }
} }
private getConnectionProfileFromContext(context: azdataType.IConnectionProfile | any): azdataType.IConnectionProfile | undefined { private getConnectionProfileFromContext(context: azdataType.IConnectionProfile | mssqlVscode.ITreeNodeInfo | undefined): azdataType.IConnectionProfile | mssqlVscode.IConnectionInfo | undefined {
if (!context) { if (!context) {
return undefined; return undefined;
} }
// depending on where import new project is launched from, the connection profile could be passed as just // depending on where import new project is launched from, the connection profile could be passed as just
// the profile or it could be wrapped in another object // the profile or it could be wrapped in another object
return (<any>context).connectionProfile ? (<any>context).connectionProfile : context; return (<any>context)?.connectionProfile ?? (context as mssqlVscode.ITreeNodeInfo).connectionInfo ?? context;
} }
public async createProjectFromDatabaseApiCall(model: ImportDataModel): Promise<void> { public async createProjectFromDatabaseApiCall(model: ImportDataModel): Promise<void> {

View File

@@ -14,29 +14,46 @@ import { mapExtractTargetEnum } from './createProjectFromDatabaseDialog';
/** /**
* Create flow for a New Project using only VS Code-native APIs such as QuickPick * Create flow for a New Project using only VS Code-native APIs such as QuickPick
* @param connectionInfo Optional connection info to use instead of prompting the user for a connection
*/ */
export async function createNewProjectFromDatabaseWithQuickpick(): Promise<ImportDataModel | undefined> { export async function createNewProjectFromDatabaseWithQuickpick(connectionInfo?: IConnectionInfo): Promise<ImportDataModel | undefined> {
const vscodeMssqlApi = await getVscodeMssqlApi();
// 1. Select connection // 1. Select connection
const vscodeMssqlApi = await getVscodeMssqlApi(); // Use passed in profile if we have one - otherwise prompt user to select one
let connectionProfile: IConnectionInfo | undefined = undefined; let connectionProfile: IConnectionInfo | undefined = connectionInfo ?? await vscodeMssqlApi.promptForConnection(true);
let connectionUri: string = '';
let dbs: string[] | undefined = undefined;
while (!dbs) {
connectionProfile = await vscodeMssqlApi.promptForConnection(true);
if (!connectionProfile) { if (!connectionProfile) {
// User cancelled // User cancelled
return undefined; return undefined;
} }
let connectionUri: string = '';
let dbs: string[] | undefined = undefined;
while (!dbs) {
// Get the list of databases now to validate that the connection is valid and re-prompt them if it isn't // Get the list of databases now to validate that the connection is valid and re-prompt them if it isn't
try { try {
connectionUri = await vscodeMssqlApi.connect(connectionProfile); connectionUri = await vscodeMssqlApi.connect(connectionProfile);
dbs = (await vscodeMssqlApi.listDatabases(connectionUri)) dbs = (await vscodeMssqlApi.listDatabases(connectionUri))
.filter(db => !constants.systemDbs.includes(db)); // Filter out system dbs .filter(db => !constants.systemDbs.includes(db)); // Filter out system dbs
} catch (err) { } catch (err) {
// no-op, the mssql extension handles showing the error to the user. We'll just go // The mssql extension handles showing the error to the user. Prompt the user
// back and prompt the user for a connection again // for a new connection and then go and try getting the DBs again
connectionProfile = await vscodeMssqlApi.promptForConnection(true);
if (!connectionProfile) {
// User cancelled
return undefined;
} }
}
}
// Move the database for the given connection up to the top
if (connectionProfile.database && connectionProfile.database !== constants.master) {
const index = dbs.indexOf(connectionProfile.database);
if (index >= 0) {
dbs.splice(index, 1);
}
dbs.unshift(connectionProfile.database);
} }
// 2. Select database // 2. Select database

View File

@@ -4,6 +4,9 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
declare module 'vscode-mssql' { declare module 'vscode-mssql' {
import * as vscode from 'vscode';
/** /**
* Covers defining what the vscode-mssql extension exports to other extensions * Covers defining what the vscode-mssql extension exports to other extensions
* *
@@ -51,6 +54,15 @@ declare module 'vscode-mssql' {
* @returns The list of database names * @returns The list of database names
*/ */
listDatabases(connectionUri: string): Promise<string[]>; listDatabases(connectionUri: string): Promise<string[]>;
/**
* Gets the database name for the node - which is the database name of the connection for a server node, the database name
* for nodes at or under a database node or a default value if it's neither of those.
* @param node The node to get the database name of
* @returns The database name
*/
getDatabaseNameFromTreeNode(node: ITreeNodeInfo): string;
} }
/** /**
@@ -490,4 +502,29 @@ declare module 'vscode-mssql' {
defaultDeploymentOptions: DeploymentOptions; defaultDeploymentOptions: DeploymentOptions;
} }
export interface ITreeNodeInfo extends vscode.TreeItem {
readonly connectionInfo: IConnectionInfo;
nodeType: string;
metadata: ObjectMetadata;
parentNode: ITreeNodeInfo;
}
export const enum MetadataType {
Table = 0,
View = 1,
SProc = 2,
Function = 3
}
export interface ObjectMetadata {
metadataType: MetadataType;
metadataTypeName: string;
urn: string;
name: string;
schema: string;
}
} }