mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-14 01:25:37 -05:00
Add reference to another sql project (#12186)
* add projects to add database reference dialog * able to add project references * check for circular dependency * only allow adding reference to project in the same workspace * fix location dropdown when project reference is enabled * add tests * more tests * cleanup * fix flakey test * addressing comments
This commit is contained in:
@@ -5,12 +5,14 @@
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
import * as constants from '../common/constants';
|
||||
|
||||
import { Project, SystemDatabase, DatabaseReferenceLocation } from '../models/project';
|
||||
import { Project, SystemDatabase } from '../models/project';
|
||||
import { cssStyles } from '../common/uiConstants';
|
||||
import { IconPathHelper } from '../common/iconHelper';
|
||||
import { ISystemDatabaseReferenceSettings, IDacpacReferenceSettings } from '../models/IDatabaseReferenceSettings';
|
||||
import { ISystemDatabaseReferenceSettings, IDacpacReferenceSettings, IProjectReferenceSettings } from '../models/IDatabaseReferenceSettings';
|
||||
import { getSqlProjectFilesInFolder } from '../common/utils';
|
||||
|
||||
export enum ReferenceType {
|
||||
project,
|
||||
@@ -28,6 +30,8 @@ export class AddDatabaseReferenceDialog {
|
||||
public addDatabaseReferenceTab: azdata.window.DialogTab;
|
||||
private view: azdata.ModelView | undefined;
|
||||
private formBuilder: azdata.FormBuilder | undefined;
|
||||
private projectDropdown: azdata.DropDownComponent | undefined;
|
||||
private projectFormComponent: azdata.FormComponent | undefined;
|
||||
private systemDatabaseDropdown: azdata.DropDownComponent | undefined;
|
||||
private systemDatabaseFormComponent: azdata.FormComponent | undefined;
|
||||
public dacpacTextbox: azdata.InputBoxComponent | undefined;
|
||||
@@ -41,23 +45,16 @@ export class AddDatabaseReferenceDialog {
|
||||
public exampleUsage: azdata.TextComponent | undefined;
|
||||
|
||||
public currentReferenceType: ReferenceType | undefined;
|
||||
private referenceLocationMap: Map<string, DatabaseReferenceLocation>;
|
||||
|
||||
private toDispose: vscode.Disposable[] = [];
|
||||
private initDialogComplete: Deferred<void> | undefined;
|
||||
private initDialogPromise: Promise<void> = new Promise<void>((resolve, reject) => this.initDialogComplete = { resolve, reject });
|
||||
|
||||
public addReference: ((proj: Project, settings: ISystemDatabaseReferenceSettings | IDacpacReferenceSettings) => any) | undefined;
|
||||
public addReference: ((proj: Project, settings: ISystemDatabaseReferenceSettings | IDacpacReferenceSettings | IProjectReferenceSettings) => any) | undefined;
|
||||
|
||||
constructor(private project: Project) {
|
||||
this.dialog = azdata.window.createModelViewDialog(constants.addDatabaseReferenceDialogName);
|
||||
this.addDatabaseReferenceTab = azdata.window.createTab(constants.addDatabaseReferenceDialogName);
|
||||
|
||||
this.referenceLocationMap = new Map([
|
||||
[constants.sameDatabase, DatabaseReferenceLocation.sameDatabase],
|
||||
[constants.differentDbSameServer, DatabaseReferenceLocation.differentDatabaseSameServer],
|
||||
[constants.differentDbDifferentServer, DatabaseReferenceLocation.differentDatabaseDifferentServer]
|
||||
]);
|
||||
}
|
||||
|
||||
public async openDialog(): Promise<void> {
|
||||
@@ -84,6 +81,7 @@ export class AddDatabaseReferenceDialog {
|
||||
private initializeTab(): void {
|
||||
this.addDatabaseReferenceTab.registerContent(async view => {
|
||||
this.view = view;
|
||||
this.projectFormComponent = await this.createProjectDropdown();
|
||||
const radioButtonGroup = this.createRadioButtons();
|
||||
this.systemDatabaseFormComponent = this.createSystemDatabaseDropdown();
|
||||
this.dacpacFormComponent = this.createDacpacTextbox();
|
||||
@@ -100,7 +98,7 @@ export class AddDatabaseReferenceDialog {
|
||||
title: '',
|
||||
components: [
|
||||
radioButtonGroup,
|
||||
this.systemDatabaseFormComponent,
|
||||
this.currentReferenceType === ReferenceType.project ? this.projectFormComponent : this.systemDatabaseFormComponent,
|
||||
locationDropdown,
|
||||
variableSection,
|
||||
exampleUsage,
|
||||
@@ -118,15 +116,27 @@ export class AddDatabaseReferenceDialog {
|
||||
|
||||
let formModel = this.formBuilder.component();
|
||||
await view.initializeModel(formModel);
|
||||
this.updateEnabledInputBoxes();
|
||||
|
||||
this.initDialogComplete?.resolve();
|
||||
});
|
||||
}
|
||||
|
||||
public async addReferenceClick(): Promise<void> {
|
||||
let referenceSettings: ISystemDatabaseReferenceSettings | IDacpacReferenceSettings;
|
||||
let referenceSettings: ISystemDatabaseReferenceSettings | IDacpacReferenceSettings | IProjectReferenceSettings;
|
||||
|
||||
if (this.currentReferenceType === ReferenceType.systemDb) {
|
||||
if (this.currentReferenceType === ReferenceType.project) {
|
||||
referenceSettings = {
|
||||
projectName: <string>this.projectDropdown?.value,
|
||||
projectGuid: '',
|
||||
projectRelativePath: undefined,
|
||||
databaseName: <string>this.databaseNameTextbox?.value,
|
||||
databaseVariable: <string>this.databaseVariableTextbox?.value,
|
||||
serverName: <string>this.serverNameTextbox?.value,
|
||||
serverVariable: <string>this.serverVariableTextbox?.value,
|
||||
suppressMissingDependenciesErrors: <boolean>this.suppressMissingDependenciesErrorsCheckbox?.checked
|
||||
};
|
||||
} else if (this.currentReferenceType === ReferenceType.systemDb) {
|
||||
referenceSettings = {
|
||||
databaseName: <string>this.databaseNameTextbox?.value,
|
||||
systemDb: <string>this.systemDatabaseDropdown?.value === constants.master ? SystemDatabase.master : SystemDatabase.msdb,
|
||||
@@ -135,14 +145,12 @@ export class AddDatabaseReferenceDialog {
|
||||
} else { // this.currentReferenceType === ReferenceType.dacpac
|
||||
referenceSettings = {
|
||||
databaseName: <string>this.databaseNameTextbox?.value,
|
||||
databaseLocation: <DatabaseReferenceLocation>this.referenceLocationMap.get(<string>this.locationDropdown?.value),
|
||||
dacpacFileLocation: vscode.Uri.file(<string>this.dacpacTextbox?.value),
|
||||
databaseVariable: <string>this.databaseVariableTextbox?.value,
|
||||
serverName: <string>this.serverNameTextbox?.value,
|
||||
serverVariable: <string>this.serverVariableTextbox?.value,
|
||||
suppressMissingDependenciesErrors: <boolean>this.suppressMissingDependenciesErrorsCheckbox?.checked
|
||||
};
|
||||
// TODO: add project reference support
|
||||
}
|
||||
|
||||
await this.addReference!(this.project, referenceSettings);
|
||||
@@ -151,14 +159,22 @@ export class AddDatabaseReferenceDialog {
|
||||
}
|
||||
|
||||
private createRadioButtons(): azdata.FormComponent {
|
||||
// TODO: add project reference button
|
||||
const projectRadioButton = this.view!.modelBuilder.radioButton()
|
||||
.withProperties({
|
||||
name: 'referenceType',
|
||||
label: constants.projectRadioButtonTitle
|
||||
}).component();
|
||||
|
||||
projectRadioButton.onDidClick(() => {
|
||||
this.projectRadioButtonClick();
|
||||
});
|
||||
|
||||
const systemDatabaseRadioButton = this.view!.modelBuilder.radioButton()
|
||||
.withProperties({
|
||||
name: 'referenceType',
|
||||
label: constants.systemDatabaseRadioButtonTitle
|
||||
}).component();
|
||||
|
||||
systemDatabaseRadioButton.checked = true;
|
||||
systemDatabaseRadioButton.onDidClick(() => {
|
||||
this.systemDbRadioButtonClick();
|
||||
});
|
||||
@@ -173,10 +189,20 @@ export class AddDatabaseReferenceDialog {
|
||||
this.dacpacRadioButtonClick();
|
||||
});
|
||||
|
||||
this.currentReferenceType = ReferenceType.systemDb;
|
||||
if (this.projectDropdown?.values?.length) {
|
||||
projectRadioButton.checked = true;
|
||||
this.currentReferenceType = ReferenceType.project;
|
||||
} else {
|
||||
systemDatabaseRadioButton.checked = true;
|
||||
this.currentReferenceType = ReferenceType.systemDb;
|
||||
|
||||
// disable projects radio button if there aren't any projects that can be added as a reference
|
||||
projectRadioButton.enabled = false;
|
||||
}
|
||||
|
||||
let flexRadioButtonsModel: azdata.FlexContainer = this.view!.modelBuilder.flexContainer()
|
||||
.withLayout({ flexFlow: 'column' })
|
||||
.withItems([systemDatabaseRadioButton, dacpacRadioButton])
|
||||
.withItems([projectRadioButton, systemDatabaseRadioButton, dacpacRadioButton])
|
||||
.withProperties({ ariaRole: 'radiogroup' })
|
||||
.component();
|
||||
|
||||
@@ -186,21 +212,35 @@ export class AddDatabaseReferenceDialog {
|
||||
};
|
||||
}
|
||||
|
||||
public projectRadioButtonClick(): void {
|
||||
this.formBuilder!.removeFormItem(<azdata.FormComponent>this.dacpacFormComponent);
|
||||
this.formBuilder!.removeFormItem(<azdata.FormComponent>this.systemDatabaseFormComponent);
|
||||
this.formBuilder!.insertFormItem(<azdata.FormComponent>this.projectFormComponent, 2);
|
||||
|
||||
this.currentReferenceType = ReferenceType.project;
|
||||
this.updateEnabledInputBoxes();
|
||||
this.tryEnableAddReferenceButton();
|
||||
this.updateExampleUsage();
|
||||
}
|
||||
|
||||
public systemDbRadioButtonClick(): void {
|
||||
this.formBuilder!.removeFormItem(<azdata.FormComponent>this.dacpacFormComponent);
|
||||
this.formBuilder!.removeFormItem(<azdata.FormComponent>this.projectFormComponent);
|
||||
this.formBuilder!.insertFormItem(<azdata.FormComponent>this.systemDatabaseFormComponent, 2);
|
||||
|
||||
// update dropdown values because only different database, same server is a valid location for system db references
|
||||
this.locationDropdown!.values = constants.systemDbLocationDropdownValues;
|
||||
this.locationDropdown!.value = constants.differentDbSameServer;
|
||||
|
||||
this.currentReferenceType = ReferenceType.systemDb;
|
||||
this.updateEnabledInputBoxes(true);
|
||||
this.updateEnabledInputBoxes();
|
||||
this.tryEnableAddReferenceButton();
|
||||
this.updateExampleUsage();
|
||||
}
|
||||
|
||||
public dacpacRadioButtonClick(): void {
|
||||
this.formBuilder!.removeFormItem(<azdata.FormComponent>this.systemDatabaseFormComponent);
|
||||
this.formBuilder!.removeFormItem(<azdata.FormComponent>this.projectFormComponent);
|
||||
this.formBuilder!.insertFormItem(<azdata.FormComponent>this.dacpacFormComponent, 2);
|
||||
|
||||
this.locationDropdown!.values = constants.locationDropdownValues;
|
||||
@@ -212,6 +252,39 @@ export class AddDatabaseReferenceDialog {
|
||||
this.updateExampleUsage();
|
||||
}
|
||||
|
||||
private async createProjectDropdown(): Promise<azdata.FormComponent> {
|
||||
this.projectDropdown = this.view!.modelBuilder.dropDown().withProperties({
|
||||
ariaLabel: constants.databaseProject
|
||||
}).component();
|
||||
|
||||
// get projects in workspace
|
||||
const workspaceFolders = vscode.workspace.workspaceFolders;
|
||||
if (workspaceFolders?.length) {
|
||||
let projectFiles = await getSqlProjectFilesInFolder(workspaceFolders[0].uri.fsPath);
|
||||
|
||||
// check if current project is in same open folder (should only be able to add a reference to another project in
|
||||
// the folder if the current project is also in the folder)
|
||||
if (projectFiles.find(p => p === this.project.projectFilePath)) {
|
||||
// filter out current project
|
||||
projectFiles = projectFiles.filter(p => p !== this.project.projectFilePath);
|
||||
|
||||
projectFiles.forEach(p => {
|
||||
projectFiles[projectFiles.indexOf(p)] = path.parse(p).name;
|
||||
});
|
||||
|
||||
this.projectDropdown.values = projectFiles;
|
||||
} else {
|
||||
this.projectDropdown.values = [];
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
component: this.projectDropdown,
|
||||
title: constants.databaseProject
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private createSystemDatabaseDropdown(): azdata.FormComponent {
|
||||
this.systemDatabaseDropdown = this.view!.modelBuilder.dropDown().withProperties({
|
||||
values: [constants.master, constants.msdb],
|
||||
@@ -286,9 +359,11 @@ export class AddDatabaseReferenceDialog {
|
||||
private createLocationDropdown(): azdata.FormComponent {
|
||||
this.locationDropdown = this.view!.modelBuilder.dropDown().withProperties({
|
||||
ariaLabel: constants.locationDropdown,
|
||||
values: constants.systemDbLocationDropdownValues
|
||||
values: this.currentReferenceType === ReferenceType.systemDb ? constants.systemDbLocationDropdownValues : constants.locationDropdownValues
|
||||
}).component();
|
||||
|
||||
this.locationDropdown.value = constants.differentDbSameServer;
|
||||
|
||||
this.locationDropdown.onValueChanged(() => {
|
||||
this.updateEnabledInputBoxes();
|
||||
this.tryEnableAddReferenceButton();
|
||||
@@ -305,7 +380,9 @@ export class AddDatabaseReferenceDialog {
|
||||
* Update the enabled input boxes based on what the location of the database reference selected in the dropdown is
|
||||
* @param isSystemDb
|
||||
*/
|
||||
public updateEnabledInputBoxes(isSystemDb: boolean = false): void {
|
||||
public updateEnabledInputBoxes(): void {
|
||||
const isSystemDb = this.currentReferenceType === ReferenceType.systemDb;
|
||||
|
||||
if (this.locationDropdown?.value === constants.sameDatabase) {
|
||||
this.databaseNameTextbox!.enabled = false;
|
||||
this.databaseVariableTextbox!.enabled = false;
|
||||
@@ -387,7 +464,7 @@ export class AddDatabaseReferenceDialog {
|
||||
|
||||
private createExampleUsage(): azdata.FormComponent {
|
||||
this.exampleUsage = this.view!.modelBuilder.text().withProperties({
|
||||
value: constants.systemDatabaseReferenceRequired,
|
||||
value: this.currentReferenceType === ReferenceType.project ? constants.databaseNameRequiredVariableOptional : constants.systemDatabaseReferenceRequired,
|
||||
CSSStyles: { 'user-select': 'text' }
|
||||
}).component();
|
||||
|
||||
@@ -440,27 +517,35 @@ export class AddDatabaseReferenceDialog {
|
||||
*/
|
||||
public tryEnableAddReferenceButton(): void {
|
||||
switch (this.currentReferenceType) {
|
||||
case ReferenceType.project: {
|
||||
this.dialog.okButton.enabled = this.projectRequiredFieldsFilled();
|
||||
break;
|
||||
}
|
||||
case ReferenceType.systemDb: {
|
||||
this.dialog.okButton.enabled = !!this.databaseNameTextbox?.value;
|
||||
break;
|
||||
}
|
||||
case ReferenceType.dacpac: {
|
||||
this.dialog.okButton.enabled = this.dacpacFieldsRequiredFieldsFilled();
|
||||
this.dialog.okButton.enabled = this.dacpacRequiredFieldsFilled();
|
||||
break;
|
||||
}
|
||||
case ReferenceType.project: {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private dacpacFieldsRequiredFieldsFilled(): boolean {
|
||||
private dacpacRequiredFieldsFilled(): boolean {
|
||||
return !!this.dacpacTextbox?.value &&
|
||||
((this.locationDropdown?.value === constants.sameDatabase)
|
||||
|| (this.locationDropdown?.value === constants.differentDbSameServer && this.differentDatabaseSameServerRequiredFieldsFilled())
|
||||
|| ((this.locationDropdown?.value === constants.differentDbDifferentServer && this.differentDatabaseDifferentServerRequiredFieldsFilled())));
|
||||
}
|
||||
|
||||
private projectRequiredFieldsFilled(): boolean {
|
||||
return !!this.projectDropdown?.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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user