diff --git a/extensions/sql-database-projects/src/controllers/mainController.ts b/extensions/sql-database-projects/src/controllers/mainController.ts index 5b1db9d6bd..84d82199ba 100644 --- a/extensions/sql-database-projects/src/controllers/mainController.ts +++ b/extensions/sql-database-projects/src/controllers/mainController.ts @@ -17,6 +17,7 @@ import * as constants from '../common/constants'; import { SqlDatabaseProjectProvider } from '../projectProvider/projectProvider'; import { launchAddSqlBindingQuickpick } from '../dialogs/addSqlBindingQuickpick'; import { PackageHelper } from '../tools/packageHelper'; +import { GenerateProjectFromOpenApiSpecOptions } from 'sqldbproj'; /** * The main controller class that initializes the extension @@ -64,7 +65,7 @@ export default class MainController implements vscode.Disposable { vscode.commands.registerCommand('sqlDatabaseProjects.publish', async (node: WorkspaceTreeItem) => { return this.projectsController.publishProject(node); }); vscode.commands.registerCommand('sqlDatabaseProjects.schemaCompare', async (node: WorkspaceTreeItem) => { return this.projectsController.schemaCompare(node); }); vscode.commands.registerCommand('sqlDatabaseProjects.createProjectFromDatabase', async (context: azdataType.IConnectionProfile | vscodeMssql.ITreeNodeInfo | undefined) => { return this.projectsController.createProjectFromDatabase(context); }); - vscode.commands.registerCommand('sqlDatabaseProjects.generateProjectFromOpenApiSpec', async () => { return this.projectsController.generateProjectFromOpenApiSpec(); }); + vscode.commands.registerCommand('sqlDatabaseProjects.generateProjectFromOpenApiSpec', async (options?: GenerateProjectFromOpenApiSpecOptions) => { return this.projectsController.generateProjectFromOpenApiSpec(options); }); vscode.commands.registerCommand('sqlDatabaseProjects.newScript', async (node: WorkspaceTreeItem) => { return this.projectsController.addItemPromptFromNode(node, templates.script); }); vscode.commands.registerCommand('sqlDatabaseProjects.newPreDeploymentScript', async (node: WorkspaceTreeItem) => { return this.projectsController.addItemPromptFromNode(node, templates.preDeployScript); }); diff --git a/extensions/sql-database-projects/src/controllers/projectController.ts b/extensions/sql-database-projects/src/controllers/projectController.ts index 1f0859aff4..7c043cfe04 100644 --- a/extensions/sql-database-projects/src/controllers/projectController.ts +++ b/extensions/sql-database-projects/src/controllers/projectController.ts @@ -38,7 +38,7 @@ import { DashboardData, PublishData, Status } from '../models/dashboardData/dash import { getPublishDatabaseSettings, launchPublishTargetOption } from '../dialogs/publishDatabaseQuickpick'; import { launchPublishToDockerContainerQuickpick } from '../dialogs/deployDatabaseQuickpick'; import { DeployService } from '../models/deploy/deployService'; -import { SqlTargetPlatform } from 'sqldbproj'; +import { GenerateProjectFromOpenApiSpecOptions, SqlTargetPlatform } from 'sqldbproj'; import { AutorestHelper } from '../tools/autorestHelper'; import { createNewProjectFromDatabaseWithQuickpick } from '../dialogs/createProjectFromDatabaseQuickpick'; import { addDatabaseReferenceQuickpick } from '../dialogs/addDatabaseReferenceQuickpick'; @@ -901,25 +901,34 @@ export class ProjectsController { * outputFolder: 'C:\Source', * projectName: 'MyProject'} */ - public async selectAutorestProjectLocation(projectName: string): Promise<{ newProjectFolder: string, outputFolder: string, projectName: string } | undefined> { - let valid = false; - let newProjectFolder: string = ''; - let outputFolder: string = ''; + public async selectAutorestProjectLocation(projectName: string, defaultOutputLocation: vscode.Uri | undefined): Promise<{ newProjectFolder: string, outputFolder: string, projectName: string } | undefined> { + let newProjectFolder = defaultOutputLocation ? path.join(defaultOutputLocation.fsPath, projectName) : ''; + let outputFolder = defaultOutputLocation?.fsPath || ''; + while (true) { + let quickPickTitle = ''; + if (newProjectFolder && await utils.exists(newProjectFolder)) { + // Folder already exists at target location, prompt for new location + quickPickTitle = constants.folderAlreadyExistsChooseNewLocation(newProjectFolder); + } + else if (!newProjectFolder) { + // No target location yet + quickPickTitle = constants.selectProjectLocation; + } + else { + // Folder doesn't exist at target location so we're done + break; + } + const quickpickSelection = await vscode.window.showQuickPick([constants.browseEllipsisWithIcon], { title: quickPickTitle, ignoreFocusOut: true }); + if (!quickpickSelection) { + return; + } - let quickpickSelection = await vscode.window.showQuickPick( - [constants.browseEllipsisWithIcon], - { title: constants.selectProjectLocation, ignoreFocusOut: true }); - if (!quickpickSelection) { - return; - } - - while (!valid) { const folders = await vscode.window.showOpenDialog({ canSelectFiles: false, canSelectFolders: true, canSelectMany: false, openLabel: constants.selectString, - defaultUri: vscode.workspace.workspaceFolders?.[0]?.uri, + defaultUri: defaultOutputLocation ?? vscode.workspace.workspaceFolders?.[0]?.uri, title: constants.selectProjectLocation }); @@ -930,18 +939,6 @@ export class ProjectsController { outputFolder = folders[0].fsPath; newProjectFolder = path.join(outputFolder, projectName); - - if (await utils.exists(newProjectFolder)) { - - quickpickSelection = await vscode.window.showQuickPick( - [constants.browseEllipsisWithIcon], - { title: constants.folderAlreadyExistsChooseNewLocation(newProjectFolder), ignoreFocusOut: true }); - if (!quickpickSelection) { - return; - } - } else { - valid = true; - } } return { newProjectFolder, outputFolder, projectName }; @@ -987,22 +984,22 @@ export class ProjectsController { return name; } - public async generateProjectFromOpenApiSpec(): Promise { + public async generateProjectFromOpenApiSpec(options?: GenerateProjectFromOpenApiSpecOptions): Promise { try { // 1. select spec file - const specPath: string | undefined = await this.selectAutorestSpecFile(); + const specPath: string | undefined = options?.openApiSpecFile?.fsPath || await this.selectAutorestSpecFile(); if (!specPath) { return; } // 2. prompt for project name - const projectName = await this.promptForAutorestProjectName(path.basename(specPath, path.extname(specPath))); + const projectName = await this.promptForAutorestProjectName(options?.defaultProjectName || path.basename(specPath, path.extname(specPath))); if (!projectName) { return; } // 3. select location, make new folder - const projectInfo = await this.selectAutorestProjectLocation(projectName!); + const projectInfo = await this.selectAutorestProjectLocation(projectName!, options?.defaultOutputLocation); if (!projectInfo) { return; } @@ -1039,8 +1036,10 @@ export class ProjectsController { await project.addScriptItem(path.relative(project.projectFolderPath, postDeploymentScript.fsPath), undefined, templates.postDeployScript); } - // 7. add project to workspace and open - await this.openProjectInWorkspace(newProjFilePath); + if (options?.doNotOpenInWorkspace !== true) { + // 7. add project to workspace and open + await this.openProjectInWorkspace(newProjFilePath); + } return project; } catch (err) { diff --git a/extensions/sql-database-projects/src/sqldbproj.d.ts b/extensions/sql-database-projects/src/sqldbproj.d.ts index 416f7fbba6..a2502639b5 100644 --- a/extensions/sql-database-projects/src/sqldbproj.d.ts +++ b/extensions/sql-database-projects/src/sqldbproj.d.ts @@ -37,6 +37,28 @@ declare module 'sqldbproj' { openSqlNewProjectDialog(allowedTargetPlatforms?: SqlTargetPlatform[]): Promise; } + /** + * Options to use when generating a project from an OpenAPI spec + */ + export type GenerateProjectFromOpenApiSpecOptions = { + /** + * The OpenAPI spec file to use instead of having the user select it + */ + openApiSpecFile?: vscode.Uri, + /** + * The default name to give the generated project in the name input prompt + */ + defaultProjectName?: string, + /** + * The default location to show when the user is selecting the output location of the project + */ + defaultOutputLocation?: vscode.Uri, + /** + * If true then the project will not be opened in the workspace after being created + */ + doNotOpenInWorkspace?: boolean + }; + export interface ISqlProject { /** * Reads the project setting and contents from the file diff --git a/extensions/sql-database-projects/src/test/projectController.test.ts b/extensions/sql-database-projects/src/test/projectController.test.ts index 6011802b9c..8113169c47 100644 --- a/extensions/sql-database-projects/src/test/projectController.test.ts +++ b/extensions/sql-database-projects/src/test/projectController.test.ts @@ -693,7 +693,7 @@ describe('ProjectsController', function (): void { projController.callBase = true; projController.setup(x => x.selectAutorestSpecFile()).returns(async () => specName); - projController.setup(x => x.selectAutorestProjectLocation(TypeMoq.It.isAny())).returns(async () => { + projController.setup(x => x.selectAutorestProjectLocation(TypeMoq.It.isAny(), undefined)).returns(async () => { await fs.mkdir(newProjFolder); return {