diff --git a/extensions/sql-database-projects/src/common/constants.ts b/extensions/sql-database-projects/src/common/constants.ts index 9b6d1c3343..ef6ec4aa80 100644 --- a/extensions/sql-database-projects/src/common/constants.ts +++ b/extensions/sql-database-projects/src/common/constants.ts @@ -132,6 +132,7 @@ export const NetCoreTargets = '$(NETCoreTargetsPath)\\Microsoft.Data.Tools.Schem export const SqlDbTargets = '$(SQLDBExtensionsRefPath)\\Microsoft.Data.Tools.Schema.SqlTasks.targets'; export const MsBuildtargets = '$(MSBuildExtensionsPath)\\Microsoft\\VisualStudio\\v$(VisualStudioVersion)\\SSDT\\Microsoft.Data.Tools.Schema.SqlTasks.targets'; export const NetCoreCondition = '\'$(NetCoreBuild)\' == \'true\''; +export const NotNetCoreCondition = '\'$(NetCoreBuild)\' != \'true\''; export const SqlDbPresentCondition = '\'$(SQLDBExtensionsRefPath)\' != \'\''; export const SqlDbNotPresentCondition = '\'$(SQLDBExtensionsRefPath)\' == \'\''; export const RoundTripSqlDbPresentCondition = '\'$(NetCoreBuild)\' != \'true\' AND \'$(SQLDBExtensionsRefPath)\' != \'\''; diff --git a/extensions/sql-database-projects/src/controllers/projectController.ts b/extensions/sql-database-projects/src/controllers/projectController.ts index e20a18e7da..f681cf39cf 100644 --- a/extensions/sql-database-projects/src/controllers/projectController.ts +++ b/extensions/sql-database-projects/src/controllers/projectController.ts @@ -316,9 +316,9 @@ export class ProjectsController { if (databaseLocation === DatabaseReferenceLocation.differentDatabaseSameServer) { const databaseName = await this.getDatabaseName(dacpacFileLocation); - await project.addDatabaseReference(dacpacFileLocation, databaseLocation, false, databaseName); + await project.addDatabaseReference(dacpacFileLocation, databaseLocation, databaseName); } else { - await project.addDatabaseReference(dacpacFileLocation, databaseLocation, false); + await project.addDatabaseReference(dacpacFileLocation, databaseLocation); } } diff --git a/extensions/sql-database-projects/src/models/project.ts b/extensions/sql-database-projects/src/models/project.ts index d87e92b5f2..63a4e3d791 100644 --- a/extensions/sql-database-projects/src/models/project.ts +++ b/extensions/sql-database-projects/src/models/project.ts @@ -74,12 +74,14 @@ export class Project { // find all database references to include for (let r = 0; r < this.projFileXmlDoc.documentElement.getElementsByTagName(constants.ArtifactReference).length; r++) { - const filepath = this.projFileXmlDoc.documentElement.getElementsByTagName(constants.ArtifactReference)[r].getAttribute(constants.Include); - if (!filepath) { - throw new Error(constants.invalidDatabaseReference); - } + if (this.projFileXmlDoc.documentElement.getElementsByTagName(constants.ArtifactReference)[r].getAttribute(constants.Condition) !== constants.NotNetCoreCondition) { + const filepath = this.projFileXmlDoc.documentElement.getElementsByTagName(constants.ArtifactReference)[r].getAttribute(constants.Include); + if (!filepath) { + throw new Error(constants.invalidDatabaseReference); + } - this.databaseReferences.push(path.parse(filepath).name); + this.databaseReferences.push(path.parse(filepath).name); + } } } @@ -195,16 +197,20 @@ export class Project { */ public async addSystemDatabaseReference(name: SystemDatabase): Promise { let uri: Uri; + let ssdtUri: Uri; let dbName: string; if (name === SystemDatabase.master) { uri = this.getSystemDacpacUri(constants.masterDacpac); + ssdtUri = this.getSystemDacpacSsdtUri(constants.masterDacpac); dbName = constants.master; } else { uri = this.getSystemDacpacUri(constants.msdbDacpac); + ssdtUri = this.getSystemDacpacSsdtUri(constants.msdbDacpac); dbName = constants.msdb; } - await this.addDatabaseReference(uri, DatabaseReferenceLocation.differentDatabaseSameServer, true, dbName); + let systemDatabaseReferenceProjectEntry = new SystemDatabaseReferenceProjectEntry(uri, ssdtUri, dbName); + await this.addToProjFile(systemDatabaseReferenceProjectEntry); } public getSystemDacpacUri(dacpac: string): Uri { @@ -212,6 +218,11 @@ export class Project { return Uri.parse(path.join('$(NETCoreTargetsPath)', 'SystemDacpacs', version, dacpac)); } + public getSystemDacpacSsdtUri(dacpac: string): Uri { + let version = this.getProjectTargetPlatform(); + return Uri.parse(path.join('$(DacPacRootPath)', 'Extensions', 'Microsoft', 'SQLDB', 'Extensions', 'SqlServer', version, 'SqlSchemas', dacpac)); + } + public getProjectTargetPlatform(): string { // check for invalid DSP if (this.projFileXmlDoc.getElementsByTagName(constants.DSP).length !== 1 || this.projFileXmlDoc.getElementsByTagName(constants.DSP)[0].childNodes.length !== 1) { @@ -239,8 +250,8 @@ export class Project { * @param uri Uri of the dacpac * @param databaseName name of the database */ - public async addDatabaseReference(uri: Uri, databaseLocation: DatabaseReferenceLocation, isSystemDatabase: boolean, databaseName?: string): Promise { - let databaseReferenceEntry = new DatabaseReferenceProjectEntry(uri, databaseLocation, isSystemDatabase, databaseName); + public async addDatabaseReference(uri: Uri, databaseLocation: DatabaseReferenceLocation, databaseName?: string): Promise { + let databaseReferenceEntry = new DatabaseReferenceProjectEntry(uri, databaseLocation, databaseName); await this.addToProjFile(databaseReferenceEntry); } @@ -289,24 +300,41 @@ export class Project { } private addDatabaseReferenceToProjFile(entry: DatabaseReferenceProjectEntry): void { - const referenceNode = this.projFileXmlDoc.createElement(constants.ArtifactReference); - referenceNode.setAttribute(constants.Condition, constants.NetCoreCondition); - referenceNode.setAttribute(constants.Include, entry.isSystemDatabase ? entry.fsUri.fsPath.substring(1) : entry.fsUri.fsPath); // need to remove the leading slash for system database path for build to work on Windows + let referenceNode = this.projFileXmlDoc.createElement(constants.ArtifactReference); + const isSystemDatabaseProjectEntry = (entry).ssdtUri; + // if it's a system database reference, we'll add an additional node with the SSDT location of the dacpac later + if (isSystemDatabaseProjectEntry) { + referenceNode.setAttribute(constants.Condition, constants.NetCoreCondition); + } + + referenceNode.setAttribute(constants.Include, isSystemDatabaseProjectEntry ? entry.fsUri.fsPath.substring(1) : entry.fsUri.fsPath); // need to remove the leading slash for system database path for build to work on Windows + this.addDatabaseReferenceChildren(referenceNode, entry.name); + this.findOrCreateItemGroup(constants.ArtifactReference).appendChild(referenceNode); + this.databaseReferences.push(path.parse(entry.fsUri.fsPath.toString()).name); + + // add a reference to the system dacpac in SSDT if it's a system db + if (isSystemDatabaseProjectEntry) { + let ssdtReferenceNode = this.projFileXmlDoc.createElement(constants.ArtifactReference); + ssdtReferenceNode.setAttribute(constants.Condition, constants.NotNetCoreCondition); + ssdtReferenceNode.setAttribute(constants.Include, (entry).ssdtUri.fsPath.substring(1)); // need to remove the leading slash for system database path for build to work on Windows + this.addDatabaseReferenceChildren(ssdtReferenceNode, entry.name); + this.findOrCreateItemGroup(constants.ArtifactReference).appendChild(ssdtReferenceNode); + } + } + + private addDatabaseReferenceChildren(referenceNode: any, name?: string): void { let suppressMissingDependenciesErrorNode = this.projFileXmlDoc.createElement(constants.SuppressMissingDependenciesErrors); let falseTextNode = this.projFileXmlDoc.createTextNode('False'); suppressMissingDependenciesErrorNode.appendChild(falseTextNode); referenceNode.appendChild(suppressMissingDependenciesErrorNode); - if (entry.databaseLocation === DatabaseReferenceLocation.differentDatabaseSameServer) { + if (name) { let databaseVariableLiteralValue = this.projFileXmlDoc.createElement(constants.DatabaseVariableLiteralValue); - let databaseTextNode = this.projFileXmlDoc.createTextNode(entry.name); + let databaseTextNode = this.projFileXmlDoc.createTextNode(name); databaseVariableLiteralValue.appendChild(databaseTextNode); referenceNode.appendChild(databaseVariableLiteralValue); } - - this.findOrCreateItemGroup(constants.ArtifactReference).appendChild(referenceNode); - this.databaseReferences.push(path.parse(entry.fsUri.fsPath.toString()).name); } private async updateImportedTargetsToProjFile(condition: string, projectAttributeVal: string, oldImportNode?: any): Promise { @@ -341,7 +369,8 @@ export class Project { public containsSSDTOnlySystemDatabaseReferences(): boolean { for (let r = 0; r < this.projFileXmlDoc.documentElement.getElementsByTagName(constants.ArtifactReference).length; r++) { const currentNode = this.projFileXmlDoc.documentElement.getElementsByTagName(constants.ArtifactReference)[r]; - if (!currentNode.getAttribute(constants.NetCoreCondition) && currentNode.getAttribute(constants.Include).includes(constants.DacpacRootPath)) { + if (currentNode.getAttribute(constants.Condition) !== constants.NetCoreCondition && currentNode.getAttribute(constants.Condition) !== constants.NotNetCoreCondition + && currentNode.getAttribute(constants.Include).includes(constants.DacpacRootPath)) { return true; } } @@ -364,7 +393,7 @@ export class Project { } // remove from database references because it'll get added again later - this.databaseReferences.splice(this.databaseReferences.findIndex(n => n === (name ? constants.master : constants.msdb)), 1); + this.databaseReferences.splice(this.databaseReferences.findIndex(n => n === (name === SystemDatabase.master ? constants.master : constants.msdb)), 1); await this.addSystemDatabaseReference(name); } @@ -444,11 +473,17 @@ export class ProjectEntry { * Represents a database reference entry in a project file */ class DatabaseReferenceProjectEntry extends ProjectEntry { - constructor(uri: Uri, public databaseLocation: DatabaseReferenceLocation, public isSystemDatabase: boolean, public name?: string) { + constructor(uri: Uri, public databaseLocation: DatabaseReferenceLocation, public name?: string) { super(uri, '', EntryType.DatabaseReference); } } +class SystemDatabaseReferenceProjectEntry extends DatabaseReferenceProjectEntry { + constructor(uri: Uri, public ssdtUri: Uri, public name: string) { + super(uri, DatabaseReferenceLocation.differentDatabaseSameServer, name); + } +} + export enum EntryType { File, Folder, diff --git a/extensions/sql-database-projects/src/test/baselines/SSDTProjectAfterUpdateBaseline.xml b/extensions/sql-database-projects/src/test/baselines/SSDTProjectAfterUpdateBaseline.xml index 69110afb0a..cea51b847f 100644 --- a/extensions/sql-database-projects/src/test/baselines/SSDTProjectAfterUpdateBaseline.xml +++ b/extensions/sql-database-projects/src/test/baselines/SSDTProjectAfterUpdateBaseline.xml @@ -81,5 +81,9 @@ False master + + False + master + diff --git a/extensions/sql-database-projects/src/test/baselines/SSDTProjectAfterUpdateBaselineWindows.xml b/extensions/sql-database-projects/src/test/baselines/SSDTProjectAfterUpdateBaselineWindows.xml index c28c6b694a..0930ef4985 100644 --- a/extensions/sql-database-projects/src/test/baselines/SSDTProjectAfterUpdateBaselineWindows.xml +++ b/extensions/sql-database-projects/src/test/baselines/SSDTProjectAfterUpdateBaselineWindows.xml @@ -81,5 +81,9 @@ False master + + False + master + diff --git a/extensions/sql-database-projects/src/test/baselines/SSDTUpdatedProjectAfterSystemDbUpdateBaseline.xml b/extensions/sql-database-projects/src/test/baselines/SSDTUpdatedProjectAfterSystemDbUpdateBaseline.xml index dfdf41181e..8ee0fb0595 100644 --- a/extensions/sql-database-projects/src/test/baselines/SSDTUpdatedProjectAfterSystemDbUpdateBaseline.xml +++ b/extensions/sql-database-projects/src/test/baselines/SSDTUpdatedProjectAfterSystemDbUpdateBaseline.xml @@ -81,9 +81,17 @@ False master + + False + master + False msdb + + False + msdb + diff --git a/extensions/sql-database-projects/src/test/baselines/SSDTUpdatedProjectAfterSystemDbUpdateBaselineWindows.xml b/extensions/sql-database-projects/src/test/baselines/SSDTUpdatedProjectAfterSystemDbUpdateBaselineWindows.xml index 43cd0401cd..039d30f87e 100644 --- a/extensions/sql-database-projects/src/test/baselines/SSDTUpdatedProjectAfterSystemDbUpdateBaselineWindows.xml +++ b/extensions/sql-database-projects/src/test/baselines/SSDTUpdatedProjectAfterSystemDbUpdateBaselineWindows.xml @@ -81,9 +81,17 @@ False master + + False + master + False msdb + + False + msdb + diff --git a/extensions/sql-database-projects/src/test/baselines/SSDTUpdatedProjectBaseline.xml b/extensions/sql-database-projects/src/test/baselines/SSDTUpdatedProjectBaseline.xml index e42d403ecd..2b05d96a3f 100644 --- a/extensions/sql-database-projects/src/test/baselines/SSDTUpdatedProjectBaseline.xml +++ b/extensions/sql-database-projects/src/test/baselines/SSDTUpdatedProjectBaseline.xml @@ -81,6 +81,10 @@ False master + + False + master + $(DacPacRootPath)\Extensions\Microsoft\SQLDB\Extensions\SqlServer\130\SqlSchemas\msdb.dacpac False diff --git a/extensions/sql-database-projects/src/test/baselines/SSDTUpdatedProjectBaselineWindows.xml b/extensions/sql-database-projects/src/test/baselines/SSDTUpdatedProjectBaselineWindows.xml index 2b9a00bd07..b7af463f15 100644 --- a/extensions/sql-database-projects/src/test/baselines/SSDTUpdatedProjectBaselineWindows.xml +++ b/extensions/sql-database-projects/src/test/baselines/SSDTUpdatedProjectBaselineWindows.xml @@ -86,5 +86,9 @@ False msdb + + False + master + diff --git a/extensions/sql-database-projects/src/test/baselines/openSqlProjectBaseline.xml b/extensions/sql-database-projects/src/test/baselines/openSqlProjectBaseline.xml index 19daaba5c3..ece1d32071 100644 --- a/extensions/sql-database-projects/src/test/baselines/openSqlProjectBaseline.xml +++ b/extensions/sql-database-projects/src/test/baselines/openSqlProjectBaseline.xml @@ -88,6 +88,10 @@ False master + + False + master + diff --git a/extensions/sql-database-projects/src/test/project.test.ts b/extensions/sql-database-projects/src/test/project.test.ts index 51b3ed7e16..5d008be85c 100644 --- a/extensions/sql-database-projects/src/test/project.test.ts +++ b/extensions/sql-database-projects/src/test/project.test.ts @@ -44,6 +44,7 @@ describe('Project: sqlproj content operations', function (): void { should(project.sqlCmdVariables['BackupDatabaseName']).equal('MyBackupDatabase'); // Database references + // should only have one database reference even though there are two master.dacpac references (1 for ADS and 1 for SSDT) should(project.databaseReferences.length).equal(1); should(project.databaseReferences[0]).containEql(constants.master); }); @@ -103,15 +104,21 @@ describe('Project: sqlproj content operations', function (): void { await project.readProjFile(); let uri = project.getSystemDacpacUri(constants.masterDacpac); + let ssdtUri = project.getSystemDacpacSsdtUri(constants.masterDacpac); should.equal(uri.fsPath, Uri.parse(path.join('$(NETCoreTargetsPath)', 'SystemDacpacs', '130', constants.masterDacpac)).fsPath); + should.equal(ssdtUri.fsPath, Uri.parse(path.join('$(DacPacRootPath)', 'Extensions', 'Microsoft', 'SQLDB', 'Extensions', 'SqlServer', '130', 'SqlSchemas', constants.masterDacpac)).fsPath); project.changeDSP(TargetPlatform.Sql150.toString()); uri = project.getSystemDacpacUri(constants.masterDacpac); + ssdtUri = project.getSystemDacpacSsdtUri(constants.masterDacpac); should.equal(uri.fsPath, Uri.parse(path.join('$(NETCoreTargetsPath)', 'SystemDacpacs', '150', constants.masterDacpac)).fsPath); + should.equal(ssdtUri.fsPath, Uri.parse(path.join('$(DacPacRootPath)', 'Extensions', 'Microsoft', 'SQLDB', 'Extensions', 'SqlServer', '150', 'SqlSchemas', constants.masterDacpac)).fsPath); project.changeDSP(TargetPlatform.SqlAzureV12.toString()); uri = project.getSystemDacpacUri(constants.masterDacpac); - should.equal(uri.fsPath, Uri.parse(path.join('$(NETCoreTargetsPath)', 'SystemDacpacs', 'AzureV12', constants.masterDacpac)).fsPath); + ssdtUri = project.getSystemDacpacSsdtUri(constants.masterDacpac); + should.equal(uri.fsPath, Uri.parse(path.join('$(NETCoreTargetsPath)', 'SystemDacpacs', 'AzureV12',constants.masterDacpac)).fsPath); + should.equal(ssdtUri.fsPath, Uri.parse(path.join('$(DacPacRootPath)', 'Extensions', 'Microsoft', 'SQLDB', 'Extensions', 'SqlServer', 'AzureV12', 'SqlSchemas', constants.masterDacpac)).fsPath); }); it('Should choose correct msdb dacpac', async function (): Promise { @@ -120,15 +127,21 @@ describe('Project: sqlproj content operations', function (): void { await project.readProjFile(); let uri = project.getSystemDacpacUri(constants.msdbDacpac); + let ssdtUri = project.getSystemDacpacSsdtUri(constants.msdbDacpac); should.equal(uri.fsPath, Uri.parse(path.join('$(NETCoreTargetsPath)', 'SystemDacpacs', '130', constants.msdbDacpac)).fsPath); + should.equal(ssdtUri.fsPath, Uri.parse(path.join('$(DacPacRootPath)', 'Extensions', 'Microsoft', 'SQLDB', 'Extensions', 'SqlServer', '130', 'SqlSchemas', constants.msdbDacpac)).fsPath); project.changeDSP(TargetPlatform.Sql150.toString()); uri = project.getSystemDacpacUri(constants.msdbDacpac); + ssdtUri = project.getSystemDacpacSsdtUri(constants.msdbDacpac); should.equal(uri.fsPath, Uri.parse(path.join('$(NETCoreTargetsPath)', 'SystemDacpacs', '150', constants.msdbDacpac)).fsPath); + should.equal(ssdtUri.fsPath, Uri.parse(path.join('$(DacPacRootPath)', 'Extensions', 'Microsoft', 'SQLDB', 'Extensions', 'SqlServer', '150', 'SqlSchemas', constants.msdbDacpac)).fsPath); project.changeDSP(TargetPlatform.SqlAzureV12.toString()); uri = project.getSystemDacpacUri(constants.msdbDacpac); + ssdtUri = project.getSystemDacpacSsdtUri(constants.msdbDacpac); should.equal(uri.fsPath, Uri.parse(path.join('$(NETCoreTargetsPath)', 'SystemDacpacs', 'AzureV12', constants.msdbDacpac)).fsPath); + should.equal(ssdtUri.fsPath, Uri.parse(path.join('$(DacPacRootPath)', 'Extensions', 'Microsoft', 'SQLDB', 'Extensions', 'SqlServer', 'AzureV12', 'SqlSchemas', constants.msdbDacpac)).fsPath); }); it('Should throw error when choosing correct master dacpac if invalid DSP', async function (): Promise { @@ -149,12 +162,18 @@ describe('Project: sqlproj content operations', function (): void { await project.addSystemDatabaseReference(SystemDatabase.master); should(project.databaseReferences.length).equal(1); should(project.databaseReferences[0]).equal(constants.master); + // make sure reference to SSDT master dacpac was added + let projFileText = (await fs.readFile(projFilePath)).toString(); + should(projFileText).containEql(project.getSystemDacpacSsdtUri(constants.master).fsPath.substring(1)); await project.addSystemDatabaseReference(SystemDatabase.msdb); should(project.databaseReferences.length).equal(2); should(project.databaseReferences[1]).equal(constants.msdb); + // make sure reference to SSDT msdb dacpac was added + projFileText = (await fs.readFile(projFilePath)).toString(); + should(projFileText).containEql(project.getSystemDacpacSsdtUri(constants.msdb).fsPath.substring(1)); - await project.addDatabaseReference(Uri.parse('test.dacpac'), DatabaseReferenceLocation.sameDatabase, false); + await project.addDatabaseReference(Uri.parse('test.dacpac'), DatabaseReferenceLocation.sameDatabase); should(project.databaseReferences.length).equal(3); should(project.databaseReferences[2]).equal('test'); });