diff --git a/extensions/sql-database-projects/src/models/IDatabaseReferenceSettings.ts b/extensions/sql-database-projects/src/models/IDatabaseReferenceSettings.ts index 7e2c3e6c39..a79dc83dd8 100644 --- a/extensions/sql-database-projects/src/models/IDatabaseReferenceSettings.ts +++ b/extensions/sql-database-projects/src/models/IDatabaseReferenceSettings.ts @@ -31,3 +31,8 @@ export interface IProjectReferenceSettings extends IUserDatabaseReferenceSetting projectName: string; projectGuid: string; } + +export interface INugetPackageReferenceSettings extends IUserDatabaseReferenceSettings { + packageName: string; + packageVersion: string; +} diff --git a/extensions/sql-database-projects/src/models/project.ts b/extensions/sql-database-projects/src/models/project.ts index e0950cc9a2..d0d6b1854f 100644 --- a/extensions/sql-database-projects/src/models/project.ts +++ b/extensions/sql-database-projects/src/models/project.ts @@ -14,9 +14,9 @@ import { promises as fs } from 'fs'; import { Uri, window } from 'vscode'; import { EntryType, IDatabaseReferenceProjectEntry, ISqlProject, ItemType } from 'sqldbproj'; import { DataSource } from './dataSources/dataSources'; -import { ISystemDatabaseReferenceSettings, IDacpacReferenceSettings, IProjectReferenceSettings } from './IDatabaseReferenceSettings'; +import { ISystemDatabaseReferenceSettings, IDacpacReferenceSettings, IProjectReferenceSettings, INugetPackageReferenceSettings, IUserDatabaseReferenceSettings } from './IDatabaseReferenceSettings'; import { TelemetryActions, TelemetryReporter, TelemetryViews } from '../common/telemetry'; -import { DacpacReferenceProjectEntry, FileProjectEntry, SqlProjectReferenceProjectEntry, SystemDatabaseReferenceProjectEntry } from './projectEntry'; +import { DacpacReferenceProjectEntry, FileProjectEntry, NugetPackageReferenceProjectEntry, SqlProjectReferenceProjectEntry, SystemDatabaseReferenceProjectEntry } from './projectEntry'; import { ResultStatus } from 'azdata'; import { BaseProjectTreeItem } from './tree/baseTreeItem'; import { NoneNode, PostDeployNode, PreDeployNode, PublishProfileNode, SqlObjectFileNode } from './tree/fileFolderTreeItem'; @@ -428,6 +428,20 @@ export class Project implements ISqlProject { systemDbReference.databaseVariableLiteralName, systemDbReference.suppressMissingDependencies)); } + + for (const nupkgReference of databaseReferencesResult.nugetPackageReferences) { + this._databaseReferences.push(new NugetPackageReferenceProjectEntry({ + packageName: nupkgReference.packageName, + packageVersion: nupkgReference.packageVersion, + suppressMissingDependenciesErrors: nupkgReference.suppressMissingDependencies, + + databaseVariableLiteralValue: nupkgReference.databaseVariableLiteralName, + databaseName: nupkgReference.databaseVariable?.varName, + databaseVariable: nupkgReference.databaseVariable?.value, + serverName: nupkgReference.serverVariable?.varName, + serverVariable: nupkgReference.serverVariable?.value + })); + } } //#endregion @@ -781,7 +795,12 @@ export class Project implements ISqlProject { await this.addUserDatabaseReference(settings, projectReferenceEntry); } - private async addUserDatabaseReference(settings: IProjectReferenceSettings | IDacpacReferenceSettings, reference: SqlProjectReferenceProjectEntry | DacpacReferenceProjectEntry): Promise { + public async addNugetPackageReference(settings: INugetPackageReferenceSettings): Promise { + const nupkgReferenceEntry = new NugetPackageReferenceProjectEntry(settings); + await this.addUserDatabaseReference(settings, nupkgReferenceEntry); + } + + private async addUserDatabaseReference(settings: IUserDatabaseReferenceSettings, reference: SqlProjectReferenceProjectEntry | DacpacReferenceProjectEntry | NugetPackageReferenceProjectEntry): Promise { // check if reference to this database already exists if (this.databaseReferenceExists(reference)) { throw new Error(constants.databaseReferenceAlreadyExists); @@ -806,9 +825,12 @@ export class Project implements ISqlProject { if (reference instanceof SqlProjectReferenceProjectEntry) { referenceName = (settings).projectName; result = await this.sqlProjService.addSqlProjectReference(this.projectFilePath, reference.pathForSqlProj(), reference.projectGuid, settings.suppressMissingDependenciesErrors, settings.databaseVariable, settings.serverVariable, databaseLiteral) - } else { // dacpac + } else if (reference instanceof DacpacReferenceProjectEntry) { referenceName = (settings).dacpacFileLocation.fsPath; result = await this.sqlProjService.addDacpacReference(this.projectFilePath, reference.pathForSqlProj(), settings.suppressMissingDependenciesErrors, settings.databaseVariable, settings.serverVariable, databaseLiteral) + } else {// nupkg reference + referenceName = (settings).packageName; + result = await this.sqlProjService.addNugetPackageReference(this.projectFilePath, reference.packageName, (settings).packageVersion, settings.suppressMissingDependenciesErrors, settings.databaseVariable, settings.serverVariable, databaseLiteral) } if (!result.success && result.errorMessage) { diff --git a/extensions/sql-database-projects/src/models/projectEntry.ts b/extensions/sql-database-projects/src/models/projectEntry.ts index 9c13e0d44a..ad4747dc59 100644 --- a/extensions/sql-database-projects/src/models/projectEntry.ts +++ b/extensions/sql-database-projects/src/models/projectEntry.ts @@ -5,7 +5,7 @@ import * as path from 'path'; import * as utils from '../common/utils'; -import { IDacpacReferenceSettings, IProjectReferenceSettings } from './IDatabaseReferenceSettings'; +import { IDacpacReferenceSettings, INugetPackageReferenceSettings, IProjectReferenceSettings } from './IDatabaseReferenceSettings'; import { EntryType, IDatabaseReferenceProjectEntry, IFileProjectEntry, IProjectEntry } from 'sqldbproj'; import { Uri } from 'vscode'; @@ -124,6 +124,37 @@ export class SqlProjectReferenceProjectEntry extends FileProjectEntry implements } } +export class NugetPackageReferenceProjectEntry extends FileProjectEntry implements IDatabaseReferenceProjectEntry { + databaseSqlCmdVariableValue?: string; + databaseSqlCmdVariableName?: string; + databaseVariableLiteralValue?: string; + serverSqlCmdVariableName?: string; + serverSqlCmdVariableValue?: string; + suppressMissingDependenciesErrors: boolean; + packageName: string; + + constructor(settings: INugetPackageReferenceSettings) { + super(Uri.file(settings.packageName), /* relativePath doesn't get set for database references */ '', EntryType.DatabaseReference); + this.packageName = settings.packageName; + this.suppressMissingDependenciesErrors = settings.suppressMissingDependenciesErrors; + + this.databaseVariableLiteralValue = settings.databaseVariableLiteralValue; + this.databaseSqlCmdVariableName = settings.databaseName; + this.databaseSqlCmdVariableValue = settings.databaseVariable; + + this.serverSqlCmdVariableName = settings.serverName; + this.serverSqlCmdVariableValue = settings.serverVariable; + } + + public get referenceName(): string { + return this.packageName; + } + + public override pathForSqlProj(): string { + return this.packageName; + } +} + export class SqlCmdVariableProjectEntry extends ProjectEntry { constructor(public variableName: string, public defaultValue: string) { super(EntryType.SqlCmdVariable); diff --git a/extensions/sql-database-projects/src/test/baselines/newSdkStyleSqlProjectSdkNodeBaseline.xml b/extensions/sql-database-projects/src/test/baselines/newSdkStyleSqlProjectSdkNodeBaseline.xml index 1a7b1d5da3..2ac3caed39 100644 --- a/extensions/sql-database-projects/src/test/baselines/newSdkStyleSqlProjectSdkNodeBaseline.xml +++ b/extensions/sql-database-projects/src/test/baselines/newSdkStyleSqlProjectSdkNodeBaseline.xml @@ -1,6 +1,6 @@ - + TestProjectName {2C283C5D-9E4A-4313-8FF9-4E0CEE20B063} diff --git a/extensions/sql-database-projects/src/test/project.test.ts b/extensions/sql-database-projects/src/test/project.test.ts index 05e54a8e0c..5f6bcfac35 100644 --- a/extensions/sql-database-projects/src/test/project.test.ts +++ b/extensions/sql-database-projects/src/test/project.test.ts @@ -14,7 +14,7 @@ import { promises as fs } from 'fs'; import { Project } from '../models/project'; import { exists, convertSlashesForSqlProj, getPlatformSafeFileEntryPath, systemDatabaseToString } from '../common/utils'; import { Uri, window } from 'vscode'; -import { IDacpacReferenceSettings, IProjectReferenceSettings, ISystemDatabaseReferenceSettings } from '../models/IDatabaseReferenceSettings'; +import { IDacpacReferenceSettings, INugetPackageReferenceSettings, IProjectReferenceSettings, ISystemDatabaseReferenceSettings } from '../models/IDatabaseReferenceSettings'; import { ItemType } from 'sqldbproj'; import { SystemDatabaseReferenceProjectEntry, SqlProjectReferenceProjectEntry, DacpacReferenceProjectEntry } from '../models/projectEntry'; import { ProjectType, SystemDatabase } from 'mssql'; @@ -645,7 +645,7 @@ describe('Project: database references', function (): void { const projFilePath = await testUtils.createTestSqlProjFile(this.test, baselines.newProjectFileBaseline); let project = await Project.openProject(projFilePath); - // add database reference to a different database on a different server + // add database reference to the same database should(project.databaseReferences.length).equal(0, 'There should be no database references to start with'); should(project.sqlCmdVariables.size).equal(0, `There should be no sqlcmd variables to start with. Actual: ${project.sqlCmdVariables.size}`); await project.addProjectReference({ @@ -665,7 +665,7 @@ describe('Project: database references', function (): void { const projFilePath = await testUtils.createTestSqlProjFile(this.test, baselines.newProjectFileBaseline); let project = await Project.openProject(projFilePath); - // add database reference to a different database on a different server + // add database reference to a different database on the same different server should(project.databaseReferences.length).equal(0, 'There should be no database references to start with'); should(project.sqlCmdVariables.size).equal(0, 'There should be no sqlcmd variables to start with'); await project.addProjectReference({ @@ -707,6 +707,89 @@ describe('Project: database references', function (): void { should(project.sqlCmdVariables.size).equal(2, `There should be two new sqlcmd variables added. Actual: ${project.sqlCmdVariables.size}`); }); + it('Should add a nupkg reference to the same database correctly', async function (): Promise { + const projFilePath = await testUtils.createTestSqlProjFile(this.test, baselines.newSdkStyleProjectSdkNodeBaseline); + let project = await Project.openProject(projFilePath); + + // add database reference to the same database + should(project.sqlProjStyle).equal(ProjectType.SdkStyle, 'Project should be SDK-style'); + should(project.databaseReferences.length).equal(0, 'There should be no database references to start with'); + should(project.sqlCmdVariables.size).equal(0, `There should be no sqlcmd variables to start with. Actual: ${project.sqlCmdVariables.size}`); + await project.addNugetPackageReference({ + packageName: 'testPackage', + packageVersion: '1.0.1', + suppressMissingDependenciesErrors: false + }); + + should(project.databaseReferences.length).equal(1, 'There should be a database reference after adding a reference to project1'); + should(project.databaseReferences[0].referenceName).equal('testPackage', 'The database reference should be project1'); + should(project.databaseReferences[0].suppressMissingDependenciesErrors).equal(false, 'project.databaseReferences[0].suppressMissingDependenciesErrors should be false'); + should(project.sqlCmdVariables.size).equal(0, `There should be no sqlcmd variables added. Actual: ${project.sqlCmdVariables.size}`); + }); + + it('Should add a nupkg reference to a different database in the same server correctly', async function (): Promise { + const projFilePath = await testUtils.createTestSqlProjFile(this.test, baselines.newSdkStyleProjectSdkNodeBaseline); + let project = await Project.openProject(projFilePath); + + // add database reference to a different database on the same different server + should(project.databaseReferences.length).equal(0, 'There should be no database references to start with'); + should(project.sqlCmdVariables.size).equal(0, 'There should be no sqlcmd variables to start with'); + await project.addNugetPackageReference({ + packageName: 'testPackage', + packageVersion: '1.0.1', + databaseName: 'testdbName', + databaseVariable: 'testdb', + suppressMissingDependenciesErrors: false + }); + + should(project.databaseReferences.length).equal(1, 'There should be a database reference after adding a reference to testPackage'); + should(project.databaseReferences[0].referenceName).equal('testPackage', 'The database reference should be testPackage'); + should(project.databaseReferences[0].suppressMissingDependenciesErrors).equal(false, 'project.databaseReferences[0].suppressMissingDependenciesErrors should be false'); + should(project.sqlCmdVariables.size).equal(1, `There should be one new sqlcmd variable added. Actual: ${project.sqlCmdVariables.size}`); + }); + + it('Should add a nupkg reference to a different database in a different server correctly', async function (): Promise { + const projFilePath = await testUtils.createTestSqlProjFile(this.test, baselines.newSdkStyleProjectSdkNodeBaseline); + let 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'); + should(project.sqlCmdVariables.size).equal(0, 'There should be no sqlcmd variables to start with'); + await project.addNugetPackageReference({ + packageName: 'testPackage', + packageVersion: '1.0.1', + databaseName: 'testdbName', + databaseVariable: 'testdb', + serverName: 'otherServerName', + serverVariable: 'otherServer', + suppressMissingDependenciesErrors: false + }); + + should(project.databaseReferences.length).equal(1, 'There should be a database reference after adding a reference to testPackage'); + should(project.databaseReferences[0].referenceName).equal('testPackage', 'The database reference should be testPackage'); + should(project.databaseReferences[0].suppressMissingDependenciesErrors).equal(false, 'project.databaseReferences[0].suppressMissingDependenciesErrors should be false'); + should(project.sqlCmdVariables.size).equal(2, `There should be two new sqlcmd variables added. Actual: ${project.sqlCmdVariables.size}`); + }); + + it('Should throw an error trying to add a nupkg reference to legacy style project', async function (): Promise { + const projFilePath = await testUtils.createTestSqlProjFile(this.test, baselines.newProjectFileBaseline); + let project = await Project.openProject(projFilePath); + + // add database reference to the same database + should(project.sqlProjStyle).equal(ProjectType.LegacyStyle, 'Project should be legacy-style'); + should(project.databaseReferences.length).equal(0, 'There should be no database references to start with'); + should(project.sqlCmdVariables.size).equal(0, `There should be no sqlcmd variables to start with. Actual: ${project.sqlCmdVariables.size}`); + await testUtils.shouldThrowSpecificError(async () => await project.addNugetPackageReference({ + packageName: 'testPackage', + packageVersion: '1.0.1', + suppressMissingDependenciesErrors: false + }), + `Error adding database reference to testPackage. Error: Nuget package database references are not supported for the project ${project.projectFilePath}` + ); + + should(project.databaseReferences.length).equal(0, 'There should not have been any database reference added'); + }); + it('Should not allow adding duplicate dacpac references', async function (): Promise { const projFilePath = await testUtils.createTestSqlProjFile(this.test, baselines.newProjectFileBaseline); let project = await Project.openProject(projFilePath); @@ -763,6 +846,27 @@ describe('Project: database references', function (): void { should(project.databaseReferences.length).equal(1, 'There should be one database reference after trying to add a reference to testProject again'); }); + it('Should not allow adding duplicate nupkg references', async function (): Promise { + const projFilePath = await testUtils.createTestSqlProjFile(this.test, baselines.newSdkStyleProjectSdkNodeBaseline); + let project = await Project.openProject(projFilePath); + + should(project.databaseReferences.length).equal(0, 'There should be no database references to start with'); + + const nupkgReference: INugetPackageReferenceSettings = { + packageName: 'testPackage', + packageVersion: '1.0.0', + suppressMissingDependenciesErrors: false + }; + await project.addNugetPackageReference(nupkgReference); + + should(project.databaseReferences.length).equal(1, 'There should be one database reference after adding a reference to testPackage'); + should(project.databaseReferences[0].referenceName).equal('testPackage', 'project.databaseReferences[0].databaseName should be testPackage'); + + // try to add reference to testPackage again + await testUtils.shouldThrowSpecificError(async () => await project.addNugetPackageReference(nupkgReference), constants.databaseReferenceAlreadyExists); + should(project.databaseReferences.length).equal(1, 'There should be one database reference after trying to add a reference to testPackage again'); + }); + it('Should handle trying to add duplicate database references when slashes are different direction', async function (): Promise { const projFilePath = await testUtils.createTestSqlProjFile(this.test, baselines.newProjectFileBaseline); let project = await Project.openProject(projFilePath);