diff --git a/extensions/sql-database-projects/src/common/constants.ts b/extensions/sql-database-projects/src/common/constants.ts index 0b34573e97..7099cd7c27 100644 --- a/extensions/sql-database-projects/src/common/constants.ts +++ b/extensions/sql-database-projects/src/common/constants.ts @@ -197,6 +197,8 @@ export const Value = 'Value'; export const ArtifactReference = 'ArtifactReference'; export const SuppressMissingDependenciesErrors = 'SuppressMissingDependenciesErrors'; export const DatabaseVariableLiteralValue = 'DatabaseVariableLiteralValue'; +export const DatabaseSqlCmdVariable = 'DatabaseSqlCmdVariable'; +export const ServerSqlCmdVariable = 'ServerSqlCmdVariable'; export const DSP = 'DSP'; export const Properties = 'Properties'; export const RelativeOuterPath = '..'; diff --git a/extensions/sql-database-projects/src/dialogs/addDatabaseReferenceDialog.ts b/extensions/sql-database-projects/src/dialogs/addDatabaseReferenceDialog.ts index 5ab0b4d97b..9f2e6f46b6 100644 --- a/extensions/sql-database-projects/src/dialogs/addDatabaseReferenceDialog.ts +++ b/extensions/sql-database-projects/src/dialogs/addDatabaseReferenceDialog.ts @@ -126,7 +126,9 @@ export class AddDatabaseReferenceDialog { databaseName: this.databaseNameTextbox?.value, databaseLocation: this.referenceLocationMap.get(this.locationDropdown?.value), dacpacFileLocation: vscode.Uri.file(this.dacpacTextbox?.value), - databaseVariable: this.databaseVariableTextbox?.value + databaseVariable: this.databaseVariableTextbox?.value, + serverName: this.serverNameTextbox?.value, + serverVariable: this.serverVariableTextbox?.value }; // TODO: add project reference support } diff --git a/extensions/sql-database-projects/src/models/project.ts b/extensions/sql-database-projects/src/models/project.ts index 24d8b7358a..c47ec33312 100644 --- a/extensions/sql-database-projects/src/models/project.ts +++ b/extensions/sql-database-projects/src/models/project.ts @@ -498,7 +498,6 @@ export class Project { if (isSystemDatabaseProjectEntry) { this.addSystemDatabaseReferenceToProjFile(entry); } else { - const referenceNode = this.projFileXmlDoc.createElement(constants.ArtifactReference); referenceNode.setAttribute(constants.Include, entry.pathForSqlProj()); this.addDatabaseReferenceChildren(referenceNode, entry); @@ -519,13 +518,30 @@ export class Project { suppressMissingDependenciesErrorNode.appendChild(falseTextNode); referenceNode.appendChild(suppressMissingDependenciesErrorNode); - // TODO: add support for sqlcmd vars and server https://github.com/microsoft/azuredatastudio/issues/12036 - if (entry.databaseVariableLiteralValue) { + if ((entry).databaseSqlCmdVariable) { + const databaseSqlCmdVariableElement = this.projFileXmlDoc.createElement(constants.DatabaseSqlCmdVariable); + const databaseSqlCmdVariableTextNode = this.projFileXmlDoc.createTextNode((entry).databaseSqlCmdVariable); + databaseSqlCmdVariableElement.appendChild(databaseSqlCmdVariableTextNode); + referenceNode.appendChild(databaseSqlCmdVariableElement); + + // add SQLCMD variable + this.addSqlCmdVariable((entry).databaseSqlCmdVariable!, (entry).databaseName); + } else if (entry.databaseVariableLiteralValue) { const databaseVariableLiteralValueElement = this.projFileXmlDoc.createElement(constants.DatabaseVariableLiteralValue); const databaseTextNode = this.projFileXmlDoc.createTextNode(entry.databaseVariableLiteralValue); databaseVariableLiteralValueElement.appendChild(databaseTextNode); referenceNode.appendChild(databaseVariableLiteralValueElement); } + + if ((entry).serverSqlCmdVariable) { + const serverSqlCmdVariableElement = this.projFileXmlDoc.createElement(constants.ServerSqlCmdVariable); + const serverSqlCmdVariableTextNode = this.projFileXmlDoc.createTextNode((entry).serverSqlCmdVariable); + serverSqlCmdVariableElement.appendChild(serverSqlCmdVariableTextNode); + referenceNode.appendChild(serverSqlCmdVariableElement); + + // add SQLCMD variable + this.addSqlCmdVariable((entry).serverSqlCmdVariable!, (entry).serverName!); + } } public addSqlCmdVariableToProjFile(entry: SqlCmdVariableProjectEntry): void { @@ -561,7 +577,7 @@ export class Project { */ private getNextSqlCmdVariableCounter(): number { const sqlCmdVariableNodes = this.projFileXmlDoc.documentElement.getElementsByTagName(constants.SqlCmdVariable); - let highestNumber = 1; + let highestNumber = 0; for (let i = 0; i < sqlCmdVariableNodes.length; i++) { const value: string = sqlCmdVariableNodes[i].getElementsByTagName(constants.Value)[0].childNodes[0].nodeValue; @@ -767,11 +783,17 @@ export interface IDatabaseReferenceProjectEntry extends FileProjectEntry { export class DacpacReferenceProjectEntry extends FileProjectEntry implements IDatabaseReferenceProjectEntry { databaseLocation: DatabaseReferenceLocation; databaseVariableLiteralValue?: string; + databaseSqlCmdVariable?: string; + serverName?: string; + serverSqlCmdVariable?: string; constructor(settings: IDacpacReferenceSettings) { super(settings.dacpacFileLocation, '', EntryType.DatabaseReference); this.databaseLocation = settings.databaseLocation; + this.databaseSqlCmdVariable = settings.databaseVariable; this.databaseVariableLiteralValue = settings.databaseName; + this.serverName = settings.serverName; + this.serverSqlCmdVariable = settings.serverVariable; } /** diff --git a/extensions/sql-database-projects/src/test/project.test.ts b/extensions/sql-database-projects/src/test/project.test.ts index 2c35877810..a2ab561693 100644 --- a/extensions/sql-database-projects/src/test/project.test.ts +++ b/extensions/sql-database-projects/src/test/project.test.ts @@ -187,11 +187,11 @@ describe('Project: sqlproj content operations', function (): void { await testUtils.shouldThrowSpecificError(async () => await project.getSystemDacpacUri(constants.masterDacpac), constants.invalidDataSchemaProvider); }); - it('Should add database references correctly', async function (): Promise { + it('Should add system database references correctly', async function (): Promise { projFilePath = await testUtils.createTestSqlProjFile(baselines.newProjectFileBaseline); const project = await Project.openProject(projFilePath); - should(project.databaseReferences.length).equal(0, 'There should be no datbase references to start with'); + should(project.databaseReferences.length).equal(0, 'There should be no database references to start with'); await project.addSystemDatabaseReference({ databaseName: 'master', systemDb: SystemDatabase.master }); should(project.databaseReferences.length).equal(1, 'There should be one database reference after adding a reference to master'); should(project.databaseReferences[0].databaseName).equal(constants.master, 'The database reference should be master'); @@ -207,13 +207,66 @@ describe('Project: sqlproj content operations', function (): void { projFileText = (await fs.readFile(projFilePath)).toString(); should(projFileText).containEql(convertSlashesForSqlProj(project.getSystemDacpacUri(constants.msdb).fsPath.substring(1))); should(projFileText).containEql(convertSlashesForSqlProj(project.getSystemDacpacSsdtUri(constants.msdb).fsPath.substring(1))); + }); - await project.addDatabaseReference({ dacpacFileLocation: Uri.file('test.dacpac'), databaseLocation: DatabaseReferenceLocation.sameDatabase }); - should(project.databaseReferences.length).equal(3, 'There should be three database references after adding a reference to test'); - should(project.databaseReferences[2].databaseName).equal('test', 'The database reference should be test'); + it('Should add a dacpac reference to the same database correctly', async function (): Promise { + projFilePath = await testUtils.createTestSqlProjFile(baselines.newProjectFileBaseline); + const project = await Project.openProject(projFilePath); + + // add database reference in the same database + should(project.databaseReferences.length).equal(0, 'There should be no database references to start with'); + await project.addDatabaseReference({ dacpacFileLocation: Uri.file('test1.dacpac'), databaseLocation: DatabaseReferenceLocation.sameDatabase }); + should(project.databaseReferences.length).equal(1, 'There should be a database reference after adding a reference to test1'); + should(project.databaseReferences[0].databaseName).equal('test1', 'The database reference should be test1'); // make sure reference to test.dacpac was added - projFileText = (await fs.readFile(projFilePath)).toString(); - should(projFileText).containEql('test.dacpac'); + let projFileText = (await fs.readFile(projFilePath)).toString(); + should(projFileText).containEql('test1.dacpac'); + }); + + it('Should add a dacpac reference to a different database in the same server correctly', async function (): Promise { + projFilePath = await testUtils.createTestSqlProjFile(baselines.newProjectFileBaseline); + const project = await Project.openProject(projFilePath); + + // add database reference to a different database on the same server + should(project.databaseReferences.length).equal(0, 'There should be no database references to start with'); + await project.addDatabaseReference({ + dacpacFileLocation: Uri.file('test2.dacpac'), + databaseLocation: DatabaseReferenceLocation.differentDatabaseSameServer, + databaseName: 'test2DbName', + databaseVariable: 'test2Db' + }); + should(project.databaseReferences.length).equal(1, 'There should be a database reference after adding a reference to test2'); + should(project.databaseReferences[0].databaseName).equal('test2', 'The database reference should be test2'); + // make sure reference to test2.dacpac and SQLCMD variable was added + let projFileText = (await fs.readFile(projFilePath)).toString(); + should(projFileText).containEql('test2.dacpac'); + should(projFileText).containEql('test2Db'); + should(projFileText).containEql(''); + }); + + it('Should add a dacpac reference to a different database in a different server correctly', async function (): Promise { + projFilePath = await testUtils.createTestSqlProjFile(baselines.newProjectFileBaseline); + const project = await Project.openProject(projFilePath); + + // add database reference to a different database on a different server + should(project.databaseReferences.length).equal(0, 'There should be no database references to start with'); + await project.addDatabaseReference({ + dacpacFileLocation: Uri.file('test3.dacpac'), + databaseLocation: DatabaseReferenceLocation.differentDatabaseDifferentServer, + databaseName: 'test3DbName', + databaseVariable: 'test3Db', + serverName: 'otherServerName', + serverVariable: 'otherServer' + }); + should(project.databaseReferences.length).equal(1, 'There should be a database reference after adding a reference to test3'); + should(project.databaseReferences[0].databaseName).equal('test3', 'The database reference should be test3'); + // make sure reference to test2.dacpac and SQLCMD variables were added + let projFileText = (await fs.readFile(projFilePath)).toString(); + should(projFileText).containEql('test3.dacpac'); + should(projFileText).containEql('test3Db'); + should(projFileText).containEql(''); + should(projFileText).containEql('otherServer'); + should(projFileText).containEql(''); }); it('Should not allow adding duplicate database references', async function (): Promise {