mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
add objects dialog (#23243)
This commit is contained in:
@@ -196,8 +196,10 @@ export const SelectServerRoleMemberDialogTitle = localize('objectManagement.serv
|
||||
export const SelectServerRoleOwnerDialogTitle = localize('objectManagement.serverRole.SelectOwnerDialogTitle', "Select Server Role Owner");
|
||||
|
||||
// Find Object Dialog
|
||||
export const ObjectTypesText = localize('objectManagement.objectTypesLabel', "Object Types");
|
||||
export const FilterSectionTitle = localize('objectManagement.filterSectionTitle', "Filters");
|
||||
export const ObjectTypeText = localize('objectManagement.objectTypeLabel', "Object Type");
|
||||
export const FilterText = localize('objectManagement.filterText', "Filter");
|
||||
export const SearchTextLabel = localize('objectManagement.SearchTextLabel', "Search Text");
|
||||
export const FindText = localize('objectManagement.findText', "Find");
|
||||
export const SelectText = localize('objectManagement.selectText', "Select");
|
||||
export const ObjectsText = localize('objectManagement.objectsLabel', "Objects");
|
||||
@@ -206,8 +208,15 @@ export function LoadingObjectsCompletedText(count: number): string {
|
||||
return localize('objectManagement.loadingObjectsCompletedLabel', "Loading objects completed, {0} objects found", count);
|
||||
}
|
||||
|
||||
// Util functions
|
||||
// ObjectSelectionMethodDialog
|
||||
export const ObjectSelectionMethodDialogTitle = localize('objectManagement.objectSelectionMethodDialogTitle', "Add Objects");
|
||||
export const ObjectSelectionMethodDialog_TypeLabel = localize('objectManagement.ObjectSelectionMethodDialog_TypeLabel', "How do you want to add objects?");
|
||||
export const ObjectSelectionMethodDialog_SpecificObjects = localize('objectManagement.ObjectSelectionMethodDialog_SpecificObjects', "Specific objects…");
|
||||
export const ObjectSelectionMethodDialog_AllObjectsOfTypes = localize('objectManagement.ObjectSelectionMethodDialog_AllObjectsOfTypes', "All objects of certain types");
|
||||
export const ObjectSelectionMethodDialog_AllObjectsOfSchema = localize('objectManagement.ObjectSelectionMethodDialog_AllObjectsOfSchema', "All objects belonging to a schema");
|
||||
export const ObjectSelectionMethodDialog_SelectSchemaDropdownLabel = localize('objectManagement.ObjectSelectionMethodDialog_SelectSchemaDropdownLabel', "Schema");
|
||||
|
||||
// Util functions
|
||||
export function getNodeTypeDisplayName(type: string, inTitle: boolean = false): string {
|
||||
switch (type) {
|
||||
case ObjectManagement.NodeType.ApplicationRole:
|
||||
|
||||
@@ -26,7 +26,7 @@ export class ApplicationRoleDialog extends PrincipalDialogBase<ObjectManagement.
|
||||
private ownedSchemaTable: azdata.TableComponent;
|
||||
|
||||
constructor(objectManagementService: IObjectManagementService, options: ObjectManagementDialogOptions) {
|
||||
super(objectManagementService, options, true, false);
|
||||
super(objectManagementService, { ...options, isDatabaseLevelPrincipal: true, supportEffectivePermissions: false });
|
||||
}
|
||||
|
||||
protected override postInitializeData(): void {
|
||||
|
||||
@@ -28,7 +28,7 @@ export class DatabaseRoleDialog extends PrincipalDialogBase<ObjectManagement.Dat
|
||||
private memberTable: azdata.TableComponent;
|
||||
|
||||
constructor(objectManagementService: IObjectManagementService, options: ObjectManagementDialogOptions) {
|
||||
super(objectManagementService, options, true, false);
|
||||
super(objectManagementService, { ...options, isDatabaseLevelPrincipal: true, supportEffectivePermissions: false });
|
||||
}
|
||||
|
||||
protected override get helpUrl(): string {
|
||||
|
||||
@@ -36,6 +36,7 @@ const ObjectsTableMaxRowCount = 20;
|
||||
|
||||
export class FindObjectDialog extends DialogBase<FindObjectDialogResult> {
|
||||
private objectTypesTable: azdata.TableComponent;
|
||||
private searchTextInputBox: azdata.InputBoxComponent;
|
||||
private findButton: azdata.ButtonComponent;
|
||||
private objectsTable: azdata.TableComponent;
|
||||
private objectsLoadingComponent: azdata.LoadingComponent;
|
||||
@@ -54,7 +55,7 @@ export class FindObjectDialog extends DialogBase<FindObjectDialogResult> {
|
||||
|
||||
protected override async initialize(): Promise<void> {
|
||||
this.dialogObject.okButton.enabled = false;
|
||||
this.objectTypesTable = this.createTableList<ObjectTypeInfo>(localizedConstants.ObjectTypeText,
|
||||
this.objectTypesTable = this.createTableList<ObjectTypeInfo>(localizedConstants.ObjectTypesText,
|
||||
[localizedConstants.ObjectTypeText],
|
||||
this.options.objectTypes,
|
||||
this.selectedObjectTypes,
|
||||
@@ -64,11 +65,17 @@ export class FindObjectDialog extends DialogBase<FindObjectDialogResult> {
|
||||
}, (item1, item2) => {
|
||||
return item1.name === item2.name;
|
||||
});
|
||||
this.searchTextInputBox = this.createInputBox(localizedConstants.SearchTextLabel, async () => { });
|
||||
const searchTextRow = this.createLabelInputContainer(localizedConstants.SearchTextLabel, this.searchTextInputBox);
|
||||
this.findButton = this.createButton(localizedConstants.FindText, localizedConstants.FindText, async () => {
|
||||
await this.onFindObjectButtonClick();
|
||||
}, this.options.selectAllObjectTypes);
|
||||
const buttonContainer = this.createButtonContainer([this.findButton]);
|
||||
const objectTypeSection = this.createGroup(localizedConstants.ObjectTypeText, [this.objectTypesTable, buttonContainer]);
|
||||
const filterSection = this.createGroup(localizedConstants.FilterSectionTitle, [
|
||||
searchTextRow,
|
||||
this.objectTypesTable,
|
||||
buttonContainer
|
||||
]);
|
||||
const columns = [localizedConstants.NameText, localizedConstants.ObjectTypeText];
|
||||
if (this.options.showSchemaColumn) {
|
||||
columns.splice(1, 0, localizedConstants.SchemaText);
|
||||
@@ -101,7 +108,7 @@ export class FindObjectDialog extends DialogBase<FindObjectDialogResult> {
|
||||
}).component();
|
||||
const objectsSection = this.createGroup(localizedConstants.ObjectsText, [this.objectsLoadingComponent]);
|
||||
|
||||
this.formContainer.addItems([objectTypeSection, objectsSection], this.getSectionItemLayout());
|
||||
this.formContainer.addItems([filterSection, objectsSection], this.getSectionItemLayout());
|
||||
}
|
||||
|
||||
protected override get dialogResult(): FindObjectDialogResult | undefined {
|
||||
@@ -113,7 +120,7 @@ export class FindObjectDialog extends DialogBase<FindObjectDialogResult> {
|
||||
this.objectsLoadingComponent.loading = true;
|
||||
this.findButton.enabled = false;
|
||||
try {
|
||||
const results = await this.objectManagementService.search(this.options.contextId, this.selectedObjectTypes.map(item => item.name));
|
||||
const results = await this.objectManagementService.search(this.options.contextId, this.selectedObjectTypes.map(item => item.name), this.searchTextInputBox.value);
|
||||
this.allObjects.splice(0, this.allObjects.length, ...results);
|
||||
let data;
|
||||
if (this.options.multiSelect) {
|
||||
|
||||
@@ -35,7 +35,7 @@ export class LoginDialog extends PrincipalDialogBase<ObjectManagement.Login, Obj
|
||||
private lockedOutCheckbox: azdata.CheckBoxComponent;
|
||||
|
||||
constructor(objectManagementService: IObjectManagementService, options: ObjectManagementDialogOptions) {
|
||||
super(objectManagementService, options, false);
|
||||
super(objectManagementService, { ...options, isDatabaseLevelPrincipal: false, supportEffectivePermissions: true });
|
||||
}
|
||||
|
||||
protected override get helpUrl(): string {
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import { DefaultMaxTableRowCount, DefaultTableListItemEnabledStateGetter, DialogBase } from '../../ui/dialogBase';
|
||||
import * as localizedConstants from '../localizedConstants';
|
||||
import { ObjectTypeInfo } from './findObjectDialog';
|
||||
|
||||
export enum ObjectSelectionMethod {
|
||||
SpecificObjects,
|
||||
AllObjectsOfTypes,
|
||||
AllObjectsOfSchema
|
||||
}
|
||||
|
||||
export interface ObjectSelectionMethodDialogOptions {
|
||||
objectTypes: ObjectTypeInfo[];
|
||||
schemas: string[];
|
||||
}
|
||||
|
||||
export interface ObjectSelectionMethodDialogResult {
|
||||
method: ObjectSelectionMethod;
|
||||
schema: string | undefined;
|
||||
objectTypes: ObjectTypeInfo[];
|
||||
}
|
||||
|
||||
export class ObjectSelectionMethodDialog extends DialogBase<ObjectSelectionMethodDialogResult> {
|
||||
private specificObjectsRadioButton: azdata.RadioButtonComponent;
|
||||
private allObjectsOfTypesRadioButton: azdata.RadioButtonComponent;
|
||||
private allObjectsOfSchemaRadioButton: azdata.RadioButtonComponent;
|
||||
private objectTypesTable: azdata.TableComponent;
|
||||
private schemaRow: azdata.FlexContainer;
|
||||
private result: ObjectSelectionMethodDialogResult;
|
||||
|
||||
constructor(private readonly options: ObjectSelectionMethodDialogOptions) {
|
||||
super(localizedConstants.ObjectSelectionMethodDialogTitle, 'ObjectSelectionMethodDialog');
|
||||
this.result = {
|
||||
method: ObjectSelectionMethod.SpecificObjects,
|
||||
schema: undefined,
|
||||
objectTypes: []
|
||||
};
|
||||
}
|
||||
|
||||
protected override async initialize(): Promise<void> {
|
||||
const radioGroupName = 'objectSelectionMethodRadioGroup';
|
||||
this.specificObjectsRadioButton = this.createRadioButton(localizedConstants.ObjectSelectionMethodDialog_SpecificObjects, radioGroupName, true, async (checked) => { await this.handleTypeChange(checked); });
|
||||
this.allObjectsOfTypesRadioButton = this.createRadioButton(localizedConstants.ObjectSelectionMethodDialog_AllObjectsOfTypes, radioGroupName, false, async (checked) => { await this.handleTypeChange(checked); });
|
||||
this.allObjectsOfSchemaRadioButton = this.createRadioButton(localizedConstants.ObjectSelectionMethodDialog_AllObjectsOfSchema, radioGroupName, false, async (checked) => { await this.handleTypeChange(checked); });
|
||||
|
||||
const typeGroup = this.createGroup(localizedConstants.ObjectSelectionMethodDialog_TypeLabel, [this.specificObjectsRadioButton, this.allObjectsOfTypesRadioButton, this.allObjectsOfSchemaRadioButton], false);
|
||||
this.objectTypesTable = this.createTableList<ObjectTypeInfo>(localizedConstants.ObjectTypeText,
|
||||
[localizedConstants.ObjectTypesText],
|
||||
this.options.objectTypes,
|
||||
this.result.objectTypes,
|
||||
DefaultMaxTableRowCount,
|
||||
DefaultTableListItemEnabledStateGetter, (item) => {
|
||||
return [item.displayName];
|
||||
}, (item1, item2) => {
|
||||
return item1.name === item2.name;
|
||||
});
|
||||
|
||||
const schemaDropdown = this.createDropdown(localizedConstants.ObjectSelectionMethodDialog_SelectSchemaDropdownLabel, async (newValue) => {
|
||||
this.result.schema = newValue;
|
||||
}, this.options.schemas, this.options.schemas[0]);
|
||||
this.schemaRow = this.createLabelInputContainer(localizedConstants.ObjectSelectionMethodDialog_SelectSchemaDropdownLabel, schemaDropdown);
|
||||
await this.setComponentsVisibility(false, false);
|
||||
this.formContainer.addItems([typeGroup, this.schemaRow, this.objectTypesTable], this.getSectionItemLayout());
|
||||
}
|
||||
|
||||
private async handleTypeChange(checked: boolean): Promise<void> {
|
||||
let method: ObjectSelectionMethod = ObjectSelectionMethod.SpecificObjects;
|
||||
let showSchema = false;
|
||||
let showObjectTypes = false;
|
||||
await this.setComponentsVisibility(showObjectTypes, showSchema);
|
||||
if (this.allObjectsOfTypesRadioButton.checked) {
|
||||
method = ObjectSelectionMethod.AllObjectsOfTypes;
|
||||
showSchema = false;
|
||||
showObjectTypes = true;
|
||||
} else if (this.allObjectsOfSchemaRadioButton.checked) {
|
||||
method = ObjectSelectionMethod.AllObjectsOfSchema;
|
||||
showSchema = true;
|
||||
showObjectTypes = false;
|
||||
}
|
||||
this.result.method = method;
|
||||
await this.setComponentsVisibility(showObjectTypes, showSchema);
|
||||
}
|
||||
|
||||
private async setComponentsVisibility(showObjectTypes: boolean, showSchema: boolean): Promise<void> {
|
||||
await this.schemaRow.updateCssStyles({ display: showSchema ? 'flex' : 'none' });
|
||||
await this.objectTypesTable.updateCssStyles({ display: showObjectTypes ? 'block' : 'none' });
|
||||
}
|
||||
|
||||
protected override get dialogResult(): ObjectSelectionMethodDialogResult | undefined {
|
||||
return this.result;
|
||||
}
|
||||
|
||||
protected override async onFormFieldChange(): Promise<void> {
|
||||
this.dialogObject.okButton.enabled = this.result.method !== ObjectSelectionMethod.AllObjectsOfTypes || this.result.objectTypes.length > 0;
|
||||
}
|
||||
}
|
||||
@@ -8,14 +8,20 @@ import * as mssql from 'mssql';
|
||||
import * as localizedConstants from '../localizedConstants';
|
||||
|
||||
import { ObjectManagementDialogBase, ObjectManagementDialogOptions } from './objectManagementDialogBase';
|
||||
import { FindObjectDialog } from './findObjectDialog';
|
||||
import { FindObjectDialog, FindObjectDialogResult } from './findObjectDialog';
|
||||
import { deepClone } from '../../util/objects';
|
||||
import { DefaultTableWidth, getTableHeight } from '../../ui/dialogBase';
|
||||
import { ObjectSelectionMethod, ObjectSelectionMethodDialog } from './objectSelectionMethodDialog';
|
||||
|
||||
const GrantColumnIndex = 2;
|
||||
const WithGrantColumnIndex = 3;
|
||||
const DenyColumnIndex = 4;
|
||||
|
||||
export interface PrincipalDialogOptions extends ObjectManagementDialogOptions {
|
||||
isDatabaseLevelPrincipal: boolean;
|
||||
supportEffectivePermissions: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for security principal dialogs such as user, role, etc.
|
||||
*/
|
||||
@@ -28,8 +34,8 @@ export abstract class PrincipalDialogBase<ObjectInfoType extends mssql.ObjectMan
|
||||
protected effectivePermissionTableLabel: azdata.TextComponent;
|
||||
private securablePermissions: mssql.ObjectManagement.SecurablePermissions[] = [];
|
||||
|
||||
constructor(objectManagementService: mssql.IObjectManagementService, options: ObjectManagementDialogOptions, private readonly showSchemaColumn: boolean, private readonly supportEffectivePermissions: boolean = true) {
|
||||
super(objectManagementService, options);
|
||||
constructor(objectManagementService: mssql.IObjectManagementService, private readonly dialogOptions: PrincipalDialogOptions) {
|
||||
super(objectManagementService, dialogOptions);
|
||||
}
|
||||
|
||||
protected override async initializeUI(): Promise<void> {
|
||||
@@ -40,12 +46,12 @@ export abstract class PrincipalDialogBase<ObjectInfoType extends mssql.ObjectMan
|
||||
private initializeSecurableSection(): void {
|
||||
const items: azdata.Component[] = [];
|
||||
const securableTableColumns = [localizedConstants.NameText, localizedConstants.ObjectTypeText];
|
||||
if (this.showSchemaColumn) {
|
||||
if (this.dialogOptions.isDatabaseLevelPrincipal) {
|
||||
securableTableColumns.splice(1, 0, localizedConstants.SchemaText);
|
||||
}
|
||||
this.securableTable = this.createTable(localizedConstants.SecurablesText, securableTableColumns, this.getSecurableTableData());
|
||||
const buttonContainer = this.addButtonsForTable(this.securableTable, localizedConstants.AddSecurableAriaLabel, localizedConstants.RemoveSecurableAriaLabel,
|
||||
() => this.onAddSecurableButtonClicked(), () => this.onRemoveSecurableButtonClicked());
|
||||
(button) => this.onAddSecurableButtonClicked(button), () => this.onRemoveSecurableButtonClicked());
|
||||
this.disposables.push(this.securableTable.onRowSelected(async () => {
|
||||
await this.updatePermissionsTable();
|
||||
}));
|
||||
@@ -115,19 +121,40 @@ export abstract class PrincipalDialogBase<ObjectInfoType extends mssql.ObjectMan
|
||||
this.securableSection = this.createGroup(localizedConstants.SecurablesText, items, true, true);
|
||||
}
|
||||
|
||||
private async onAddSecurableButtonClicked(): Promise<void> {
|
||||
const dialog = new FindObjectDialog(this.objectManagementService, {
|
||||
objectTypes: this.viewInfo.supportedSecurableTypes,
|
||||
selectAllObjectTypes: false,
|
||||
multiSelect: true,
|
||||
contextId: this.contextId,
|
||||
title: localizedConstants.SelectSecurablesDialogTitle,
|
||||
showSchemaColumn: this.showSchemaColumn
|
||||
});
|
||||
await dialog.open();
|
||||
const result = await dialog.waitForClose();
|
||||
if (result && result.selectedObjects.length > 0) {
|
||||
result.selectedObjects.forEach(obj => {
|
||||
private async onAddSecurableButtonClicked(button: azdata.ButtonComponent): Promise<void> {
|
||||
const selectedObjects: mssql.ObjectManagement.SearchResultItem[] = [];
|
||||
if (this.dialogOptions.isDatabaseLevelPrincipal) {
|
||||
const methodDialog = new ObjectSelectionMethodDialog({
|
||||
objectTypes: this.viewInfo.supportedSecurableTypes,
|
||||
schemas: (<mssql.ObjectManagement.DatabaseLevelPrincipalViewInfo<mssql.ObjectManagement.SecurityPrincipalObject>><unknown>this.viewInfo).schemas,
|
||||
});
|
||||
await methodDialog.open();
|
||||
const methodResult = await methodDialog.waitForClose();
|
||||
if (methodResult) {
|
||||
switch (methodResult.method) {
|
||||
case ObjectSelectionMethod.AllObjectsOfTypes:
|
||||
selectedObjects.push(... await this.searchForObjects(methodResult.objectTypes.map(item => item.name)));
|
||||
break;
|
||||
case ObjectSelectionMethod.AllObjectsOfSchema:
|
||||
selectedObjects.push(... await this.searchForObjects(this.viewInfo.supportedSecurableTypes.map(item => item.name), methodResult.schema));
|
||||
break;
|
||||
default:
|
||||
const objectsResult = await this.openFindObjectsDialog();
|
||||
if (objectsResult) {
|
||||
selectedObjects.push(...objectsResult.selectedObjects);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const result = await this.openFindObjectsDialog();
|
||||
if (result) {
|
||||
selectedObjects.push(...result.selectedObjects);
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedObjects.length > 0) {
|
||||
selectedObjects.forEach(obj => {
|
||||
if (this.securablePermissions.find(securable => securable.type === obj.type && securable.name === obj.name && securable.schema === obj.schema)) {
|
||||
return;
|
||||
}
|
||||
@@ -150,6 +177,27 @@ export abstract class PrincipalDialogBase<ObjectInfoType extends mssql.ObjectMan
|
||||
const data = this.getSecurableTableData();
|
||||
await this.setTableData(this.securableTable, data);
|
||||
}
|
||||
await button.focus();
|
||||
}
|
||||
|
||||
private async searchForObjects(objectTypes: string[], schema: string = undefined): Promise<mssql.ObjectManagement.SearchResultItem[]> {
|
||||
this.updateLoadingStatus(true);
|
||||
const result = await this.objectManagementService.search(this.contextId, objectTypes, undefined, schema);
|
||||
this.updateLoadingStatus(false);
|
||||
return result;
|
||||
}
|
||||
|
||||
private async openFindObjectsDialog(): Promise<FindObjectDialogResult> {
|
||||
const dialog = new FindObjectDialog(this.objectManagementService, {
|
||||
objectTypes: this.viewInfo.supportedSecurableTypes,
|
||||
selectAllObjectTypes: false,
|
||||
multiSelect: true,
|
||||
contextId: this.contextId,
|
||||
title: localizedConstants.SelectSecurablesDialogTitle,
|
||||
showSchemaColumn: this.dialogOptions.isDatabaseLevelPrincipal
|
||||
});
|
||||
await dialog.open();
|
||||
return await dialog.waitForClose();
|
||||
}
|
||||
|
||||
private async onRemoveSecurableButtonClicked(): Promise<void> {
|
||||
@@ -164,7 +212,7 @@ export abstract class PrincipalDialogBase<ObjectInfoType extends mssql.ObjectMan
|
||||
private getSecurableTableData(): string[][] {
|
||||
return this.securablePermissions.map(securable => {
|
||||
const row = [securable.name, this.getSecurableTypeDisplayName(securable.type)];
|
||||
if (this.showSchemaColumn) {
|
||||
if (this.dialogOptions.isDatabaseLevelPrincipal) {
|
||||
row.splice(1, 0, securable.schema);
|
||||
}
|
||||
return row;
|
||||
@@ -223,6 +271,6 @@ export abstract class PrincipalDialogBase<ObjectInfoType extends mssql.ObjectMan
|
||||
}
|
||||
|
||||
private get showEffectivePermissions(): boolean {
|
||||
return !this.options.isNewObject && this.supportEffectivePermissions;
|
||||
return !this.dialogOptions.isNewObject && this.dialogOptions.supportEffectivePermissions;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ export class ServerRoleDialog extends PrincipalDialogBase<ObjectManagement.Serve
|
||||
|
||||
|
||||
constructor(objectManagementService: IObjectManagementService, options: ObjectManagementDialogOptions) {
|
||||
super(objectManagementService, options, false, false);
|
||||
super(objectManagementService, { ...options, isDatabaseLevelPrincipal: false, supportEffectivePermissions: false });
|
||||
}
|
||||
|
||||
protected override get helpUrl(): string {
|
||||
|
||||
@@ -32,7 +32,7 @@ export class UserDialog extends PrincipalDialogBase<ObjectManagement.User, Objec
|
||||
private membershipTable: azdata.TableComponent;
|
||||
|
||||
constructor(objectManagementService: IObjectManagementService, options: ObjectManagementDialogOptions) {
|
||||
super(objectManagementService, options, true);
|
||||
super(objectManagementService, { ...options, isDatabaseLevelPrincipal: true, supportEffectivePermissions: true });
|
||||
}
|
||||
|
||||
protected override get helpUrl(): string {
|
||||
|
||||
Reference in New Issue
Block a user