diff --git a/extensions/sql-database-projects/resources/templates/newSqlProjectTemplate.xml b/extensions/sql-database-projects/resources/templates/newSqlProjectTemplate.xml index 6ebef84f7c..0e69c4d9f6 100644 --- a/extensions/sql-database-projects/resources/templates/newSqlProjectTemplate.xml +++ b/extensions/sql-database-projects/resources/templates/newSqlProjectTemplate.xml @@ -61,4 +61,7 @@ + + + diff --git a/extensions/sql-database-projects/src/common/constants.ts b/extensions/sql-database-projects/src/common/constants.ts index 5ed000bc8c..843deb8408 100644 --- a/extensions/sql-database-projects/src/common/constants.ts +++ b/extensions/sql-database-projects/src/common/constants.ts @@ -111,6 +111,11 @@ export const Include = 'Include'; export const Import = 'Import'; export const Project = 'Project'; export const Condition = 'Condition'; +export const Target = 'Target'; +export const Name = 'Name'; +export const AfterCleanTarget = 'AfterClean'; +export const Delete = 'Delete'; +export const Files = 'Files'; export const PackageReference = 'PackageReference'; export const Version = 'Version'; export const PrivateAssets = 'PrivateAssets'; @@ -128,6 +133,7 @@ export const SqlDbPresentCondition = '\'$(SQLDBExtensionsRefPath)\' != \'\''; export const SqlDbNotPresentCondition = '\'$(SQLDBExtensionsRefPath)\' == \'\''; export const RoundTripSqlDbPresentCondition = '\'$(NetCoreBuild)\' != \'true\' AND \'$(SQLDBExtensionsRefPath)\' != \'\''; export const RoundTripSqlDbNotPresentCondition = '\'$(NetCoreBuild)\' != \'true\' AND \'$(SQLDBExtensionsRefPath)\' == \'\''; +export const ProjJsonToClean = '$(BaseIntermediateOutputPath)\\project.assets.json'; // SqlProj Reference Assembly Information export const NETFrameworkAssembly = 'Microsoft.NETFramework.ReferenceAssemblies'; diff --git a/extensions/sql-database-projects/src/models/project.ts b/extensions/sql-database-projects/src/models/project.ts index 07df32ffde..45d25dc29a 100644 --- a/extensions/sql-database-projects/src/models/project.ts +++ b/extensions/sql-database-projects/src/models/project.ts @@ -78,6 +78,7 @@ export class Project { await fs.copyFile(this.projectFilePath, this.projectFilePath + '_backup'); await this.updateImportToSupportRoundTrip(); await this.updatePackageReferenceInProjFile(); + await this.updateAfterCleanTargetInProjFile(); } private async updateImportToSupportRoundTrip(): Promise { @@ -99,6 +100,30 @@ export class Project { await this.updateImportedTargetsToProjFile(constants.NetCoreCondition, constants.NetCoreTargets, undefined); } + private async updateAfterCleanTargetInProjFile(): Promise { + // Search if clean target already present, update it + for (let i = 0; i < this.projFileXmlDoc.documentElement.getElementsByTagName(constants.Target).length; i++) { + const afterCleanNode = this.projFileXmlDoc.documentElement.getElementsByTagName(constants.Target)[i]; + const name = afterCleanNode.getAttribute(constants.Name); + if (name === constants.AfterCleanTarget) { + return await this.createCleanFileNode(afterCleanNode); + } + } + + // If clean target not found, create new + const afterCleanNode = this.projFileXmlDoc.createElement(constants.Target); + afterCleanNode.setAttribute(constants.Name, constants.AfterCleanTarget); + this.projFileXmlDoc.documentElement.appendChild(afterCleanNode); + await this.createCleanFileNode(afterCleanNode); + } + + private async createCleanFileNode(parentNode: any): Promise { + const deleteFileNode = this.projFileXmlDoc.createElement(constants.Delete); + deleteFileNode.setAttribute(constants.Files, constants.ProjJsonToClean); + parentNode.appendChild(deleteFileNode); + await this.serializeToProjFile(this.projFileXmlDoc); + } + /** * Adds a folder to the project, and saves the project file * @param relativeFolderPath Relative path of the folder diff --git a/extensions/sql-database-projects/src/test/baselines/SSDTProjectAfterUpdateBaseline.xml b/extensions/sql-database-projects/src/test/baselines/SSDTProjectAfterUpdateBaseline.xml index 9728cd79ab..f3bf53f0b5 100644 --- a/extensions/sql-database-projects/src/test/baselines/SSDTProjectAfterUpdateBaseline.xml +++ b/extensions/sql-database-projects/src/test/baselines/SSDTProjectAfterUpdateBaseline.xml @@ -73,4 +73,7 @@ + + + diff --git a/extensions/sql-database-projects/src/test/baselines/SSDTProjectBaselineWithCleanTarget.xml b/extensions/sql-database-projects/src/test/baselines/SSDTProjectBaselineWithCleanTarget.xml new file mode 100644 index 0000000000..c03b6dfd1b --- /dev/null +++ b/extensions/sql-database-projects/src/test/baselines/SSDTProjectBaselineWithCleanTarget.xml @@ -0,0 +1,75 @@ + + + + Debug + AnyCPU + TestProjectName + 2.0 + 4.1 + {BA5EBA11-C0DE-5EA7-ACED-BABB1E70A575} + Microsoft.Data.Tools.Schema.Sql.Sql130DatabaseSchemaProvider + Database + + + TestProjectName + TestProjectName + 1033, CI + BySchemaAndSchemaType + True + v4.5 + CS + Properties + False + True + True + + + bin\Release\ + $(MSBuildProjectName).sql + False + pdbonly + true + false + true + prompt + 4 + + + bin\Debug\ + $(MSBuildProjectName).sql + false + true + full + false + true + true + prompt + 4 + + + 11.0 + + True + 11.0 + + + + + + + + + + + + + + + + + + + + + + diff --git a/extensions/sql-database-projects/src/test/baselines/SSDTProjectBaselineWithCleanTargetAfterUpdate.xml b/extensions/sql-database-projects/src/test/baselines/SSDTProjectBaselineWithCleanTargetAfterUpdate.xml new file mode 100644 index 0000000000..c39272fd87 --- /dev/null +++ b/extensions/sql-database-projects/src/test/baselines/SSDTProjectBaselineWithCleanTargetAfterUpdate.xml @@ -0,0 +1,80 @@ + + + + Debug + AnyCPU + TestProjectName + 2.0 + 4.1 + {BA5EBA11-C0DE-5EA7-ACED-BABB1E70A575} + Microsoft.Data.Tools.Schema.Sql.Sql130DatabaseSchemaProvider + Database + + + TestProjectName + TestProjectName + 1033, CI + BySchemaAndSchemaType + True + v4.5 + CS + Properties + False + True + True + + + bin\Release\ + $(MSBuildProjectName).sql + False + pdbonly + true + false + true + prompt + 4 + + + bin\Debug\ + $(MSBuildProjectName).sql + false + true + full + false + true + true + prompt + 4 + + + 11.0 + + True + 11.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/extensions/sql-database-projects/src/test/baselines/baselines.ts b/extensions/sql-database-projects/src/test/baselines/baselines.ts index b233850422..8a2872bcb9 100644 --- a/extensions/sql-database-projects/src/test/baselines/baselines.ts +++ b/extensions/sql-database-projects/src/test/baselines/baselines.ts @@ -12,6 +12,8 @@ export let openProjectFileBaseline: string; export let openDataSourcesBaseline: string; export let SSDTProjectFileBaseline: string; export let SSDTProjectAfterUpdateBaseline: string; +export let SSDTProjectBaselineWithCleanTarget: string; +export let SSDTProjectBaselineWithCleanTargetAfterUpdate: string; const baselineFolderPath = __dirname; @@ -21,6 +23,8 @@ export async function loadBaselines() { openDataSourcesBaseline = await loadBaseline(baselineFolderPath, 'openDataSourcesBaseline.json'); SSDTProjectFileBaseline = await loadBaseline(baselineFolderPath, 'SSDTProjectBaseline.xml'); SSDTProjectAfterUpdateBaseline = await loadBaseline(baselineFolderPath, 'SSDTProjectAfterUpdateBaseline.xml'); + SSDTProjectBaselineWithCleanTarget = await loadBaseline(baselineFolderPath, 'SSDTProjectBaselineWithCleanTarget.xml'); + SSDTProjectBaselineWithCleanTargetAfterUpdate = await loadBaseline(baselineFolderPath, 'SSDTProjectBaselineWithCleanTargetAfterUpdate.xml'); } async function loadBaseline(baselineFolderPath: string, fileName: string): Promise { diff --git a/extensions/sql-database-projects/src/test/baselines/newSqlProjectBaseline.xml b/extensions/sql-database-projects/src/test/baselines/newSqlProjectBaseline.xml index 7f2fb66c3e..7521477225 100644 --- a/extensions/sql-database-projects/src/test/baselines/newSqlProjectBaseline.xml +++ b/extensions/sql-database-projects/src/test/baselines/newSqlProjectBaseline.xml @@ -61,4 +61,7 @@ + + + diff --git a/extensions/sql-database-projects/src/test/baselines/openSqlProjectBaseline.xml b/extensions/sql-database-projects/src/test/baselines/openSqlProjectBaseline.xml index 86b4eeaeb0..a2c072f2d5 100644 --- a/extensions/sql-database-projects/src/test/baselines/openSqlProjectBaseline.xml +++ b/extensions/sql-database-projects/src/test/baselines/openSqlProjectBaseline.xml @@ -79,4 +79,7 @@ master + + + diff --git a/extensions/sql-database-projects/src/test/project.test.ts b/extensions/sql-database-projects/src/test/project.test.ts index a1cdd69664..789afed671 100644 --- a/extensions/sql-database-projects/src/test/project.test.ts +++ b/extensions/sql-database-projects/src/test/project.test.ts @@ -157,16 +157,24 @@ describe('Project: round trip updates', function (): void { }); it('Should update SSDT project to work in ADS', async function (): Promise { - projFilePath = await testUtils.createTestSqlProjFile(baselines.SSDTProjectFileBaseline); - const project: Project = new Project(projFilePath); - await project.readProjFile(); + await testUpdateInRoundTrip(baselines.SSDTProjectFileBaseline, baselines.SSDTProjectAfterUpdateBaseline); + }); - await project.updateProjectForRoundTrip(); - - should(await exists(projFilePath + '_backup')).equal(true); // backup file should be generated before the project is updated - should(project.importedTargets.length).equal(3); // additional target added by updateProjectForRoundTrip method - - let projFileText = (await fs.readFile(projFilePath)).toString(); - should(projFileText).equal(baselines.SSDTProjectAfterUpdateBaseline.trim()); + it('Should update SSDT project to work in ADS handling pre-exsiting targets', async function (): Promise { + await testUpdateInRoundTrip(baselines.SSDTProjectBaselineWithCleanTarget, baselines.SSDTProjectBaselineWithCleanTargetAfterUpdate); }); }); + +async function testUpdateInRoundTrip(fileBeforeupdate: string, fileAfterUpdate:string) : Promise { + projFilePath = await testUtils.createTestSqlProjFile(fileBeforeupdate); + const project: Project = new Project(projFilePath); + await project.readProjFile(); + + await project.updateProjectForRoundTrip(); + + should(await exists(projFilePath + '_backup')).equal(true); // backup file should be generated before the project is updated + should(project.importedTargets.length).equal(3); // additional target added by updateProjectForRoundTrip method + + let projFileText = (await fs.readFile(projFilePath)).toString(); + should(projFileText).equal(fileAfterUpdate.trim()); +}