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:
28
extensions/mssql/src/mssql.d.ts
vendored
28
extensions/mssql/src/mssql.d.ts
vendored
@@ -975,6 +975,16 @@ declare module 'mssql' {
|
|||||||
supportedSecurableTypes: SecurableTypeMetadata[];
|
supportedSecurableTypes: SecurableTypeMetadata[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base interface for database level security principal object's view information.
|
||||||
|
*/
|
||||||
|
export interface DatabaseLevelPrincipalViewInfo<T extends SecurityPrincipalObject> extends SecurityPrincipalViewInfo<T> {
|
||||||
|
/**
|
||||||
|
* The schemas in the database.
|
||||||
|
*/
|
||||||
|
schemas: string[];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Server level login.
|
* Server level login.
|
||||||
*/
|
*/
|
||||||
@@ -1237,7 +1247,7 @@ declare module 'mssql' {
|
|||||||
/**
|
/**
|
||||||
* The information required to render the user view.
|
* The information required to render the user view.
|
||||||
*/
|
*/
|
||||||
export interface UserViewInfo extends SecurityPrincipalViewInfo<User> {
|
export interface UserViewInfo extends DatabaseLevelPrincipalViewInfo<User> {
|
||||||
/**
|
/**
|
||||||
* All user types supported by the database.
|
* All user types supported by the database.
|
||||||
*/
|
*/
|
||||||
@@ -1246,10 +1256,6 @@ declare module 'mssql' {
|
|||||||
* All languages supported by the database.
|
* All languages supported by the database.
|
||||||
*/
|
*/
|
||||||
languages: string[];
|
languages: string[];
|
||||||
/**
|
|
||||||
* All schemas in the database.
|
|
||||||
*/
|
|
||||||
schemas: string[];
|
|
||||||
/**
|
/**
|
||||||
* Name of all the logins in the server.
|
* Name of all the logins in the server.
|
||||||
*/
|
*/
|
||||||
@@ -1313,11 +1319,7 @@ declare module 'mssql' {
|
|||||||
/**
|
/**
|
||||||
* Interface representing the information required to render the application role view.
|
* Interface representing the information required to render the application role view.
|
||||||
*/
|
*/
|
||||||
export interface ApplicationRoleViewInfo extends SecurityPrincipalViewInfo<ApplicationRoleInfo> {
|
export interface ApplicationRoleViewInfo extends DatabaseLevelPrincipalViewInfo<ApplicationRoleInfo> {
|
||||||
/**
|
|
||||||
* List of all the schemas in the database.
|
|
||||||
*/
|
|
||||||
schemas: string[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1341,11 +1343,7 @@ declare module 'mssql' {
|
|||||||
/**
|
/**
|
||||||
* Interface representing the information required to render the database role view.
|
* Interface representing the information required to render the database role view.
|
||||||
*/
|
*/
|
||||||
export interface DatabaseRoleViewInfo extends SecurityPrincipalViewInfo<DatabaseRoleInfo> {
|
export interface DatabaseRoleViewInfo extends DatabaseLevelPrincipalViewInfo<DatabaseRoleInfo> {
|
||||||
/**
|
|
||||||
* List of all the schemas in the database.
|
|
||||||
*/
|
|
||||||
schemas: string[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -196,8 +196,10 @@ export const SelectServerRoleMemberDialogTitle = localize('objectManagement.serv
|
|||||||
export const SelectServerRoleOwnerDialogTitle = localize('objectManagement.serverRole.SelectOwnerDialogTitle', "Select Server Role Owner");
|
export const SelectServerRoleOwnerDialogTitle = localize('objectManagement.serverRole.SelectOwnerDialogTitle', "Select Server Role Owner");
|
||||||
|
|
||||||
// Find Object Dialog
|
// 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 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 FindText = localize('objectManagement.findText', "Find");
|
||||||
export const SelectText = localize('objectManagement.selectText', "Select");
|
export const SelectText = localize('objectManagement.selectText', "Select");
|
||||||
export const ObjectsText = localize('objectManagement.objectsLabel', "Objects");
|
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);
|
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 {
|
export function getNodeTypeDisplayName(type: string, inTitle: boolean = false): string {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case ObjectManagement.NodeType.ApplicationRole:
|
case ObjectManagement.NodeType.ApplicationRole:
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export class ApplicationRoleDialog extends PrincipalDialogBase<ObjectManagement.
|
|||||||
private ownedSchemaTable: azdata.TableComponent;
|
private ownedSchemaTable: azdata.TableComponent;
|
||||||
|
|
||||||
constructor(objectManagementService: IObjectManagementService, options: ObjectManagementDialogOptions) {
|
constructor(objectManagementService: IObjectManagementService, options: ObjectManagementDialogOptions) {
|
||||||
super(objectManagementService, options, true, false);
|
super(objectManagementService, { ...options, isDatabaseLevelPrincipal: true, supportEffectivePermissions: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override postInitializeData(): void {
|
protected override postInitializeData(): void {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export class DatabaseRoleDialog extends PrincipalDialogBase<ObjectManagement.Dat
|
|||||||
private memberTable: azdata.TableComponent;
|
private memberTable: azdata.TableComponent;
|
||||||
|
|
||||||
constructor(objectManagementService: IObjectManagementService, options: ObjectManagementDialogOptions) {
|
constructor(objectManagementService: IObjectManagementService, options: ObjectManagementDialogOptions) {
|
||||||
super(objectManagementService, options, true, false);
|
super(objectManagementService, { ...options, isDatabaseLevelPrincipal: true, supportEffectivePermissions: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override get helpUrl(): string {
|
protected override get helpUrl(): string {
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ const ObjectsTableMaxRowCount = 20;
|
|||||||
|
|
||||||
export class FindObjectDialog extends DialogBase<FindObjectDialogResult> {
|
export class FindObjectDialog extends DialogBase<FindObjectDialogResult> {
|
||||||
private objectTypesTable: azdata.TableComponent;
|
private objectTypesTable: azdata.TableComponent;
|
||||||
|
private searchTextInputBox: azdata.InputBoxComponent;
|
||||||
private findButton: azdata.ButtonComponent;
|
private findButton: azdata.ButtonComponent;
|
||||||
private objectsTable: azdata.TableComponent;
|
private objectsTable: azdata.TableComponent;
|
||||||
private objectsLoadingComponent: azdata.LoadingComponent;
|
private objectsLoadingComponent: azdata.LoadingComponent;
|
||||||
@@ -54,7 +55,7 @@ export class FindObjectDialog extends DialogBase<FindObjectDialogResult> {
|
|||||||
|
|
||||||
protected override async initialize(): Promise<void> {
|
protected override async initialize(): Promise<void> {
|
||||||
this.dialogObject.okButton.enabled = false;
|
this.dialogObject.okButton.enabled = false;
|
||||||
this.objectTypesTable = this.createTableList<ObjectTypeInfo>(localizedConstants.ObjectTypeText,
|
this.objectTypesTable = this.createTableList<ObjectTypeInfo>(localizedConstants.ObjectTypesText,
|
||||||
[localizedConstants.ObjectTypeText],
|
[localizedConstants.ObjectTypeText],
|
||||||
this.options.objectTypes,
|
this.options.objectTypes,
|
||||||
this.selectedObjectTypes,
|
this.selectedObjectTypes,
|
||||||
@@ -64,11 +65,17 @@ export class FindObjectDialog extends DialogBase<FindObjectDialogResult> {
|
|||||||
}, (item1, item2) => {
|
}, (item1, item2) => {
|
||||||
return item1.name === item2.name;
|
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 () => {
|
this.findButton = this.createButton(localizedConstants.FindText, localizedConstants.FindText, async () => {
|
||||||
await this.onFindObjectButtonClick();
|
await this.onFindObjectButtonClick();
|
||||||
}, this.options.selectAllObjectTypes);
|
}, this.options.selectAllObjectTypes);
|
||||||
const buttonContainer = this.createButtonContainer([this.findButton]);
|
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];
|
const columns = [localizedConstants.NameText, localizedConstants.ObjectTypeText];
|
||||||
if (this.options.showSchemaColumn) {
|
if (this.options.showSchemaColumn) {
|
||||||
columns.splice(1, 0, localizedConstants.SchemaText);
|
columns.splice(1, 0, localizedConstants.SchemaText);
|
||||||
@@ -101,7 +108,7 @@ export class FindObjectDialog extends DialogBase<FindObjectDialogResult> {
|
|||||||
}).component();
|
}).component();
|
||||||
const objectsSection = this.createGroup(localizedConstants.ObjectsText, [this.objectsLoadingComponent]);
|
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 {
|
protected override get dialogResult(): FindObjectDialogResult | undefined {
|
||||||
@@ -113,7 +120,7 @@ export class FindObjectDialog extends DialogBase<FindObjectDialogResult> {
|
|||||||
this.objectsLoadingComponent.loading = true;
|
this.objectsLoadingComponent.loading = true;
|
||||||
this.findButton.enabled = false;
|
this.findButton.enabled = false;
|
||||||
try {
|
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);
|
this.allObjects.splice(0, this.allObjects.length, ...results);
|
||||||
let data;
|
let data;
|
||||||
if (this.options.multiSelect) {
|
if (this.options.multiSelect) {
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export class LoginDialog extends PrincipalDialogBase<ObjectManagement.Login, Obj
|
|||||||
private lockedOutCheckbox: azdata.CheckBoxComponent;
|
private lockedOutCheckbox: azdata.CheckBoxComponent;
|
||||||
|
|
||||||
constructor(objectManagementService: IObjectManagementService, options: ObjectManagementDialogOptions) {
|
constructor(objectManagementService: IObjectManagementService, options: ObjectManagementDialogOptions) {
|
||||||
super(objectManagementService, options, false);
|
super(objectManagementService, { ...options, isDatabaseLevelPrincipal: false, supportEffectivePermissions: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override get helpUrl(): string {
|
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 * as localizedConstants from '../localizedConstants';
|
||||||
|
|
||||||
import { ObjectManagementDialogBase, ObjectManagementDialogOptions } from './objectManagementDialogBase';
|
import { ObjectManagementDialogBase, ObjectManagementDialogOptions } from './objectManagementDialogBase';
|
||||||
import { FindObjectDialog } from './findObjectDialog';
|
import { FindObjectDialog, FindObjectDialogResult } from './findObjectDialog';
|
||||||
import { deepClone } from '../../util/objects';
|
import { deepClone } from '../../util/objects';
|
||||||
import { DefaultTableWidth, getTableHeight } from '../../ui/dialogBase';
|
import { DefaultTableWidth, getTableHeight } from '../../ui/dialogBase';
|
||||||
|
import { ObjectSelectionMethod, ObjectSelectionMethodDialog } from './objectSelectionMethodDialog';
|
||||||
|
|
||||||
const GrantColumnIndex = 2;
|
const GrantColumnIndex = 2;
|
||||||
const WithGrantColumnIndex = 3;
|
const WithGrantColumnIndex = 3;
|
||||||
const DenyColumnIndex = 4;
|
const DenyColumnIndex = 4;
|
||||||
|
|
||||||
|
export interface PrincipalDialogOptions extends ObjectManagementDialogOptions {
|
||||||
|
isDatabaseLevelPrincipal: boolean;
|
||||||
|
supportEffectivePermissions: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for security principal dialogs such as user, role, etc.
|
* 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;
|
protected effectivePermissionTableLabel: azdata.TextComponent;
|
||||||
private securablePermissions: mssql.ObjectManagement.SecurablePermissions[] = [];
|
private securablePermissions: mssql.ObjectManagement.SecurablePermissions[] = [];
|
||||||
|
|
||||||
constructor(objectManagementService: mssql.IObjectManagementService, options: ObjectManagementDialogOptions, private readonly showSchemaColumn: boolean, private readonly supportEffectivePermissions: boolean = true) {
|
constructor(objectManagementService: mssql.IObjectManagementService, private readonly dialogOptions: PrincipalDialogOptions) {
|
||||||
super(objectManagementService, options);
|
super(objectManagementService, dialogOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async initializeUI(): Promise<void> {
|
protected override async initializeUI(): Promise<void> {
|
||||||
@@ -40,12 +46,12 @@ export abstract class PrincipalDialogBase<ObjectInfoType extends mssql.ObjectMan
|
|||||||
private initializeSecurableSection(): void {
|
private initializeSecurableSection(): void {
|
||||||
const items: azdata.Component[] = [];
|
const items: azdata.Component[] = [];
|
||||||
const securableTableColumns = [localizedConstants.NameText, localizedConstants.ObjectTypeText];
|
const securableTableColumns = [localizedConstants.NameText, localizedConstants.ObjectTypeText];
|
||||||
if (this.showSchemaColumn) {
|
if (this.dialogOptions.isDatabaseLevelPrincipal) {
|
||||||
securableTableColumns.splice(1, 0, localizedConstants.SchemaText);
|
securableTableColumns.splice(1, 0, localizedConstants.SchemaText);
|
||||||
}
|
}
|
||||||
this.securableTable = this.createTable(localizedConstants.SecurablesText, securableTableColumns, this.getSecurableTableData());
|
this.securableTable = this.createTable(localizedConstants.SecurablesText, securableTableColumns, this.getSecurableTableData());
|
||||||
const buttonContainer = this.addButtonsForTable(this.securableTable, localizedConstants.AddSecurableAriaLabel, localizedConstants.RemoveSecurableAriaLabel,
|
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 () => {
|
this.disposables.push(this.securableTable.onRowSelected(async () => {
|
||||||
await this.updatePermissionsTable();
|
await this.updatePermissionsTable();
|
||||||
}));
|
}));
|
||||||
@@ -115,19 +121,40 @@ export abstract class PrincipalDialogBase<ObjectInfoType extends mssql.ObjectMan
|
|||||||
this.securableSection = this.createGroup(localizedConstants.SecurablesText, items, true, true);
|
this.securableSection = this.createGroup(localizedConstants.SecurablesText, items, true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async onAddSecurableButtonClicked(): Promise<void> {
|
private async onAddSecurableButtonClicked(button: azdata.ButtonComponent): Promise<void> {
|
||||||
const dialog = new FindObjectDialog(this.objectManagementService, {
|
const selectedObjects: mssql.ObjectManagement.SearchResultItem[] = [];
|
||||||
objectTypes: this.viewInfo.supportedSecurableTypes,
|
if (this.dialogOptions.isDatabaseLevelPrincipal) {
|
||||||
selectAllObjectTypes: false,
|
const methodDialog = new ObjectSelectionMethodDialog({
|
||||||
multiSelect: true,
|
objectTypes: this.viewInfo.supportedSecurableTypes,
|
||||||
contextId: this.contextId,
|
schemas: (<mssql.ObjectManagement.DatabaseLevelPrincipalViewInfo<mssql.ObjectManagement.SecurityPrincipalObject>><unknown>this.viewInfo).schemas,
|
||||||
title: localizedConstants.SelectSecurablesDialogTitle,
|
});
|
||||||
showSchemaColumn: this.showSchemaColumn
|
await methodDialog.open();
|
||||||
});
|
const methodResult = await methodDialog.waitForClose();
|
||||||
await dialog.open();
|
if (methodResult) {
|
||||||
const result = await dialog.waitForClose();
|
switch (methodResult.method) {
|
||||||
if (result && result.selectedObjects.length > 0) {
|
case ObjectSelectionMethod.AllObjectsOfTypes:
|
||||||
result.selectedObjects.forEach(obj => {
|
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)) {
|
if (this.securablePermissions.find(securable => securable.type === obj.type && securable.name === obj.name && securable.schema === obj.schema)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -150,6 +177,27 @@ export abstract class PrincipalDialogBase<ObjectInfoType extends mssql.ObjectMan
|
|||||||
const data = this.getSecurableTableData();
|
const data = this.getSecurableTableData();
|
||||||
await this.setTableData(this.securableTable, data);
|
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> {
|
private async onRemoveSecurableButtonClicked(): Promise<void> {
|
||||||
@@ -164,7 +212,7 @@ export abstract class PrincipalDialogBase<ObjectInfoType extends mssql.ObjectMan
|
|||||||
private getSecurableTableData(): string[][] {
|
private getSecurableTableData(): string[][] {
|
||||||
return this.securablePermissions.map(securable => {
|
return this.securablePermissions.map(securable => {
|
||||||
const row = [securable.name, this.getSecurableTypeDisplayName(securable.type)];
|
const row = [securable.name, this.getSecurableTypeDisplayName(securable.type)];
|
||||||
if (this.showSchemaColumn) {
|
if (this.dialogOptions.isDatabaseLevelPrincipal) {
|
||||||
row.splice(1, 0, securable.schema);
|
row.splice(1, 0, securable.schema);
|
||||||
}
|
}
|
||||||
return row;
|
return row;
|
||||||
@@ -223,6 +271,6 @@ export abstract class PrincipalDialogBase<ObjectInfoType extends mssql.ObjectMan
|
|||||||
}
|
}
|
||||||
|
|
||||||
private get showEffectivePermissions(): boolean {
|
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) {
|
constructor(objectManagementService: IObjectManagementService, options: ObjectManagementDialogOptions) {
|
||||||
super(objectManagementService, options, false, false);
|
super(objectManagementService, { ...options, isDatabaseLevelPrincipal: false, supportEffectivePermissions: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override get helpUrl(): string {
|
protected override get helpUrl(): string {
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export class UserDialog extends PrincipalDialogBase<ObjectManagement.User, Objec
|
|||||||
private membershipTable: azdata.TableComponent;
|
private membershipTable: azdata.TableComponent;
|
||||||
|
|
||||||
constructor(objectManagementService: IObjectManagementService, options: ObjectManagementDialogOptions) {
|
constructor(objectManagementService: IObjectManagementService, options: ObjectManagementDialogOptions) {
|
||||||
super(objectManagementService, options, true);
|
super(objectManagementService, { ...options, isDatabaseLevelPrincipal: true, supportEffectivePermissions: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override get helpUrl(): string {
|
protected override get helpUrl(): string {
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ export abstract class DialogBase<DialogResult> {
|
|||||||
|
|
||||||
public async open(): Promise<void> {
|
public async open(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
this.onLoadingStatusChanged(true);
|
this.updateLoadingStatus(true);
|
||||||
const initializeDialogPromise = new Promise<void>((async resolve => {
|
const initializeDialogPromise = new Promise<void>((async resolve => {
|
||||||
this.dialogObject.registerContent(async view => {
|
this.dialogObject.registerContent(async view => {
|
||||||
this._modelView = view;
|
this._modelView = view;
|
||||||
@@ -97,7 +97,7 @@ export abstract class DialogBase<DialogResult> {
|
|||||||
azdata.window.openDialog(this.dialogObject);
|
azdata.window.openDialog(this.dialogObject);
|
||||||
await initializeDialogPromise;
|
await initializeDialogPromise;
|
||||||
await this.initialize();
|
await this.initialize();
|
||||||
this.onLoadingStatusChanged(false);
|
this.updateLoadingStatus(false);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
azdata.window.closeDialog(this.dialogObject);
|
azdata.window.closeDialog(this.dialogObject);
|
||||||
throw err;
|
throw err;
|
||||||
@@ -121,10 +121,10 @@ export abstract class DialogBase<DialogResult> {
|
|||||||
return errors.length === 0;
|
return errors.length === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected createLabelInputContainer(label: string, input: azdata.InputBoxComponent | azdata.DropDownComponent): azdata.FlexContainer {
|
protected createLabelInputContainer(label: string, component: azdata.Component, required: boolean = false): azdata.FlexContainer {
|
||||||
const labelComponent = this.modelView.modelBuilder.text().withProps({ width: DefaultLabelWidth, value: label, requiredIndicator: input.required }).component();
|
const labelComponent = this.modelView.modelBuilder.text().withProps({ width: DefaultLabelWidth, value: label, requiredIndicator: required }).component();
|
||||||
const container = this.modelView.modelBuilder.flexContainer().withLayout({ flexFlow: 'horizontal', flexWrap: 'nowrap', alignItems: 'center' }).withItems([labelComponent], { flex: '0 0 auto' }).component();
|
const container = this.modelView.modelBuilder.flexContainer().withLayout({ flexFlow: 'horizontal', flexWrap: 'nowrap', alignItems: 'center' }).withItems([labelComponent], { flex: '0 0 auto' }).component();
|
||||||
container.addItem(input, { flex: '1 1 auto' });
|
container.addItem(component, { flex: '1 1 auto' });
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,7 +236,7 @@ export abstract class DialogBase<DialogResult> {
|
|||||||
return table;
|
return table;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected addButtonsForTable(table: azdata.TableComponent, addButtonAriaLabel: string, removeButtonAriaLabel: string, addHandler: () => Promise<void>, removeHandler: () => Promise<void>): azdata.FlexContainer {
|
protected addButtonsForTable(table: azdata.TableComponent, addButtonAriaLabel: string, removeButtonAriaLabel: string, addHandler: (button: azdata.ButtonComponent) => Promise<void>, removeHandler: (button: azdata.ButtonComponent) => Promise<void>): azdata.FlexContainer {
|
||||||
let addButton: azdata.ButtonComponent;
|
let addButton: azdata.ButtonComponent;
|
||||||
let removeButton: azdata.ButtonComponent;
|
let removeButton: azdata.ButtonComponent;
|
||||||
const updateButtons = () => {
|
const updateButtons = () => {
|
||||||
@@ -244,11 +244,11 @@ export abstract class DialogBase<DialogResult> {
|
|||||||
removeButton.enabled = table.selectedRows?.length === 1 && table.selectedRows[0] !== -1 && table.selectedRows[0] < table.data.length;
|
removeButton.enabled = table.selectedRows?.length === 1 && table.selectedRows[0] !== -1 && table.selectedRows[0] < table.data.length;
|
||||||
}
|
}
|
||||||
addButton = this.createButton(uiLoc.AddText, addButtonAriaLabel, async () => {
|
addButton = this.createButton(uiLoc.AddText, addButtonAriaLabel, async () => {
|
||||||
await addHandler();
|
await addHandler(addButton);
|
||||||
updateButtons();
|
updateButtons();
|
||||||
});
|
});
|
||||||
removeButton = this.createButton(uiLoc.RemoveText, removeButtonAriaLabel, async () => {
|
removeButton = this.createButton(uiLoc.RemoveText, removeButtonAriaLabel, async () => {
|
||||||
await removeHandler();
|
await removeHandler(removeButton);
|
||||||
if (table.selectedRows.length === 1 && table.selectedRows[0] >= table.data.length) {
|
if (table.selectedRows.length === 1 && table.selectedRows[0] >= table.data.length) {
|
||||||
table.selectedRows = [table.data.length - 1];
|
table.selectedRows = [table.data.length - 1];
|
||||||
}
|
}
|
||||||
@@ -308,6 +308,20 @@ export abstract class DialogBase<DialogResult> {
|
|||||||
}).withItems(items, { flex: '0 0 auto' }).component();
|
}).withItems(items, { flex: '0 0 auto' }).component();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected createRadioButton(label: string, groupName: string, checked: boolean, handler: (checked: boolean) => Promise<void>): azdata.RadioButtonComponent {
|
||||||
|
const radio = this.modelView.modelBuilder.radioButton().withProps({
|
||||||
|
label: label,
|
||||||
|
name: groupName,
|
||||||
|
checked: checked
|
||||||
|
}).component();
|
||||||
|
this.disposables.push(radio.onDidChangeCheckedState(async checked => {
|
||||||
|
await handler(checked);
|
||||||
|
this.onFormFieldChange();
|
||||||
|
await this.runValidation(false);
|
||||||
|
}));
|
||||||
|
return radio;
|
||||||
|
}
|
||||||
|
|
||||||
protected removeItem(container: azdata.DivContainer | azdata.FlexContainer, item: azdata.Component): void {
|
protected removeItem(container: azdata.DivContainer | azdata.FlexContainer, item: azdata.Component): void {
|
||||||
if (container.items.indexOf(item) !== -1) {
|
if (container.items.indexOf(item) !== -1) {
|
||||||
container.removeItem(item);
|
container.removeItem(item);
|
||||||
@@ -324,7 +338,7 @@ export abstract class DialogBase<DialogResult> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected onLoadingStatusChanged(isLoading: boolean): void {
|
protected updateLoadingStatus(isLoading: boolean): void {
|
||||||
if (this._loadingComponent) {
|
if (this._loadingComponent) {
|
||||||
this._loadingComponent.loading = isLoading;
|
this._loadingComponent.loading = isLoading;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,8 +68,8 @@ export abstract class ScriptableDialogBase<OptionsType extends ScriptableDialogO
|
|||||||
await this.initializeUI();
|
await this.initializeUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override onLoadingStatusChanged(isLoading: boolean): void {
|
protected override updateLoadingStatus(isLoading: boolean): void {
|
||||||
super.onLoadingStatusChanged(isLoading);
|
super.updateLoadingStatus(isLoading);
|
||||||
this._helpButton.enabled = !isLoading;
|
this._helpButton.enabled = !isLoading;
|
||||||
this.dialogObject.okButton.enabled = this._scriptButton.enabled = isLoading ? false : this.isDirty;
|
this.dialogObject.okButton.enabled = this._scriptButton.enabled = isLoading ? false : this.isDirty;
|
||||||
}
|
}
|
||||||
@@ -80,7 +80,7 @@ export abstract class ScriptableDialogBase<OptionsType extends ScriptableDialogO
|
|||||||
protected abstract generateScript(): Promise<string>;
|
protected abstract generateScript(): Promise<string>;
|
||||||
|
|
||||||
private async onScriptButtonClick(): Promise<void> {
|
private async onScriptButtonClick(): Promise<void> {
|
||||||
this.onLoadingStatusChanged(true);
|
this.updateLoadingStatus(true);
|
||||||
try {
|
try {
|
||||||
const isValid = await this.runValidation();
|
const isValid = await this.runValidation();
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
@@ -104,7 +104,7 @@ export abstract class ScriptableDialogBase<OptionsType extends ScriptableDialogO
|
|||||||
level: azdata.window.MessageLevel.Error
|
level: azdata.window.MessageLevel.Error
|
||||||
};
|
};
|
||||||
} finally {
|
} finally {
|
||||||
this.onLoadingStatusChanged(false);
|
this.updateLoadingStatus(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,9 +22,10 @@ import { ILogService } from 'vs/platform/log/common/log';
|
|||||||
selector: 'modelview-groupContainer',
|
selector: 'modelview-groupContainer',
|
||||||
template: `
|
template: `
|
||||||
<div *ngIf="hasHeader()" [class]="getHeaderClass()" (click)="changeState()" (keydown)="onKeyDown($event)" [tabindex]="isCollapsible()? 0 : -1" [attr.role]="isCollapsible() ? 'button' : null" [attr.aria-expanded]="isCollapsible() ? !collapsed : null">
|
<div *ngIf="hasHeader()" [class]="getHeaderClass()" (click)="changeState()" (keydown)="onKeyDown($event)" [tabindex]="isCollapsible()? 0 : -1" [attr.role]="isCollapsible() ? 'button' : null" [attr.aria-expanded]="isCollapsible() ? !collapsed : null">
|
||||||
{{_containerLayout.header}}
|
{{header}}
|
||||||
</div>
|
</div>
|
||||||
<!-- This extra div is needed so that the expanded state of the header is updated correctly. See https://github.com/microsoft/azuredatastudio/pull/16499 for more details -->
|
<!-- This extra div is needed so that the expanded state of the header is updated correctly. See https://github.com/microsoft/azuredatastudio/pull/16499 for more details -->
|
||||||
|
<fieldset [attr.aria-label]="header" class="modelview-group-fieldset">
|
||||||
<div>
|
<div>
|
||||||
<div #container *ngIf="items" class="modelview-group-container" [ngStyle]="CSSStyles">
|
<div #container *ngIf="items" class="modelview-group-container" [ngStyle]="CSSStyles">
|
||||||
<ng-container *ngFor="let item of items">
|
<ng-container *ngFor="let item of items">
|
||||||
@@ -37,6 +38,7 @@ import { ILogService } from 'vs/platform/log/common/log';
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</fieldset>
|
||||||
`
|
`
|
||||||
})
|
})
|
||||||
export default class GroupContainer extends ContainerBase<GroupLayout, GroupContainerProperties> implements IComponent, OnDestroy, AfterViewInit {
|
export default class GroupContainer extends ContainerBase<GroupLayout, GroupContainerProperties> implements IComponent, OnDestroy, AfterViewInit {
|
||||||
@@ -95,6 +97,10 @@ export default class GroupContainer extends ContainerBase<GroupLayout, GroupCont
|
|||||||
return this.getPropertyOrDefault<boolean>((props) => props.collapsed, false);
|
return this.getPropertyOrDefault<boolean>((props) => props.collapsed, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get header(): string {
|
||||||
|
return this._containerLayout?.header;
|
||||||
|
}
|
||||||
|
|
||||||
private hasHeader(): boolean {
|
private hasHeader(): boolean {
|
||||||
return this._containerLayout && !!this._containerLayout.header;
|
return this._containerLayout && !!this._containerLayout.header;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,13 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modelview-group-fieldset {
|
||||||
|
border: none;
|
||||||
|
margin-inline: 0px;
|
||||||
|
padding-inline: 0px;
|
||||||
|
padding-block: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
.modelview-group-row {
|
.modelview-group-row {
|
||||||
display: table-row;
|
display: table-row;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user