From 39a28c5f51ea1ce549dd44d3fac82b85bd4de4df Mon Sep 17 00:00:00 2001 From: Kim Santiago <31145923+kisantia@users.noreply.github.com> Date: Wed, 19 Apr 2023 10:09:15 -0700 Subject: [PATCH] Add nupkg option to add database reference dialog (#22772) * add nupkg option to add database reference dialog * Add required indicator * only show nupkg radio button for SDK-style projects * fix enable ok button * hookup * fix typo --- .../src/common/constants.ts | 4 + .../src/controllers/projectController.ts | 8 +- .../src/dialogs/addDatabaseReferenceDialog.ts | 109 ++++++++++++++++-- 3 files changed, 111 insertions(+), 10 deletions(-) diff --git a/extensions/sql-database-projects/src/common/constants.ts b/extensions/sql-database-projects/src/common/constants.ts index 145960f117..66066b6690 100644 --- a/extensions/sql-database-projects/src/common/constants.ts +++ b/extensions/sql-database-projects/src/common/constants.ts @@ -292,6 +292,10 @@ export const referenceRadioButtonsGroupTitle = localize('referenceRadioButtonsGr export const projectLabel = localize('projectLocString', "Project"); export const systemDatabase = localize('systemDatabase', "System database"); export const dacpacText = localize('dacpacText', "Data-tier application (.dacpac)"); +export const nupkgText = localize('nupkgText', "Published data-tier application (.nupkg)"); +export const nupkgNamePlaceholder = localize('nupkgNamePlaceholder', "NuGet package name"); +export const version = localize('version', "Version"); +export const versionPlaceholder = localize('versionPlaceholder', "NuGet package version"); export const selectDacpac = localize('selectDacpac', "Select .dacpac"); export const sameDatabase = localize('sameDatabase', "Same database"); export const differentDbSameServer = localize('differentDbSameServer', "Different database, same server"); diff --git a/extensions/sql-database-projects/src/controllers/projectController.ts b/extensions/sql-database-projects/src/controllers/projectController.ts index 0ee1db1bd1..637a774819 100644 --- a/extensions/sql-database-projects/src/controllers/projectController.ts +++ b/extensions/sql-database-projects/src/controllers/projectController.ts @@ -27,7 +27,7 @@ import { ShellCommandOptions } from '../tools/shellExecutionHelper'; import { BuildHelper } from '../tools/buildHelper'; import { readPublishProfile, savePublishProfile } from '../models/publishProfile/publishProfile'; import { AddDatabaseReferenceDialog } from '../dialogs/addDatabaseReferenceDialog'; -import { ISystemDatabaseReferenceSettings, IDacpacReferenceSettings, IProjectReferenceSettings } from '../models/IDatabaseReferenceSettings'; +import { ISystemDatabaseReferenceSettings, IDacpacReferenceSettings, IProjectReferenceSettings, INugetPackageReferenceSettings } from '../models/IDatabaseReferenceSettings'; import { DatabaseReferenceTreeItem } from '../models/tree/databaseReferencesTreeItem'; import { CreateProjectFromDatabaseDialog } from '../dialogs/createProjectFromDatabaseDialog'; import { UpdateProjectFromDatabaseDialog } from '../dialogs/updateProjectFromDatabaseDialog'; @@ -65,7 +65,7 @@ export enum TaskExecutionMode { executeAndScript = 2 } -export type AddDatabaseReferenceSettings = ISystemDatabaseReferenceSettings | IDacpacReferenceSettings | IProjectReferenceSettings; +export type AddDatabaseReferenceSettings = ISystemDatabaseReferenceSettings | IDacpacReferenceSettings | IProjectReferenceSettings | INugetPackageReferenceSettings; interface FileWatcherStatus { fileWatcher: vscode.FileSystemWatcher; @@ -1193,11 +1193,13 @@ export class ProjectsController { await project.addProjectReference(projectReferenceSettings); } else if ((settings).systemDb !== undefined) { await project.addSystemDatabaseReference(settings); - } else { + } else if ((settings).dacpacFileLocation !== undefined) { // update dacpacFileLocation to relative path to project file const dacpacRefSettings = settings as IDacpacReferenceSettings; dacpacRefSettings.dacpacFileLocation = vscode.Uri.file(path.relative(project.projectFolderPath, dacpacRefSettings.dacpacFileLocation.fsPath)); await project.addDatabaseReference(dacpacRefSettings); + } else { + await project.addNugetPackageReference(settings); } this.refreshProjectsTree(context); diff --git a/extensions/sql-database-projects/src/dialogs/addDatabaseReferenceDialog.ts b/extensions/sql-database-projects/src/dialogs/addDatabaseReferenceDialog.ts index 369e43cc41..f42c30f56c 100644 --- a/extensions/sql-database-projects/src/dialogs/addDatabaseReferenceDialog.ts +++ b/extensions/sql-database-projects/src/dialogs/addDatabaseReferenceDialog.ts @@ -12,16 +12,17 @@ import * as utils from '../common/utils'; import { Project } from '../models/project'; import { cssStyles } from '../common/uiConstants'; import { IconPathHelper } from '../common/iconHelper'; -import { ISystemDatabaseReferenceSettings, IDacpacReferenceSettings, IProjectReferenceSettings } from '../models/IDatabaseReferenceSettings'; +import { ISystemDatabaseReferenceSettings, IDacpacReferenceSettings, IProjectReferenceSettings, INugetPackageReferenceSettings } from '../models/IDatabaseReferenceSettings'; import { Deferred } from '../common/promise'; import { TelemetryActions, TelemetryReporter, TelemetryViews } from '../common/telemetry'; -import { SystemDatabase } from 'mssql'; +import { ProjectType, SystemDatabase } from 'mssql'; import { DbServerValues, ensureSetOrDefined, populateResultWithVars } from './utils'; export enum ReferenceType { project, systemDb, - dacpac + dacpac, + nupkg } export class AddDatabaseReferenceDialog { @@ -35,6 +36,9 @@ export class AddDatabaseReferenceDialog { private systemDatabaseFormComponent: azdataType.FormComponent | undefined; public dacpacTextbox: azdataType.InputBoxComponent | undefined; private dacpacFormComponent: azdataType.FormComponent | undefined; + public nupkgNameTextbox: azdataType.InputBoxComponent | undefined; + public nupkgVersionTextbox: azdataType.InputBoxComponent | undefined; + private nupkgFormComponent: azdataType.FormComponentGroup | undefined; public locationDropdown: azdataType.DropDownComponent | undefined; public databaseNameTextbox: azdataType.InputBoxComponent | undefined; public databaseVariableTextbox: azdataType.InputBoxComponent | undefined; @@ -50,7 +54,7 @@ export class AddDatabaseReferenceDialog { private toDispose: vscode.Disposable[] = []; private initDialogComplete: Deferred = new Deferred(); - public addReference: ((proj: Project, settings: ISystemDatabaseReferenceSettings | IDacpacReferenceSettings | IProjectReferenceSettings) => any) | undefined; + public addReference: ((proj: Project, settings: ISystemDatabaseReferenceSettings | IDacpacReferenceSettings | IProjectReferenceSettings | INugetPackageReferenceSettings) => any) | undefined; constructor(private project: Project) { this.dialog = utils.getAzdataApi()!.window.createModelViewDialog(constants.addDatabaseReferenceDialogName, 'addDatabaseReferencesDialog'); @@ -106,6 +110,7 @@ export class AddDatabaseReferenceDialog { const radioButtonGroup = this.createRadioButtons(); this.systemDatabaseFormComponent = this.createSystemDatabaseDropdown(); this.dacpacFormComponent = this.createDacpacTextbox(); + this.nupkgFormComponent = this.createNupkgFormComponentGroup(); const locationDropdown = this.createLocationDropdown(); const variableSection = this.createVariableSection(); this.suppressMissingDependenciesErrorsCheckbox = view.modelBuilder.checkBox().withProps({ @@ -150,7 +155,7 @@ export class AddDatabaseReferenceDialog { } public async addReferenceClick(): Promise { - let referenceSettings: ISystemDatabaseReferenceSettings | IDacpacReferenceSettings | IProjectReferenceSettings; + let referenceSettings: ISystemDatabaseReferenceSettings | IDacpacReferenceSettings | IProjectReferenceSettings | INugetPackageReferenceSettings; if (this.currentReferenceType === ReferenceType.systemDb) { const systemDbRef: ISystemDatabaseReferenceSettings = { @@ -170,7 +175,7 @@ export class AddDatabaseReferenceDialog { }; referenceSettings = projRef; - } else { // this.currentReferenceType === ReferenceType.dacpac + } else if (this.currentReferenceType === ReferenceType.dacpac) { const dacpacRef: IDacpacReferenceSettings = { databaseName: ensureSetOrDefined(this.databaseNameTextbox?.value), dacpacFileLocation: vscode.Uri.file(this.dacpacTextbox?.value), @@ -178,6 +183,14 @@ export class AddDatabaseReferenceDialog { }; referenceSettings = dacpacRef; + } else { // this.currentReferenceType === ReferenceType.nupkg + const nupkgRef: INugetPackageReferenceSettings = { + packageName: this.nupkgNameTextbox?.value, + packageVersion: this.nupkgVersionTextbox?.value, + suppressMissingDependenciesErrors: this.suppressMissingDependenciesErrorsCheckbox?.checked + } + + referenceSettings = nupkgRef; } const dbServerValues: DbServerValues = { @@ -236,6 +249,18 @@ export class AddDatabaseReferenceDialog { } }); + const nupkgRadioButton = this.view!.modelBuilder.radioButton() + .withProps({ + name: 'referenceType', + label: constants.nupkgText + }).component(); + + nupkgRadioButton.onDidChangeCheckedState((checked) => { + if (checked) { + this.nupkgRadioButtonClick(); + } + }); + if (this.projectDropdown?.values?.length) { this.projectRadioButton.checked = true; this.currentReferenceType = ReferenceType.project; @@ -247,9 +272,16 @@ export class AddDatabaseReferenceDialog { this.projectRadioButton.enabled = false; } + const radioButtons = [this.projectRadioButton, this.systemDatabaseRadioButton, dacpacRadioButton]; + + // only add the nupkg radio button for SDK-style projects + if (this.project.sqlProjStyle === ProjectType.SdkStyle) { + radioButtons.push(nupkgRadioButton); + } + let flexRadioButtonsModel: azdataType.FlexContainer = this.view!.modelBuilder.flexContainer() .withLayout({ flexFlow: 'column' }) - .withItems([this.projectRadioButton, this.systemDatabaseRadioButton, dacpacRadioButton]) + .withItems(radioButtons) .withProps({ ariaRole: 'radiogroup', ariaLabel: constants.referenceRadioButtonsGroupTitle }) .component(); @@ -262,6 +294,7 @@ export class AddDatabaseReferenceDialog { public projectRadioButtonClick(): void { this.formBuilder!.removeFormItem(this.dacpacFormComponent); this.formBuilder!.removeFormItem(this.systemDatabaseFormComponent); + this.formBuilder!.removeFormItem(this.nupkgFormComponent); this.formBuilder!.insertFormItem(this.projectFormComponent, 2); this.locationDropdown!.values = constants.locationDropdownValues; @@ -275,6 +308,7 @@ export class AddDatabaseReferenceDialog { public systemDbRadioButtonClick(): void { this.formBuilder!.removeFormItem(this.dacpacFormComponent); this.formBuilder!.removeFormItem(this.projectFormComponent); + this.formBuilder!.removeFormItem(this.nupkgFormComponent); this.formBuilder!.insertFormItem(this.systemDatabaseFormComponent, 2); // update dropdown values because only different database, same server is a valid location for system db references @@ -290,6 +324,7 @@ export class AddDatabaseReferenceDialog { public dacpacRadioButtonClick(): void { this.formBuilder!.removeFormItem(this.systemDatabaseFormComponent); this.formBuilder!.removeFormItem(this.projectFormComponent); + this.formBuilder!.removeFormItem(this.nupkgFormComponent); this.formBuilder!.insertFormItem(this.dacpacFormComponent, 2); this.locationDropdown!.values = constants.locationDropdownValues; @@ -300,6 +335,20 @@ export class AddDatabaseReferenceDialog { this.updateExampleUsage(); } + public nupkgRadioButtonClick(): void { + this.formBuilder!.removeFormItem(this.systemDatabaseFormComponent); + this.formBuilder!.removeFormItem(this.projectFormComponent); + this.formBuilder!.removeFormItem(this.dacpacFormComponent); + this.formBuilder!.insertFormItem(this.nupkgFormComponent, 2); + + this.locationDropdown!.values = constants.locationDropdownValues; + + this.currentReferenceType = ReferenceType.nupkg; + this.updateEnabledInputBoxes(); + this.tryEnableAddReferenceButton(); + this.updateExampleUsage(); + } + private async createProjectDropdown(): Promise { this.projectDropdown = this.view!.modelBuilder.dropDown().withProps({ ariaLabel: constants.databaseProject @@ -361,6 +410,34 @@ export class AddDatabaseReferenceDialog { }; } + private createNupkgFormComponentGroup(): azdataType.FormComponentGroup { + this.nupkgNameTextbox = this.view!.modelBuilder.inputBox().withProps({ + ariaLabel: constants.nupkgText, + placeHolder: constants.nupkgNamePlaceholder, + required: true + }).component(); + + this.nupkgVersionTextbox = this.view!.modelBuilder.inputBox().withProps({ + ariaLabel: constants.version, + placeHolder: constants.versionPlaceholder, + required: true + }).component(); + + return { + components: [ + { + title: constants.nupkgText, + component: this.nupkgNameTextbox + }, + { + title: constants.version, + component: this.nupkgVersionTextbox + } + ], + title: '' + } + } + private createLoadDacpacButton(): azdataType.ButtonComponent { const loadDacpacButton = this.view!.modelBuilder.button().withProps({ ariaLabel: constants.selectDacpac, @@ -466,6 +543,12 @@ export class AddDatabaseReferenceDialog { this.databaseVariableTextbox!.value = dacpacName ? `${dacpacName}` : ''; break; } + case ReferenceType.nupkg: { + const nupkgName = this.nupkgNameTextbox!.value ? path.parse(this.nupkgNameTextbox!.value!).name : ''; + this.databaseNameTextbox!.value = nupkgName; + this.databaseVariableTextbox!.value = nupkgName ? `${nupkgName}` : ''; + break; + } } } } @@ -603,6 +686,10 @@ export class AddDatabaseReferenceDialog { this.dialog.okButton.enabled = this.dacpacRequiredFieldsFilled(); break; } + case ReferenceType.nupkg: { + this.dialog.okButton.enabled = this.nupkgRequiredFieldsFilled(); + break; + } } } @@ -621,6 +708,14 @@ export class AddDatabaseReferenceDialog { || ((this.locationDropdown?.value === constants.differentDbDifferentServer && this.differentDatabaseDifferentServerRequiredFieldsFilled()))); } + private nupkgRequiredFieldsFilled(): boolean { + return !!this.nupkgNameTextbox?.value + && !!this.nupkgVersionTextbox?.value + && ((this.locationDropdown?.value === constants.sameDatabase) + || (this.locationDropdown?.value === constants.differentDbSameServer && this.differentDatabaseSameServerRequiredFieldsFilled()) + || ((this.locationDropdown?.value === constants.differentDbDifferentServer && this.differentDatabaseDifferentServerRequiredFieldsFilled()))); + } + private differentDatabaseSameServerRequiredFieldsFilled(): boolean { return !!this.databaseNameTextbox?.value; }