From 30c55193dc9c575ca93bfa38efaef9860eb2dbec Mon Sep 17 00:00:00 2001 From: Z Chen <13544267+zijchen@users.noreply.github.com> Date: Tue, 12 Nov 2024 16:00:28 -0800 Subject: [PATCH 1/4] Fix target platform when creating project from database via quickpick (#26049) * Create project from quickpick * Fix test * Fix test --- .../src/controllers/projectController.ts | 5 +- .../createProjectFromDatabaseQuickpick.ts | 12 ++- ...createProjectFromDatabaseQuickpick.test.ts | 93 ++++++++++++------- 3 files changed, 72 insertions(+), 38 deletions(-) diff --git a/extensions/sql-database-projects/src/controllers/projectController.ts b/extensions/sql-database-projects/src/controllers/projectController.ts index 528b52ede9d3..9f3bec1036c7 100644 --- a/extensions/sql-database-projects/src/controllers/projectController.ts +++ b/extensions/sql-database-projects/src/controllers/projectController.ts @@ -1581,10 +1581,7 @@ export class ProjectsController { const databaseName = (await utils.getVscodeMssqlApi()).getDatabaseNameFromTreeNode(treeNodeContext); (profile as mssqlVscode.IConnectionInfo).database = databaseName; } - const model = await createNewProjectFromDatabaseWithQuickpick(profile as mssqlVscode.IConnectionInfo); - if (model) { - await this.createProjectFromDatabaseCallback(model, profile as mssqlVscode.IConnectionInfo, (profile as mssqlVscode.IConnectionInfo)?.server); - } + await createNewProjectFromDatabaseWithQuickpick(profile as mssqlVscode.IConnectionInfo, this.createProjectFromDatabaseCallback); return undefined; } diff --git a/extensions/sql-database-projects/src/dialogs/createProjectFromDatabaseQuickpick.ts b/extensions/sql-database-projects/src/dialogs/createProjectFromDatabaseQuickpick.ts index d49390bb7db9..d87246bfaa27 100644 --- a/extensions/sql-database-projects/src/dialogs/createProjectFromDatabaseQuickpick.ts +++ b/extensions/sql-database-projects/src/dialogs/createProjectFromDatabaseQuickpick.ts @@ -17,8 +17,9 @@ import { getSDKStyleProjectInfo } from './quickpickHelper'; /** * 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 + * @param createProjectFromDatabaseCallback Optional callback function to create the project from the user inputs */ -export async function createNewProjectFromDatabaseWithQuickpick(connectionInfo?: IConnectionInfo): Promise { +export async function createNewProjectFromDatabaseWithQuickpick(connectionInfo?: IConnectionInfo, createProjectFromDatabaseCallback?: (model: ImportDataModel, connectionInfo?: string | IConnectionInfo, serverName?: string) => Promise): Promise { const vscodeMssqlApi = await getVscodeMssqlApi(); // 1. Select connection @@ -162,7 +163,7 @@ export async function createNewProjectFromDatabaseWithQuickpick(connectionInfo?: return; } - return { + const model = { connectionUri: connectionUri, database: selectedDatabase, projName: projectName, @@ -171,5 +172,10 @@ export async function createNewProjectFromDatabaseWithQuickpick(connectionInfo?: extractTarget: mapExtractTargetEnum(folderStructure), sdkStyle: sdkStyle, includePermissions: includePermissions - }; + } as ImportDataModel; + + // 8. Create the project using the callback + if (createProjectFromDatabaseCallback) { + await createProjectFromDatabaseCallback(model, connectionProfile, connectionProfile.server); + } } 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 23f0d9c2809e..61906bd90f8e 100644 --- a/extensions/sql-database-projects/src/test/dialogs/createProjectFromDatabaseQuickpick.test.ts +++ b/extensions/sql-database-projects/src/test/dialogs/createProjectFromDatabaseQuickpick.test.ts @@ -40,35 +40,44 @@ describe('Create Project From Database Quickpick', () => { //promptForConnection spy to verify test const promptForConnectionSpy = sinon.stub(testUtils.vscodeMssqlIExtension.object, 'promptForConnection').withArgs(sinon.match.any).resolves(undefined); - const model = await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(); + //createProjectFromDatabaseQuickpick spy to verify test + const createProjectFromDatabaseCallbackSpy = sinon.stub().resolves(); + + await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(undefined, createProjectFromDatabaseCallbackSpy); //verify that prompt for connection was called should(promptForConnectionSpy.calledOnce).be.true('promptForConnection should have been called'); - //verify quickpick exited with undefined, since promptForConnection was set to cancel (resolves to undefined) - should.equal(model, undefined); + //verify create project callback was not called, since promptForConnection was set to cancel (resolves to undefined) + should(createProjectFromDatabaseCallbackSpy.notCalled).be.true('createProjectFromDatabaseCallback should not have been called'); }); it('Should not prompt for connection when connectionInfo is provided and exit when db is not selected', async function (): Promise { //promptForConnection spy to verify test const promptForConnectionSpy = sinon.stub(testUtils.vscodeMssqlIExtension.object, 'promptForConnection').withArgs(sinon.match.any).resolves(undefined); + //createProjectFromDatabaseQuickpick spy to verify test + const createProjectFromDatabaseCallbackSpy = sinon.stub().resolves(); + //user chooses connection sinon.stub(testUtils.vscodeMssqlIExtension.object, 'connect').resolves('testConnectionURI'); sinon.stub(testUtils.vscodeMssqlIExtension.object, 'listDatabases').withArgs(sinon.match.any).resolves(dbList); // user chooses to cancel when prompted for database sinon.stub(vscode.window, 'showQuickPick').resolves(undefined); - const model = await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo); + await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo, createProjectFromDatabaseCallbackSpy); //verify connection prompt wasn't presented, since connectionInfo was passed during the call should(promptForConnectionSpy.notCalled).be.true('promptForConnection should not be called when connectionInfo is provided'); - //verify quickpick exited with undefined, since database wasn't selected (resolved to undefined) - should.equal(model, undefined); + //verify create project callback was not called, since database wasn't selected (resolved to undefined) + should(createProjectFromDatabaseCallbackSpy.notCalled).be.true('createProjectFromDatabaseCallback should not have been called'); }); it('Should exit when project name is not selected', async function (): Promise { + //createProjectFromDatabaseQuickpick spy to verify test + const createProjectFromDatabaseCallbackSpy = sinon.stub().resolves(); + //user chooses connection and database sinon.stub(testUtils.vscodeMssqlIExtension.object, 'connect').resolves('testConnectionURI'); sinon.stub(testUtils.vscodeMssqlIExtension.object, 'listDatabases').withArgs(sinon.match.any).resolves(dbList); @@ -78,13 +87,16 @@ describe('Create Project From Database Quickpick', () => { // user chooses to cancel when prompted to enter project name inputBoxStub.onSecondCall().resolves(undefined); - const model = await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo); + await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo, createProjectFromDatabaseCallbackSpy); - //verify showInputBox exited with undefined, since project name wasn't selected (resolved to undefined) - should.equal(model, undefined); + //verify create project callback was not called, since project name wasn't selected (resolved to undefined) + should(createProjectFromDatabaseCallbackSpy.notCalled).be.true('createProjectFromDatabaseCallback should not have been called'); }); it('Should exit when project location is not selected', async function (): Promise { + //createProjectFromDatabaseQuickpick spy to verify test + const createProjectFromDatabaseCallbackSpy = sinon.stub().resolves(); + //user chooses connection and database sinon.stub(testUtils.vscodeMssqlIExtension.object, 'connect').resolves('testConnectionURI'); sinon.stub(testUtils.vscodeMssqlIExtension.object, 'listDatabases').withArgs(sinon.match.any).resolves(dbList); @@ -94,13 +106,16 @@ describe('Create Project From Database Quickpick', () => { //user chooses to exit quickPickStub.onSecondCall().resolves(undefined); - const model = await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo); + await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo, createProjectFromDatabaseCallbackSpy); - //verify showQuickPick exited with undefined, since project location wasn't selected (resolved to undefined) - should.equal(model, undefined); + //verify create project callback was not called, since project location wasn't selected (resolved to undefined) + should(createProjectFromDatabaseCallbackSpy.notCalled).be.true('createProjectFromDatabaseCallback should not have been called'); }); it('Should exit when project location is not selected (test repeatedness for project location)', async function (): Promise { + //createProjectFromDatabaseQuickpick spy to verify test + const createProjectFromDatabaseCallbackSpy = sinon.stub().resolves(); + //user chooses connection and database sinon.stub(testUtils.vscodeMssqlIExtension.object, 'connect').resolves('testConnectionURI'); sinon.stub(testUtils.vscodeMssqlIExtension.object, 'listDatabases').withArgs(sinon.match.any).resolves(dbList); @@ -122,13 +137,16 @@ describe('Create Project From Database Quickpick', () => { //user chooses to exit quickPickStub.onCall(4).resolves(undefined); - const model = await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo); + await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo, createProjectFromDatabaseCallbackSpy); - //verify showQuickPick exited with undefined, since project location wasn't selected (resolved to undefined) - should.equal(model, undefined); + //verify create project callback was not called, since project location wasn't selected (resolved to undefined) + should(createProjectFromDatabaseCallbackSpy.notCalled).be.true('createProjectFromDatabaseCallback should not have been called'); }); it('Should exit when folder structure is not selected and folder is selected through browsing (test repeatedness for project location)', async function (): Promise { + //createProjectFromDatabaseQuickpick spy to verify test + const createProjectFromDatabaseCallbackSpy = sinon.stub().resolves(); + //user chooses connection and database sinon.stub(testUtils.vscodeMssqlIExtension.object, 'connect').resolves('testConnectionURI'); sinon.stub(testUtils.vscodeMssqlIExtension.object, 'listDatabases').withArgs(sinon.match.any).resolves(dbList); @@ -146,13 +164,16 @@ describe('Create Project From Database Quickpick', () => { //user chooses to exit when prompted for folder structure quickPickStub.onCall(3).resolves(undefined); - const model = await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo); + await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo, createProjectFromDatabaseCallbackSpy); - //verify showQuickPick exited with undefined, since folder structure wasn't selected (resolved to undefined) - should.equal(model, undefined); + //verify create project callback was not called, since folder structure wasn't selected (resolved to undefined) + should(createProjectFromDatabaseCallbackSpy.notCalled).be.true('createProjectFromDatabaseCallback should not have been called'); }); it('Should exit when folder structure is not selected and existing folder/file location is selected', async function (): Promise { + //createProjectFromDatabaseQuickpick spy to verify test + const createProjectFromDatabaseCallbackSpy = sinon.stub().resolves(); + //create folder and project file const projectFileName = 'TestProject'; const testProjectFilePath = await generateTestFolderPath(this.test); @@ -172,15 +193,18 @@ describe('Create Project From Database Quickpick', () => { //user chooses to exit when prompted for folder structure quickPickStub.onCall(3).resolves(undefined); - const model = await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo); + await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo, createProjectFromDatabaseCallbackSpy); await deleteGeneratedTestFolder(); - //verify showQuickPick exited with undefined, since folder structure wasn't selected (resolved to undefined) - should.equal(model, undefined); + //verify create project callback was not called, since folder structure wasn't selected (resolved to undefined) + should(createProjectFromDatabaseCallbackSpy.notCalled).be.true('createProjectFromDatabaseCallback should not have been called'); }); it('Should exit when include permissions is not selected', async function (): Promise { + //createProjectFromDatabaseQuickpick spy to verify test + const createProjectFromDatabaseCallbackSpy = sinon.stub().resolves(); + //user chooses connection and database sinon.stub(testUtils.vscodeMssqlIExtension.object, 'connect').resolves('testConnectionURI'); sinon.stub(testUtils.vscodeMssqlIExtension.object, 'listDatabases').withArgs(sinon.match.any).resolves(dbList); @@ -194,13 +218,16 @@ describe('Create Project From Database Quickpick', () => { //user chooses to exit when prompted for include permissions quickPickStub.onCall(3).resolves(undefined); - const model = await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo); + await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo, createProjectFromDatabaseCallbackSpy); - //verify showQuickPick exited with undefined, since include permissions wasn't selected (resolved to undefined) - should.equal(model, undefined); + //verify create project callback was not called, since include permissions wasn't selected (resolved to undefined) + should(createProjectFromDatabaseCallbackSpy.notCalled).be.true('createProjectFromDatabaseCallback should not have been called'); }); it('Should exit when sdk style project is not selected', async function (): Promise { + //createProjectFromDatabaseQuickpick spy to verify test + const createProjectFromDatabaseCallbackSpy = sinon.stub().resolves(); + //user chooses connection and database sinon.stub(testUtils.vscodeMssqlIExtension.object, 'connect').resolves('testConnectionURI'); sinon.stub(testUtils.vscodeMssqlIExtension.object, 'listDatabases').withArgs(sinon.match.any).resolves(dbList); @@ -216,13 +243,16 @@ describe('Create Project From Database Quickpick', () => { //user chooses to exit when prompted for sdk style project sinon.stub(quickpickHelper, 'getSDKStyleProjectInfo').resolves(undefined); - const model = await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo); + await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo, createProjectFromDatabaseCallbackSpy); - //verify showQuickPick exited with undefined, since sdk style project wasn't selected (resolved to undefined) - should.equal(model, undefined); + //verify create project callback was not called, since sdk style project wasn't selected (resolved to undefined) + should(createProjectFromDatabaseCallbackSpy.notCalled).be.true('createProjectFromDatabaseCallback should not have been called'); }); - it('Should create correct import data model when all the information is provided', async function (): Promise { + it('Should create project when all the information is provided', async function (): Promise { + //createProjectFromDatabaseQuickpick spy to verify test + const createProjectFromDatabaseCallbackSpy = sinon.stub().resolves(); + //user chooses connection and database sinon.stub(testUtils.vscodeMssqlIExtension.object, 'connect').resolves('testConnectionURI'); sinon.stub(testUtils.vscodeMssqlIExtension.object, 'listDatabases').withArgs(sinon.match.any).resolves(dbList); @@ -238,7 +268,7 @@ describe('Create Project From Database Quickpick', () => { //user chooses sdk style project to be true sinon.stub(quickpickHelper, 'getSDKStyleProjectInfo').resolves(true); - const model = await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo); + await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo, createProjectFromDatabaseCallbackSpy); const expectedImportDataModel: ImportDataModel = { connectionUri: 'testConnectionURI', @@ -251,7 +281,8 @@ describe('Create Project From Database Quickpick', () => { includePermissions: false }; - //verify the model is correctly generated - should(model!).deepEqual(expectedImportDataModel); + //verify create project callback was called with the correct model + should(createProjectFromDatabaseCallbackSpy.calledOnce).be.true('createProjectFromDatabaseCallback should have been called'); + should(createProjectFromDatabaseCallbackSpy.calledWithMatch(expectedImportDataModel)).be.true('createProjectFromDatabaseCallback should have been called with the correct model'); }); }); From dc75b4c61b4ae63e38ea51e6490b7f3aa975bf43 Mon Sep 17 00:00:00 2001 From: Z Chen <13544267+zijchen@users.noreply.github.com> Date: Wed, 13 Nov 2024 09:47:13 -0800 Subject: [PATCH 2/4] Fix a bug in my last PR (#26053) * Create project from quickpick * Fix test * Fix test * Fix a bug in my last PR --- .../sql-database-projects/src/controllers/projectController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/sql-database-projects/src/controllers/projectController.ts b/extensions/sql-database-projects/src/controllers/projectController.ts index 9f3bec1036c7..3fa998ad2c4c 100644 --- a/extensions/sql-database-projects/src/controllers/projectController.ts +++ b/extensions/sql-database-projects/src/controllers/projectController.ts @@ -1581,7 +1581,7 @@ export class ProjectsController { const databaseName = (await utils.getVscodeMssqlApi()).getDatabaseNameFromTreeNode(treeNodeContext); (profile as mssqlVscode.IConnectionInfo).database = databaseName; } - await createNewProjectFromDatabaseWithQuickpick(profile as mssqlVscode.IConnectionInfo, this.createProjectFromDatabaseCallback); + await createNewProjectFromDatabaseWithQuickpick(profile as mssqlVscode.IConnectionInfo, (model: ImportDataModel, connectionInfo?: string | mssqlVscode.IConnectionInfo, serverName?: string) => this.createProjectFromDatabaseCallback(model, connectionInfo, serverName)); return undefined; } From 698057b92bf851467cb69dba2b172eaf564f8087 Mon Sep 17 00:00:00 2001 From: Z Chen <13544267+zijchen@users.noreply.github.com> Date: Tue, 10 Dec 2024 11:15:39 -0800 Subject: [PATCH 3/4] Update default SDK version to 0.2.5-preview (#26095) * Update README.md * Update buildHelper.ts * Update newSdkSqlProjectTemplate.xml --- extensions/sql-database-projects/README.md | 2 +- .../resources/templates/newSdkSqlProjectTemplate.xml | 2 +- extensions/sql-database-projects/src/tools/buildHelper.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/sql-database-projects/README.md b/extensions/sql-database-projects/README.md index 0a97ab014de8..6804fe7bb511 100644 --- a/extensions/sql-database-projects/README.md +++ b/extensions/sql-database-projects/README.md @@ -35,7 +35,7 @@ Learn more about the SQL Database Projects extension in the documentation: https ### General Settings - `sqlDatabaseProjects.dotnetSDK Location`: The path to the folder containing the `dotnet` folder for the .NET SDK. If not set, the extension will attempt to find the .NET SDK on the system. -- `sqlDatabaseProjects.microsoftBuildSqlVersion`: Version of Microsoft.Build.Sql binaries used when building SQL projects that are not SDK-style SQL projects. If not set, the extension will use Microsoft.Build.Sql 0.2.0-preview. +- `sqlDatabaseProjects.microsoftBuildSqlVersion`: Version of Microsoft.Build.Sql binaries used when building SQL projects that are not SDK-style SQL projects. If not set, the extension will use Microsoft.Build.Sql 0.2.5-preview. - `sqlDatabaseProjects.netCoreDoNotAsk`: When true, no longer prompts to install .NET SDK when a supported installation is not found. - `sqlDatabaseProjects.collapseProjectNodes`: Option to set the default state of the project nodes in the database projects view to collapsed. If not set, the extension will default to expanded. diff --git a/extensions/sql-database-projects/resources/templates/newSdkSqlProjectTemplate.xml b/extensions/sql-database-projects/resources/templates/newSdkSqlProjectTemplate.xml index 6b108ac8bd42..50fb5d2cde69 100644 --- a/extensions/sql-database-projects/resources/templates/newSdkSqlProjectTemplate.xml +++ b/extensions/sql-database-projects/resources/templates/newSdkSqlProjectTemplate.xml @@ -1,6 +1,6 @@ - + @@PROJECT_NAME@@ {@@PROJECT_GUID@@} diff --git a/extensions/sql-database-projects/src/tools/buildHelper.ts b/extensions/sql-database-projects/src/tools/buildHelper.ts index f161661ca9d3..a76709e6d67c 100644 --- a/extensions/sql-database-projects/src/tools/buildHelper.ts +++ b/extensions/sql-database-projects/src/tools/buildHelper.ts @@ -56,7 +56,7 @@ export class BuildHelper { public async ensureDacFxDllsPresence(outputChannel: vscode.OutputChannel): Promise { const sdkName = 'Microsoft.Build.Sql'; - const microsoftBuildSqlDefaultVersion = '0.2.0-preview'; // default version of Microsoft.Build.Sql nuget to use for building legacy style projects, update in README when updating this + const microsoftBuildSqlDefaultVersion = '0.2.5-preview'; // default version of Microsoft.Build.Sql nuget to use for building legacy style projects, update in README when updating this const dacFxBuildFiles: string[] = [ 'Microsoft.Data.SqlClient.dll', From fd9afe6d9d227a422cec204380a04c239a244474 Mon Sep 17 00:00:00 2001 From: Z Chen <13544267+zijchen@users.noreply.github.com> Date: Wed, 11 Dec 2024 12:53:45 -0800 Subject: [PATCH 4/4] Update sql-database-projects extension to 1.4.5 (#26099) --- extensions/sql-database-projects/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/sql-database-projects/package.json b/extensions/sql-database-projects/package.json index 704986dc8817..d258acdcd672 100644 --- a/extensions/sql-database-projects/package.json +++ b/extensions/sql-database-projects/package.json @@ -2,7 +2,7 @@ "name": "sql-database-projects", "displayName": "SQL Database Projects", "description": "Enables users to develop and publish database schemas for MSSQL Databases", - "version": "1.4.4", + "version": "1.4.5", "publisher": "Microsoft", "preview": false, "engines": {