diff --git a/extensions/data-workspace/package.json b/extensions/data-workspace/package.json index 17c4463954..830f995106 100644 --- a/extensions/data-workspace/package.json +++ b/extensions/data-workspace/package.json @@ -13,7 +13,9 @@ "azdata": ">=1.25.0" }, "activationEvents": [ - "*" + "onView:dataworkspace.views.main", + "onCommand:projects.openExisting", + "onCommand:projects.new" ], "main": "./out/main", "repository": { @@ -158,8 +160,7 @@ "id": "dataworkspace.views.main", "name": "%main-view-name%", "contextualTitle": "%data-workspace-view-container-name%", - "icon": "images/data-workspace.svg", - "when": "isProjectProviderAvailable" + "icon": "images/data-workspace.svg" } ] }, diff --git a/extensions/data-workspace/src/common/interfaces.ts b/extensions/data-workspace/src/common/interfaces.ts index 661945c0bb..deb1d17544 100644 --- a/extensions/data-workspace/src/common/interfaces.ts +++ b/extensions/data-workspace/src/common/interfaces.ts @@ -85,8 +85,6 @@ export interface IWorkspaceService { */ gitCloneProject(url: string, localClonePath: string): Promise; - readonly isProjectProviderAvailable: boolean; - /** * Event fires when projects in workspace changes */ diff --git a/extensions/data-workspace/src/main.ts b/extensions/data-workspace/src/main.ts index 1bd493e388..b2abd3335c 100644 --- a/extensions/data-workspace/src/main.ts +++ b/extensions/data-workspace/src/main.ts @@ -10,7 +10,6 @@ import { WorkspaceTreeItem, IExtension } from 'dataworkspace'; import { DataWorkspaceExtension } from './common/dataWorkspaceExtension'; import { NewProjectDialog } from './dialogs/newProjectDialog'; import { browseForProject, OpenExistingDialog } from './dialogs/openExistingDialog'; -import { IWorkspaceService } from './common/interfaces'; import { IconPathHelper } from './common/iconHelper'; import { ProjectDashboard } from './dialogs/projectDashboard'; import { getAzdataApi } from './common/utils'; @@ -42,13 +41,6 @@ export async function activate(context: vscode.ExtensionContext): Promise { - setProjectProviderContextValue(workspaceService); - })); - setProjectProviderContextValue(workspaceService); - Logger.log(`setProjectProviderContextValue took ${new Date().getTime() - settingProjectProviderContextStartTime}ms`); - const registerCommandStartTime = new Date().getTime(); context.subscriptions.push(vscode.commands.registerCommand('projects.new', async () => { // Make sure all project providing extensions are activated to be sure the project templates show up @@ -105,9 +97,5 @@ export async function activate(context: vscode.ExtensionContext): Promise Logger.error(`Error initializing projects in workspace ${err}`)); } - get isProjectProviderAvailable(): boolean { - Logger.log(`Checking ${vscode.extensions.all.length} extensions to see if there is a project provider is available`); - const startTime = new Date().getTime(); - for (const extension of vscode.extensions.all) { - const projectTypes = extension.packageJSON.contributes && extension.packageJSON.contributes.projects as string[]; - if (projectTypes && projectTypes.length > 0) { - Logger.log(`Project provider found. Total time = ${new Date().getTime() - startTime}ms`); - return true; - } - } - - Logger.log(`No project providers found. Total time = ${new Date().getTime() - startTime}ms`); - return false; - } - /** * Verify that a workspace is open or that if one isn't and we're running in ADS, it's ok to create a workspace and restart ADS */ diff --git a/extensions/sql-database-projects/package.json b/extensions/sql-database-projects/package.json index 82db50115f..d9cf65c723 100644 --- a/extensions/sql-database-projects/package.json +++ b/extensions/sql-database-projects/package.json @@ -26,7 +26,8 @@ }, "extensionDependencies": [ "Microsoft.mssql", - "Microsoft.schema-compare" + "Microsoft.schema-compare", + "Microsoft.data-workspace" ], "capabilities": { "virtualWorkspaces": false, diff --git a/extensions/sql-database-projects/src/test/dialogs/createProjectFromDatabaseDialog.test.ts b/extensions/sql-database-projects/src/test/dialogs/createProjectFromDatabaseDialog.test.ts index 7bdee76fda..13d2563c88 100644 --- a/extensions/sql-database-projects/src/test/dialogs/createProjectFromDatabaseDialog.test.ts +++ b/extensions/sql-database-projects/src/test/dialogs/createProjectFromDatabaseDialog.test.ts @@ -7,6 +7,9 @@ import * as should from 'should'; import * as azdata from 'azdata'; import * as mssql from 'mssql'; import * as sinon from 'sinon'; +import * as utils from '../../common/utils' +import * as newProjectTool from '../../tools/newProjectTool'; + import { CreateProjectFromDatabaseDialog } from '../../dialogs/createProjectFromDatabaseDialog'; import { mockConnectionProfile } from '../testContext'; import { ImportDataModel } from '../../models/api/import'; @@ -18,7 +21,7 @@ describe('Create Project From Database Dialog', () => { it('Should open dialog successfully', async function (): Promise { sinon.stub(azdata.connection, 'getConnections').resolves([]); - sinon.stub(azdata.connection, 'connect').resolves({ connected: true, connectionId: '0', errorMessage: '', errorCode: 0}); + sinon.stub(azdata.connection, 'connect').resolves({ connected: true, connectionId: '0', errorMessage: '', errorCode: 0 }); sinon.stub(azdata.connection, 'listDatabases').resolves([]); const dialog = new CreateProjectFromDatabaseDialog(mockConnectionProfile); await dialog.openDialog(); @@ -27,7 +30,7 @@ describe('Create Project From Database Dialog', () => { it('Should enable ok button correctly with a connection profile', async function (): Promise { sinon.stub(azdata.connection, 'getConnections').resolves([]); - sinon.stub(azdata.connection, 'connect').resolves({ connected: true, connectionId: '0', errorMessage: '', errorCode: 0}); + sinon.stub(azdata.connection, 'connect').resolves({ connected: true, connectionId: '0', errorMessage: '', errorCode: 0 }); sinon.stub(azdata.connection, 'listDatabases').resolves([]); const dialog = new CreateProjectFromDatabaseDialog(mockConnectionProfile); await dialog.openDialog(); // should set connection details @@ -79,8 +82,10 @@ describe('Create Project From Database Dialog', () => { it('Should create default project name correctly when database information is populated', async function (): Promise { sinon.stub(azdata.connection, 'getConnections').resolves([]); - sinon.stub(azdata.connection, 'connect').resolves({ connected: true, connectionId: '0', errorMessage: '', errorCode: 0}); + sinon.stub(azdata.connection, 'connect').resolves({ connected: true, connectionId: '0', errorMessage: '', errorCode: 0 }); sinon.stub(azdata.connection, 'listDatabases').resolves(['My Database']); + sinon.stub(utils, 'sanitizeStringForFilename').returns('My Database'); + sinon.stub(newProjectTool, 'defaultProjectNameFromDb').returns('DatabaseProjectMy Database'); const dialog = new CreateProjectFromDatabaseDialog(mockConnectionProfile); await dialog.openDialog(); dialog.setProjectName(); @@ -92,7 +97,7 @@ describe('Create Project From Database Dialog', () => { const stubUri = 'My URI'; const dialog = new CreateProjectFromDatabaseDialog(mockConnectionProfile); sinon.stub(azdata.connection, 'getConnections').resolves([]); - sinon.stub(azdata.connection, 'connect').resolves({ connected: true, connectionId: '0', errorMessage: '', errorCode: 0}); + sinon.stub(azdata.connection, 'connect').resolves({ connected: true, connectionId: '0', errorMessage: '', errorCode: 0 }); sinon.stub(azdata.connection, 'listDatabases').resolves(['My Database']); sinon.stub(azdata.connection, 'getUriForConnection').resolves(stubUri); await dialog.openDialog(); diff --git a/extensions/sql-database-projects/src/test/dialogs/createProjectFromDatabaseQuickpick.test.ts b/extensions/sql-database-projects/src/test/dialogs/createProjectFromDatabaseQuickpick.test.ts index 3f3e8400ab..04824c21fa 100644 --- a/extensions/sql-database-projects/src/test/dialogs/createProjectFromDatabaseQuickpick.test.ts +++ b/extensions/sql-database-projects/src/test/dialogs/createProjectFromDatabaseQuickpick.test.ts @@ -12,6 +12,7 @@ import * as constants from '../../common/constants'; import * as utils from '../../common/utils' import * as quickpickHelper from '../../dialogs/quickpickHelper' import * as createProjectFromDatabaseQuickpick from '../../dialogs/createProjectFromDatabaseQuickpick'; +import * as newProjectTool from '../../tools/newProjectTool'; import { createTestUtils, mockConnectionInfo, TestUtils } from './testUtils'; import { promises as fs } from 'fs'; import { ImportDataModel } from '../../models/api/import'; @@ -25,6 +26,9 @@ describe('Create Project From Database Quickpick', () => { beforeEach(function (): void { testUtils = createTestUtils(); sinon.stub(utils, 'getVscodeMssqlApi').resolves(testUtils.vscodeMssqlIExtension.object); //set vscode mssql extension api + sinon.stub(newProjectTool, 'defaultProjectSaveLocation').returns(undefined); + sinon.stub(newProjectTool, 'defaultProjectNameFromDb').returns('DatabaseProjectTestProject'); + sinon.stub(utils, 'sanitizeStringForFilename').returns('TestProject'); }); afterEach(async function (): Promise { diff --git a/extensions/sql-database-projects/src/test/projectController.test.ts b/extensions/sql-database-projects/src/test/projectController.test.ts index 8d01458b2c..3bc4bcf486 100644 --- a/extensions/sql-database-projects/src/test/projectController.test.ts +++ b/extensions/sql-database-projects/src/test/projectController.test.ts @@ -91,7 +91,8 @@ describe('ProjectsController', function (): void { it('Should return silently when no SQL object name provided in prompts', async function (): Promise { for (const name of ['', ' ', undefined]) { - const showInputBoxStub = sinon.stub(vscode.window, 'showInputBox').resolves(name); + sinon.stub(vscode.window, 'showInputBox').resolves(name); + sinon.stub(utils, 'sanitizeStringForFilename').returns(''); const showErrorMessageSpy = sinon.spy(vscode.window, 'showErrorMessage'); const projController = new ProjectsController(testContext.outputChannel); const project = new Project('FakePath'); @@ -100,14 +101,14 @@ describe('ProjectsController', function (): void { await projController.addItemPrompt(new Project('FakePath'), '', { itemType: ItemType.script }); should(project.files.length).equal(0, 'Expected to return without throwing an exception or adding a file when an empty/undefined name is provided.'); should(showErrorMessageSpy.notCalled).be.true('showErrorMessage should not have been called'); - showInputBoxStub.restore(); - showErrorMessageSpy.restore(); + sinon.restore(); } }); it('Should show error if trying to add a file that already exists', async function (): Promise { const tableName = 'table1'; sinon.stub(vscode.window, 'showInputBox').resolves(tableName); + sinon.stub(utils, 'sanitizeStringForFilename').returns(tableName); const spy = sinon.spy(vscode.window, 'showErrorMessage'); const projController = new ProjectsController(testContext.outputChannel); let project = await testUtils.createTestProject(baselines.newProjectFileBaseline); @@ -137,6 +138,7 @@ describe('ProjectsController', function (): void { it('Should add existing item', async function (): Promise { const tableName = 'table1'; sinon.stub(vscode.window, 'showInputBox').resolves(tableName); + sinon.stub(utils, 'sanitizeStringForFilename').returns(tableName); const spy = sinon.spy(vscode.window, 'showErrorMessage'); const projController = new ProjectsController(testContext.outputChannel); let project = await testUtils.createTestProject(baselines.newProjectFileBaseline); @@ -166,6 +168,7 @@ describe('ProjectsController', function (): void { it('Should show error if trying to add a folder that already exists', async function (): Promise { const folderName = 'folder1'; const stub = sinon.stub(vscode.window, 'showInputBox').resolves(folderName); + sinon.stub(utils, 'sanitizeStringForFilename').returns(folderName); const projController = new ProjectsController(testContext.outputChannel); let project = await testUtils.createTestProject(baselines.newProjectFileBaseline); @@ -189,7 +192,8 @@ describe('ProjectsController', function (): void { it('Should be able to add folder with reserved name as long as not at project root', async function (): Promise { const folderName = 'folder1'; - const stub = sinon.stub(vscode.window, 'showInputBox').resolves(folderName); + sinon.stub(vscode.window, 'showInputBox').resolves(folderName); + sinon.stub(utils, 'sanitizeStringForFilename').returns(folderName); const projController = new ProjectsController(testContext.outputChannel); let project = await testUtils.createTestProject(baselines.openProjectFileBaseline); @@ -197,7 +201,7 @@ describe('ProjectsController', function (): void { // make sure it's ok to add these folders if they aren't where the reserved folders are at the root of the project let node = projectRoot.children.find(c => c.friendlyName === 'Tables'); - stub.restore(); + sinon.restore(); for (let i in reservedProjectFolders) { // reload project project = await Project.openProject(project.projectFilePath); @@ -208,13 +212,14 @@ describe('ProjectsController', function (): void { async function verifyFolderAdded(folderName: string, projController: ProjectsController, project: Project, node: BaseProjectTreeItem): Promise { const beforeFileCount = project.files.length; let beforeFiles = project.files.map(f => f.relativePath); - const stub = sinon.stub(vscode.window, 'showInputBox').resolves(folderName); + sinon.stub(vscode.window, 'showInputBox').resolves(folderName); + sinon.stub(utils, 'sanitizeStringForFilename').returns(folderName); await projController.addFolderPrompt(createWorkspaceTreeItem(node)); // reload project project = await Project.openProject(project.projectFilePath); should(project.files.length).equal(beforeFileCount + 1, `File count should be increased by one after adding the folder ${folderName}. before files: ${JSON.stringify(beforeFiles)}/n after files: ${JSON.stringify(project.files.map(f => f.relativePath))}`); - stub.restore(); + sinon.restore(); } async function verifyFolderNotAdded(folderName: string, projController: ProjectsController, project: Project, node: BaseProjectTreeItem): Promise { @@ -386,12 +391,14 @@ describe('ProjectsController', function (): void { const project = await testUtils.createTestProject(baselines.newProjectFileBaseline); sinon.stub(vscode.window, 'showInputBox').resolves(preDeployScriptName); + sinon.stub(utils, 'sanitizeStringForFilename').returns(preDeployScriptName); should(project.preDeployScripts.length).equal(0, 'There should be no pre deploy scripts'); await projController.addItemPrompt(project, '', { itemType: ItemType.preDeployScript }); should(project.preDeployScripts.length).equal(1, `Pre deploy script should be successfully added. ${project.preDeployScripts.length}, ${project.files.length}`); sinon.restore(); sinon.stub(vscode.window, 'showInputBox').resolves(postDeployScriptName); + sinon.stub(utils, 'sanitizeStringForFilename').returns(postDeployScriptName); should(project.postDeployScripts.length).equal(0, 'There should be no post deploy scripts'); await projController.addItemPrompt(project, '', { itemType: ItemType.postDeployScript }); should(project.postDeployScripts.length).equal(1, 'Post deploy script should be successfully added');