From 0d485ffe2b587ad913ecd0f2184f1e4df4dfc3b9 Mon Sep 17 00:00:00 2001 From: Kim Santiago <31145923+kisantia@users.noreply.github.com> Date: Tue, 2 Nov 2021 11:32:25 -0700 Subject: [PATCH] fix DefaultValue getting loaded for publish profiles (#17526) * fix DefaultValue getting loaded for publish profiles * add comment --- .../sql-database-projects/src/common/utils.ts | 8 +++++-- .../src/models/project.ts | 2 +- .../models/publishProfile/publishProfile.ts | 2 +- .../src/test/baselines/baselines.ts | 2 ++ ...ishProfileDefaultValueBaseline.publish.xml | 15 ++++++++++++ .../src/test/publishProfile.test.ts | 24 +++++++++++++++---- 6 files changed, 44 insertions(+), 9 deletions(-) create mode 100644 extensions/sql-database-projects/src/test/baselines/publishProfileDefaultValueBaseline.publish.xml diff --git a/extensions/sql-database-projects/src/common/utils.ts b/extensions/sql-database-projects/src/common/utils.ts index fbb38a1b39..01a7d83dac 100644 --- a/extensions/sql-database-projects/src/common/utils.ts +++ b/extensions/sql-database-projects/src/common/utils.ts @@ -156,14 +156,18 @@ export function convertSlashesForSqlProj(filePath: string): string { /** * Read SQLCMD variables from xmlDoc and return them * @param xmlDoc xml doc to read SQLCMD variables from. Format must be the same that sqlproj and publish profiles use + * @param publishProfile true if reading from publish profile */ -export function readSqlCmdVariables(xmlDoc: any): Record { +export function readSqlCmdVariables(xmlDoc: any, publishProfile: boolean): Record { let sqlCmdVariables: Record = {}; for (let i = 0; i < xmlDoc.documentElement.getElementsByTagName(constants.SqlCmdVariable)?.length; i++) { const sqlCmdVar = xmlDoc.documentElement.getElementsByTagName(constants.SqlCmdVariable)[i]; const varName = sqlCmdVar.getAttribute(constants.Include); - if (sqlCmdVar.getElementsByTagName(constants.DefaultValue)[0] !== undefined) { + // Publish profiles only support Value, so don't use DefaultValue even if it's there + // SSDT uses the Value (like $(SqlCmdVar__1)) where there + // are local variable values you can set in VS in the properties. Since we don't support that in ADS, only DefaultValue is supported for sqlproj. + if (!publishProfile && sqlCmdVar.getElementsByTagName(constants.DefaultValue)[0] !== undefined) { // project file path sqlCmdVariables[varName] = sqlCmdVar.getElementsByTagName(constants.DefaultValue)[0].childNodes[0].nodeValue; } diff --git a/extensions/sql-database-projects/src/models/project.ts b/extensions/sql-database-projects/src/models/project.ts index b8e38f53d3..0d6340f91c 100644 --- a/extensions/sql-database-projects/src/models/project.ts +++ b/extensions/sql-database-projects/src/models/project.ts @@ -175,7 +175,7 @@ export class Project implements ISqlProject { } // find all SQLCMD variables to include - this._sqlCmdVariables = utils.readSqlCmdVariables(this.projFileXmlDoc); + this._sqlCmdVariables = utils.readSqlCmdVariables(this.projFileXmlDoc, false); // find all database references to include const references = this.projFileXmlDoc.documentElement.getElementsByTagName(constants.ArtifactReference); diff --git a/extensions/sql-database-projects/src/models/publishProfile/publishProfile.ts b/extensions/sql-database-projects/src/models/publishProfile/publishProfile.ts index e65726b6bf..59df94756e 100644 --- a/extensions/sql-database-projects/src/models/publishProfile/publishProfile.ts +++ b/extensions/sql-database-projects/src/models/publishProfile/publishProfile.ts @@ -53,7 +53,7 @@ export async function load(profileUri: vscode.Uri, dacfxService: utils.IDacFxSer const optionsResult = await dacfxService.getOptionsFromProfile(profileUri.fsPath); // get all SQLCMD variables to include from the profile - const sqlCmdVariables = utils.readSqlCmdVariables(profileXmlDoc); + const sqlCmdVariables = utils.readSqlCmdVariables(profileXmlDoc, true); return { databaseName: targetDbName, diff --git a/extensions/sql-database-projects/src/test/baselines/baselines.ts b/extensions/sql-database-projects/src/test/baselines/baselines.ts index e72202918b..dda3468e12 100644 --- a/extensions/sql-database-projects/src/test/baselines/baselines.ts +++ b/extensions/sql-database-projects/src/test/baselines/baselines.ts @@ -19,6 +19,7 @@ export let SSDTProjectBaselineWithBeforeBuildTarget: string; export let SSDTProjectBaselineWithBeforeBuildTargetAfterUpdate: string; export let publishProfileIntegratedSecurityBaseline: string; export let publishProfileSqlLoginBaseline: string; +export let publishProfileDefaultValueBaseline: string; export let openProjectWithProjectReferencesBaseline: string; export let openSqlProjectWithPrePostDeploymentError: string; export let openSqlProjectWithAdditionalSqlCmdVariablesBaseline: string; @@ -45,6 +46,7 @@ export async function loadBaselines() { SSDTProjectBaselineWithBeforeBuildTargetAfterUpdate = await loadBaseline(baselineFolderPath, 'SSDTProjectBaselineWithBeforeBuildTargetAfterUpdate.xml'); publishProfileIntegratedSecurityBaseline = await loadBaseline(baselineFolderPath, 'publishProfileIntegratedSecurityBaseline.publish.xml'); publishProfileSqlLoginBaseline = await loadBaseline(baselineFolderPath, 'publishProfileSqlLoginBaseline.publish.xml'); + publishProfileDefaultValueBaseline = await loadBaseline(baselineFolderPath, 'publishProfileDefaultValueBaseline.publish.xml'); openProjectWithProjectReferencesBaseline = await loadBaseline(baselineFolderPath, 'openSqlProjectWithProjectReferenceBaseline.xml'); openSqlProjectWithPrePostDeploymentError = await loadBaseline(baselineFolderPath, 'openSqlProjectWithPrePostDeploymentError.xml'); openSqlProjectWithAdditionalSqlCmdVariablesBaseline = await loadBaseline(baselineFolderPath, 'openSqlProjectWithAdditionalSqlCmdVariablesBaseline.xml'); diff --git a/extensions/sql-database-projects/src/test/baselines/publishProfileDefaultValueBaseline.publish.xml b/extensions/sql-database-projects/src/test/baselines/publishProfileDefaultValueBaseline.publish.xml new file mode 100644 index 0000000000..d658142e1c --- /dev/null +++ b/extensions/sql-database-projects/src/test/baselines/publishProfileDefaultValueBaseline.publish.xml @@ -0,0 +1,15 @@ + + + + True + targetDb + DatabaseProject1.sql + 1 + + + + defaultProdName + MyProdDatabase + + + diff --git a/extensions/sql-database-projects/src/test/publishProfile.test.ts b/extensions/sql-database-projects/src/test/publishProfile.test.ts index 84671b700b..c04e51e77b 100644 --- a/extensions/sql-database-projects/src/test/publishProfile.test.ts +++ b/extensions/sql-database-projects/src/test/publishProfile.test.ts @@ -31,7 +31,7 @@ describe('Publish profile tests', function (): void { it('Should read database name, integrated security connection string, and SQLCMD variables from publish profile', async function (): Promise { await baselines.loadBaselines(); - let profilePath = await testUtils.createTestFile(baselines.publishProfileIntegratedSecurityBaseline, 'publishProfile.publish.xml'); + const profilePath = await testUtils.createTestFile(baselines.publishProfileIntegratedSecurityBaseline, 'publishProfile.publish.xml'); const connectionResult = { connected: true, connectionId: 'connId', @@ -43,7 +43,7 @@ describe('Publish profile tests', function (): void { }); sinon.stub(azdata.connection, 'connect').resolves(connectionResult); - let result = await load(vscode.Uri.file(profilePath), testContext.dacFxService.object); + const result = await load(vscode.Uri.file(profilePath), testContext.dacFxService.object); should(result.databaseName).equal('targetDb'); should(Object.keys(result.sqlCmdVariables).length).equal(1); should(result.sqlCmdVariables['ProdDatabaseName']).equal('MyProdDatabase'); @@ -54,7 +54,7 @@ describe('Publish profile tests', function (): void { it('Should read database name, SQL login connection string, and SQLCMD variables from publish profile', async function (): Promise { await baselines.loadBaselines(); - let profilePath = await testUtils.createTestFile(baselines.publishProfileSqlLoginBaseline, 'publishProfile.publish.xml'); + const profilePath = await testUtils.createTestFile(baselines.publishProfileSqlLoginBaseline, 'publishProfile.publish.xml'); const connectionResult = { providerName: 'MSSQL', connectionId: 'connId', @@ -68,7 +68,7 @@ describe('Publish profile tests', function (): void { }); sinon.stub(azdata.connection, 'openConnectionDialog').resolves(connectionResult); - let result = await load(vscode.Uri.file(profilePath), testContext.dacFxService.object); + const result = await load(vscode.Uri.file(profilePath), testContext.dacFxService.object); should(result.databaseName).equal('targetDb'); should(Object.keys(result.sqlCmdVariables).length).equal(1); should(result.sqlCmdVariables['ProdDatabaseName']).equal('MyProdDatabase'); @@ -77,9 +77,23 @@ describe('Publish profile tests', function (): void { should(result.options).equal(mockDacFxOptionsResult.deploymentOptions); }); + it('Should read SQLCMD variables correctly from publish profile even if DefaultValue is used', async function (): Promise { + await baselines.loadBaselines(); + const profilePath = await testUtils.createTestFile(baselines.publishProfileDefaultValueBaseline, 'publishProfile.publish.xml'); + testContext.dacFxService.setup(x => x.getOptionsFromProfile(TypeMoq.It.isAny())).returns(async () => { + return Promise.resolve(mockDacFxOptionsResult); + }); + + const result = await load(vscode.Uri.file(profilePath), testContext.dacFxService.object); + should(Object.keys(result.sqlCmdVariables).length).equal(1); + + // the profile has both Value and DefaultValue, but Value should be the one used + should(result.sqlCmdVariables['ProdDatabaseName']).equal('MyProdDatabase'); + }); + it('Should throw error when connecting does not work', async function (): Promise { await baselines.loadBaselines(); - let profilePath = await testUtils.createTestFile(baselines.publishProfileIntegratedSecurityBaseline, 'publishProfile.publish.xml'); + const profilePath = await testUtils.createTestFile(baselines.publishProfileIntegratedSecurityBaseline, 'publishProfile.publish.xml'); sinon.stub(azdata.connection, 'connect').throws(new Error('Could not connect'));