diff --git a/extensions/sql-database-projects/src/common/constants.ts b/extensions/sql-database-projects/src/common/constants.ts index 599dcf3116..a706d7dcdd 100644 --- a/extensions/sql-database-projects/src/common/constants.ts +++ b/extensions/sql-database-projects/src/common/constants.ts @@ -433,6 +433,7 @@ export function generatingProjectFailed(errorMessage: string) { return localize( export const noSqlFilesGenerated = localize('noSqlFilesGenerated', "No .sql files were generated by Autorest. Please confirm that your spec contains model definitions, or check the output log for details."); export function multipleMostDeploymentScripts(count: number) { return localize('multipleMostDeploymentScripts', "Unexpected number of {0} files: {1}", autorestPostDeploymentScriptName, count); } export const specSelectionText = localize('specSelectionText', "OpenAPI/Swagger spec"); +export const autorestProjectName = localize('autorestProjectName', "New SQL project name"); export function generatingProjectFromAutorest(specName: string) { return localize('generatingProjectFromAutorest', "Generating new SQL project from {0}... Check output window for details.", specName); } // System dbs diff --git a/extensions/sql-database-projects/src/common/utils.ts b/extensions/sql-database-projects/src/common/utils.ts index 20e453d79f..fbb38a1b39 100644 --- a/extensions/sql-database-projects/src/common/utils.ts +++ b/extensions/sql-database-projects/src/common/utils.ts @@ -544,8 +544,8 @@ export function validateSqlServerPortNumber(port: string | undefined): boolean { return !isNaN(valueAsNum) && valueAsNum > 0 && valueAsNum < 65535; } -export function isEmptyString(password: string | undefined): boolean { - return password === undefined || password === ''; +export function isEmptyString(input: string | undefined): boolean { + return input === undefined || input === ''; } export function isValidSQLPassword(password: string, userName: string = 'sa'): boolean { diff --git a/extensions/sql-database-projects/src/controllers/projectController.ts b/extensions/sql-database-projects/src/controllers/projectController.ts index 6300933a32..cefcad7b73 100644 --- a/extensions/sql-database-projects/src/controllers/projectController.ts +++ b/extensions/sql-database-projects/src/controllers/projectController.ts @@ -864,11 +864,10 @@ export class ProjectsController { * outputFolder: 'C:\Source', * projectName: 'MyProject'} */ - public async selectAutorestProjectLocation(specPath: string): Promise<{ newProjectFolder: string, outputFolder: string, projectName: string } | undefined> { + public async selectAutorestProjectLocation(projectName: string): Promise<{ newProjectFolder: string, outputFolder: string, projectName: string } | undefined> { let valid = false; let newProjectFolder: string = ''; let outputFolder: string = ''; - let projectName: string = ''; let quickpickSelection = await vscode.window.showQuickPick( [constants.browseEllipsisWithIcon], @@ -893,7 +892,6 @@ export class ProjectsController { outputFolder = folders[0].fsPath; - projectName = path.basename(specPath, path.extname(specPath)); newProjectFolder = path.join(outputFolder, projectName); if (await utils.exists(newProjectFolder)) { @@ -909,11 +907,12 @@ export class ProjectsController { } } - await fs.mkdir(newProjectFolder); return { newProjectFolder, outputFolder, projectName }; } public async generateAutorestFiles(specPath: string, newProjectFolder: string): Promise { + await fs.mkdir(newProjectFolder, { recursive: true }); + return vscode.window.withProgress( { location: vscode.ProgressLocation.Notification, @@ -932,6 +931,25 @@ export class ProjectsController { workspaceApi.showProjectsView(); } + public async promptForAutorestProjectName(defaultName?: string): Promise { + let name: string | undefined = await vscode.window.showInputBox({ + ignoreFocusOut: true, + prompt: constants.autorestProjectName, + value: defaultName, + validateInput: (value) => { + return value.trim() ? undefined : constants.nameMustNotBeEmpty; + } + }); + + if (name === undefined) { + return; // cancelled by user + } + + name = name.trim(); + + return name; + } + public async generateProjectFromOpenApiSpec(): Promise { try { // 1. select spec file @@ -940,13 +958,19 @@ export class ProjectsController { return; } - // 2. select location, make new folder - const projectInfo = await this.selectAutorestProjectLocation(specPath!); + // 2. prompt for project name + const projectName = await this.promptForAutorestProjectName(path.basename(specPath, path.extname(specPath))); + if (!projectName) { + return; + } + + // 3. select location, make new folder + const projectInfo = await this.selectAutorestProjectLocation(projectName!); if (!projectInfo) { return; } - // 3. run AutoRest to generate .sql files + // 4. run AutoRest to generate .sql files const result = await this.generateAutorestFiles(specPath, projectInfo.newProjectFolder); if (!result) { // user canceled operation when choosing how to run autorest return; @@ -960,7 +984,7 @@ export class ProjectsController { return; } - // 4. create new SQL project + // 5. create new SQL project const newProjFilePath = await this.createNewProject({ newProjName: projectInfo.projectName, folderUri: vscode.Uri.file(projectInfo.outputFolder), @@ -969,7 +993,7 @@ export class ProjectsController { const project = await Project.openProject(newProjFilePath); - // 5. add generated files to SQL project + // 6. add generated files to SQL project await project.addToProject(fileFolderList.filter(f => !f.fsPath.endsWith(constants.autorestPostDeploymentScriptName))); // Add generated file structure to the project const postDeploymentScript: vscode.Uri | undefined = this.findPostDeploymentScript(fileFolderList); @@ -978,7 +1002,7 @@ export class ProjectsController { await project.addScriptItem(path.relative(project.projectFolderPath, postDeploymentScript.fsPath), undefined, templates.postDeployScript); } - // 6. add project to workspace and open + // 7. add project to workspace and open await this.openProjectInWorkspace(newProjFilePath); return project; diff --git a/extensions/sql-database-projects/src/test/projectController.test.ts b/extensions/sql-database-projects/src/test/projectController.test.ts index 1b2de12007..9c4fc845dc 100644 --- a/extensions/sql-database-projects/src/test/projectController.test.ts +++ b/extensions/sql-database-projects/src/test/projectController.test.ts @@ -684,7 +684,8 @@ describe('ProjectsController', function (): void { const parentFolder = await testUtils.generateTestFolderPath(); await testUtils.createDummyFileStructure(); const specName = 'DummySpec.yaml'; - const newProjFolder = path.join(parentFolder, path.basename(specName, '.yaml')); + const renamedProjectName = 'RenamedProject'; + const newProjFolder = path.join(parentFolder, renamedProjectName); let fileList: vscode.Uri[] = []; const projController = TypeMoq.Mock.ofType(ProjectsController); @@ -697,7 +698,7 @@ describe('ProjectsController', function (): void { return { newProjectFolder: newProjFolder, outputFolder: parentFolder, - projectName: path.basename(specName, '.yaml') + projectName: renamedProjectName }; }); @@ -707,10 +708,14 @@ describe('ProjectsController', function (): void { return 'some dummy console output'; }); + projController.setup(x => x.promptForAutorestProjectName(TypeMoq.It.isAny())).returns(async () => renamedProjectName); projController.setup(x => x.openProjectInWorkspace(TypeMoq.It.isAny())).returns(async () => { }); const project = (await projController.object.generateProjectFromOpenApiSpec())!; + should(project.projectFileName).equal(renamedProjectName); + should(project.projectFolderPath.endsWith(renamedProjectName)).is.true(`Expected: '${project.projectFolderPath}' to include '${renamedProjectName}'`); + should(project.postDeployScripts.length).equal(1, `Expected 1 post-deployment script, got ${project?.postDeployScripts.length}`); const actual = path.basename(project.postDeployScripts[0].fsUri.fsPath); should(actual).equal(constants.autorestPostDeploymentScriptName, `Unexpected post-deployment script name: ${actual}, expected ${constants.autorestPostDeploymentScriptName}`);