mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-05 01:25:38 -05:00
Adding database properties general tab with read-only fields (#23318)
* Initial commit for adding a basic general tab for the database properties * Refactoring for dialog inputs * removed properties nodeType and using database node and additional cleanup, diabling the functionality. * Changes according to STS data fetch * Reuse database Dialog * Undo contract file change * more refactoring * fetched scrollbar fix into this PR * Tabbed panel is being used for horizontal tabs * stying fix for general tab button * final commit for today :) * Updates according to STS changes * missed updates * Refactored updates * moved options as discussed and added collapsible sections... need to fix scroll bar * Fixing the horizontal scroll bar of tabbedpanel
This commit is contained in:
committed by
GitHub
parent
b15217be43
commit
c8ed14f4a3
@@ -522,6 +522,11 @@
|
||||
"group": "0_query@1",
|
||||
"isDefault": true
|
||||
},
|
||||
{
|
||||
"command": "mssql.objectProperties",
|
||||
"when": "connectionProvider == MSSQL && serverInfo && !isCloud && nodeType && nodeType =~ /^(Database|Server)$/ && mssql:engineedition != 11 && isDevelopment",
|
||||
"group": "0_query@1"
|
||||
},
|
||||
{
|
||||
"command": "mssql.deleteObject",
|
||||
"when": "connectionProvider == MSSQL && nodeType =~ /^(ServerLevelLogin|User|ServerLevelServerRole|ApplicationRole|DatabaseRole|Database)$/ && config.workbench.enablePreviewFeatures",
|
||||
|
||||
@@ -106,7 +106,7 @@ async function handleObjectPropertiesDialogCommand(context: azdata.ObjectExplore
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const parentUrn = await getParentUrn(context);
|
||||
const parentUrn = context.nodeInfo ? await getParentUrn(context) : undefined;
|
||||
const options: ObjectManagementDialogOptions = {
|
||||
connectionUri: connectionUri,
|
||||
isNewObject: false,
|
||||
|
||||
@@ -28,6 +28,7 @@ export const AlterApplicationRoleDocUrl = 'https://learn.microsoft.com/sql/t-sql
|
||||
export const CreateDatabaseRoleDocUrl = 'https://learn.microsoft.com/sql/t-sql/statements/create-role-transact-sql';
|
||||
export const AlterDatabaseRoleDocUrl = 'https://learn.microsoft.com/sql/t-sql/statements/alter-role-transact-sql';
|
||||
export const CreateDatabaseDocUrl = 'https://learn.microsoft.com/sql/t-sql/statements/create-database-transact-sql';
|
||||
export const DatabasePropertiesDocUrl = 'https://learn.microsoft.com/sql/relational-databases/databases/database-properties-general-page';
|
||||
|
||||
export const enum TelemetryActions {
|
||||
CreateObject = 'CreateObject',
|
||||
|
||||
@@ -430,7 +430,15 @@ export interface Database extends ObjectManagement.SqlObject {
|
||||
recoveryModel?: string;
|
||||
compatibilityLevel?: string;
|
||||
containmentType?: string;
|
||||
|
||||
dateCreated?: string;
|
||||
lastDatabaseBackup?: string;
|
||||
lastDatabaseLogBackup?: string;
|
||||
memoryAllocatedToMemoryOptimizedObjectsInMb?: number;
|
||||
memoryUsedByMemoryOptimizedObjectsInMb?: number;
|
||||
numberOfUsers?: number;
|
||||
sizeInMb?: number;
|
||||
spaceAvailableInMb?: number;
|
||||
status?: string;
|
||||
azureBackupRedundancyLevel?: string;
|
||||
azureServiceLevelObjective?: string;
|
||||
azureEdition?: string;
|
||||
|
||||
@@ -222,6 +222,22 @@ export const ObjectSelectionMethodDialog_AllObjectsOfTypes = localize('objectMan
|
||||
export const ObjectSelectionMethodDialog_AllObjectsOfSchema = localize('objectManagement.ObjectSelectionMethodDialog_AllObjectsOfSchema', "All objects belonging to a schema");
|
||||
export const ObjectSelectionMethodDialog_SelectSchemaDropdownLabel = localize('objectManagement.ObjectSelectionMethodDialog_SelectSchemaDropdownLabel', "Schema");
|
||||
|
||||
//Database properties Dialog
|
||||
export const LastDatabaseBackupText = localize('objectManagement.lastDatabaseBackup', "Last Database Backup");
|
||||
export const LastDatabaseLogBackupText = localize('objectManagement.lastDatabaseLogBackup', "Last Database Log Backup");
|
||||
export const BackupSectionHeader = localize('objectManagement.databaseProperties.backupSectionHeader', "Backup");
|
||||
export const DatabaseSectionHeader = localize('objectManagement.databaseProperties.databaseSectionHeader', "Database");
|
||||
export const NamePropertyText = localize('objectManagement.databaseProperties.name', "Name");
|
||||
export const StatusText = localize('objectManagement.databaseProperties.status', "Status");
|
||||
export const OwnerPropertyText = localize('objectManagement.databaseProperties.owner', "Owner");
|
||||
export const DateCreatedText = localize('objectManagement.databaseProperties.dateCreated', "Date Created");
|
||||
export const SizeText = localize('objectManagement.databaseProperties.size', "Size");
|
||||
export const SpaceAvailableText = localize('objectManagement.databaseProperties.spaceAvailable', "Space Available");
|
||||
export const NumberOfUsersText = localize('objectManagement.databaseProperties.numberOfUsers', "Number of Users");
|
||||
export const MemoryAllocatedText = localize('objectManagement.databaseProperties.memoryAllocated', "Memory Allocated To Memory Optimized Objects");
|
||||
export const MemoryUsedText = localize('objectManagement.databaseProperties.memoryUsed', "Memory Used By Memory Optimized Objects");
|
||||
export const StringValueInMB = (value: string) => localize('objectManagement.databaseProperties.mbUnitText', "{0} MB", value);
|
||||
|
||||
// Util functions
|
||||
export function getNodeTypeDisplayName(type: string, inTitle: boolean = false): string {
|
||||
switch (type) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { ApplicationRoleViewInfo, AuthenticationType, DatabaseRoleViewInfo, LoginViewInfo, SecurablePermissions, SecurableTypeMetadata, ServerRoleViewInfo, User, UserType, UserViewInfo } from './interfaces';
|
||||
import { ApplicationRoleViewInfo, AuthenticationType, DatabaseRoleViewInfo, DatabaseViewInfo, LoginViewInfo, SecurablePermissions, SecurableTypeMetadata, ServerRoleViewInfo, User, UserType, UserViewInfo } from './interfaces';
|
||||
import * as Utils from '../utils';
|
||||
import * as constants from '../constants';
|
||||
import * as contracts from '../contracts';
|
||||
@@ -194,6 +194,8 @@ export class TestObjectManagementService implements IObjectManagementService {
|
||||
obj = this.getApplicationRoleView(isNewObject, objectUrn);
|
||||
} else if (objectType === ObjectManagement.NodeType.DatabaseRole) {
|
||||
obj = this.getDatabaseRoleView(isNewObject, objectUrn);
|
||||
} else if (objectType === ObjectManagement.NodeType.Database) {
|
||||
obj = this.getDatabaseView(isNewObject, objectUrn);
|
||||
} else if (objectType === ObjectManagement.NodeType.ServerLevelLogin) {
|
||||
obj = this.getLoginView(isNewObject, objectUrn);
|
||||
} else if (objectType === ObjectManagement.NodeType.ServerLevelServerRole) {
|
||||
@@ -432,6 +434,38 @@ export class TestObjectManagementService implements IObjectManagementService {
|
||||
};
|
||||
}
|
||||
|
||||
private getDatabaseView(isNewObject: boolean, name: string): DatabaseViewInfo {
|
||||
return isNewObject ? <DatabaseViewInfo>{
|
||||
objectInfo: {
|
||||
name: 'New Database Name',
|
||||
owner: '',
|
||||
collationName: '',
|
||||
compatibilityLevel: '',
|
||||
containmentType: '',
|
||||
recoveryModel: '',
|
||||
azureEdition: '',
|
||||
azureMaxSize: '',
|
||||
azureBackupRedundancyLevel: '',
|
||||
azureServiceLevelObjective: ''
|
||||
}
|
||||
} : <DatabaseViewInfo>{
|
||||
objectInfo: {
|
||||
name: 'Database Properties1',
|
||||
collationName: 'Latin1_General_100_CI_AS_KS_WS',
|
||||
dateCreated: '5/31/2023 8:05:55 AM',
|
||||
lastDatabaseBackup: 'None',
|
||||
lastDatabaseLogBackup: 'None',
|
||||
memoryAllocatedToMemoryOptimizedObjectsInMb: 0,
|
||||
memoryUsedByMemoryOptimizedObjectsInMb: 0,
|
||||
numberOfUsers: 5,
|
||||
owner: 'databaseProperties 1',
|
||||
sizeInMb: 16.00,
|
||||
spaceAvailableInMb: 1.15,
|
||||
status: 'Normal'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private delayAndResolve(obj?: any): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
|
||||
@@ -7,37 +7,84 @@ import * as azdata from 'azdata';
|
||||
import { ObjectManagementDialogBase, ObjectManagementDialogOptions } from './objectManagementDialogBase';
|
||||
import { IObjectManagementService } from 'mssql';
|
||||
import * as localizedConstants from '../localizedConstants';
|
||||
import { CreateDatabaseDocUrl } from '../constants';
|
||||
import { CreateDatabaseDocUrl, DatabasePropertiesDocUrl } from '../constants';
|
||||
import { Database, DatabaseViewInfo } from '../interfaces';
|
||||
import { convertNumToTwoDecimalStringinMB } from '../utils';
|
||||
|
||||
export class DatabaseDialog extends ObjectManagementDialogBase<Database, DatabaseViewInfo> {
|
||||
private _nameInput: azdata.InputBoxComponent;
|
||||
// Database Properties tabs
|
||||
private generalTab: azdata.Tab;
|
||||
|
||||
//Database properties options
|
||||
private nameInput: azdata.InputBoxComponent;
|
||||
private backupSection: azdata.GroupContainer;
|
||||
private lastDatabaseBackupInput: azdata.InputBoxComponent;
|
||||
private lastDatabaseLogBackupInput: azdata.InputBoxComponent;
|
||||
private databaseSection: azdata.GroupContainer;
|
||||
private statusInput: azdata.InputBoxComponent;
|
||||
private ownerInput: azdata.InputBoxComponent;
|
||||
private dateCreatedInput: azdata.InputBoxComponent;
|
||||
private sizeInput: azdata.InputBoxComponent;
|
||||
private spaceAvailabeInput: azdata.InputBoxComponent;
|
||||
private numberOfUsersInput: azdata.InputBoxComponent;
|
||||
private memoryAllocatedInput: azdata.InputBoxComponent;
|
||||
private memoryUsedInput: azdata.InputBoxComponent;
|
||||
private collationInput: azdata.InputBoxComponent;
|
||||
|
||||
constructor(objectManagementService: IObjectManagementService, options: ObjectManagementDialogOptions) {
|
||||
super(objectManagementService, options);
|
||||
}
|
||||
|
||||
protected override get helpUrl(): string {
|
||||
return CreateDatabaseDocUrl;
|
||||
return this.options.isNewObject ? CreateDatabaseDocUrl : DatabasePropertiesDocUrl;
|
||||
}
|
||||
|
||||
protected async initializeUI(): Promise<void> {
|
||||
let components = [];
|
||||
components.push(this.initializeGeneralSection());
|
||||
components.push(this.initializeOptionsSection());
|
||||
if (this.viewInfo.isAzureDB) {
|
||||
components.push(this.initializeConfigureSLOSection());
|
||||
if (this.options.isNewObject) {
|
||||
let components = [];
|
||||
components.push(this.initializeGeneralSection());
|
||||
components.push(this.initializeOptionsSection());
|
||||
if (this.viewInfo.isAzureDB) {
|
||||
components.push(this.initializeConfigureSLOSection());
|
||||
}
|
||||
this.formContainer.addItems(components);
|
||||
} else {
|
||||
// Initilaize general Tab sections
|
||||
this.initializeBackupSection();
|
||||
this.initializeDatabaseSection();
|
||||
|
||||
// Initilaize general Tab
|
||||
this.generalTab = {
|
||||
title: localizedConstants.GeneralSectionHeader,
|
||||
id: 'generalId',
|
||||
content: this.createGroup('', [
|
||||
this.databaseSection,
|
||||
this.backupSection
|
||||
], false)
|
||||
};
|
||||
|
||||
// Initilaize tab group with tabbed panel
|
||||
const propertiesTabGroup = { title: '', tabs: [this.generalTab] };
|
||||
const propertiesTabbedPannel = this.modelView.modelBuilder.tabbedPanel()
|
||||
.withTabs([propertiesTabGroup])
|
||||
.withProps({
|
||||
CSSStyles: {
|
||||
'margin': '-10px 0px 0px -10px'
|
||||
}
|
||||
})
|
||||
.component();
|
||||
this.formContainer.addItem(propertiesTabbedPannel);
|
||||
}
|
||||
this.formContainer.addItems(components);
|
||||
}
|
||||
|
||||
//#region Create Database
|
||||
private initializeGeneralSection(): azdata.GroupContainer {
|
||||
let containers: azdata.Component[] = [];
|
||||
this._nameInput = this.createInputBox(localizedConstants.NameText, async () => {
|
||||
this.objectInfo.name = this._nameInput.value;
|
||||
this.nameInput = this.createInputBox(localizedConstants.NameText, async () => {
|
||||
this.objectInfo.name = this.nameInput.value;
|
||||
await this.runValidation(false);
|
||||
});
|
||||
containers.push(this.createLabelInputContainer(localizedConstants.NameText, this._nameInput));
|
||||
containers.push(this.createLabelInputContainer(localizedConstants.NameText, this.nameInput));
|
||||
|
||||
if (this.viewInfo.loginNames?.length > 0) {
|
||||
this.objectInfo.owner = this.viewInfo.loginNames[0];
|
||||
@@ -86,6 +133,67 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
|
||||
|
||||
return this.createGroup(localizedConstants.OptionsSectionHeader, containers, true, true);
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region Database Properties - General Tab
|
||||
private initializeBackupSection(): void {
|
||||
this.lastDatabaseBackupInput = this.createInputBox(localizedConstants.LastDatabaseBackupText, async () => { }, this.objectInfo.lastDatabaseBackup, this.options.isNewObject);
|
||||
const lastDatabaseBackupContainer = this.createLabelInputContainer(localizedConstants.LastDatabaseBackupText, this.lastDatabaseBackupInput);
|
||||
|
||||
this.lastDatabaseLogBackupInput = this.createInputBox(localizedConstants.LastDatabaseLogBackupText, async () => { }, this.objectInfo.lastDatabaseLogBackup, this.options.isNewObject);
|
||||
const lastDatabaseLogBackupContainer = this.createLabelInputContainer(localizedConstants.LastDatabaseLogBackupText, this.lastDatabaseLogBackupInput);
|
||||
|
||||
this.backupSection = this.createGroup(localizedConstants.BackupSectionHeader, [
|
||||
lastDatabaseBackupContainer,
|
||||
lastDatabaseLogBackupContainer
|
||||
], true);
|
||||
}
|
||||
|
||||
private initializeDatabaseSection(): void {
|
||||
this.nameInput = this.createInputBox(localizedConstants.NamePropertyText, async () => { }, this.objectInfo.name, this.options.isNewObject);
|
||||
const nameContainer = this.createLabelInputContainer(localizedConstants.NamePropertyText, this.nameInput);
|
||||
|
||||
this.statusInput = this.createInputBox(localizedConstants.StatusText, async () => { }, this.objectInfo.status, this.options.isNewObject);
|
||||
const statusContainer = this.createLabelInputContainer(localizedConstants.StatusText, this.statusInput);
|
||||
|
||||
this.ownerInput = this.createInputBox(localizedConstants.OwnerPropertyText, async () => { }, this.objectInfo.owner, this.options.isNewObject);
|
||||
const ownerContainer = this.createLabelInputContainer(localizedConstants.OwnerPropertyText, this.ownerInput);
|
||||
|
||||
this.dateCreatedInput = this.createInputBox(localizedConstants.DateCreatedText, async () => { }, this.objectInfo.dateCreated, this.options.isNewObject);
|
||||
const dateCreatedContainer = this.createLabelInputContainer(localizedConstants.DateCreatedText, this.dateCreatedInput);
|
||||
|
||||
this.sizeInput = this.createInputBox(localizedConstants.SizeText, async () => { }, convertNumToTwoDecimalStringinMB(this.objectInfo.sizeInMb), this.options.isNewObject);
|
||||
const sizeContainer = this.createLabelInputContainer(localizedConstants.SizeText, this.sizeInput);
|
||||
|
||||
this.spaceAvailabeInput = this.createInputBox(localizedConstants.SpaceAvailableText, async () => { }, convertNumToTwoDecimalStringinMB(this.objectInfo.spaceAvailableInMb), this.options.isNewObject);
|
||||
const spaceAvailabeContainer = this.createLabelInputContainer(localizedConstants.SpaceAvailableText, this.spaceAvailabeInput);
|
||||
|
||||
this.numberOfUsersInput = this.createInputBox(localizedConstants.NumberOfUsersText, async () => { }, this.objectInfo.numberOfUsers.toString(), this.options.isNewObject);
|
||||
const numberOfUsersContainer = this.createLabelInputContainer(localizedConstants.NumberOfUsersText, this.numberOfUsersInput);
|
||||
|
||||
this.memoryAllocatedInput = this.createInputBox(localizedConstants.MemoryAllocatedText, async () => { }, convertNumToTwoDecimalStringinMB(this.objectInfo.memoryAllocatedToMemoryOptimizedObjectsInMb), this.options.isNewObject);
|
||||
const memoryAllocatedContainer = this.createLabelInputContainer(localizedConstants.MemoryAllocatedText, this.memoryAllocatedInput);
|
||||
|
||||
this.memoryUsedInput = this.createInputBox(localizedConstants.MemoryUsedText, async () => { }, convertNumToTwoDecimalStringinMB(this.objectInfo.memoryUsedByMemoryOptimizedObjectsInMb), this.options.isNewObject);
|
||||
const memoryUsedContainer = this.createLabelInputContainer(localizedConstants.MemoryUsedText, this.memoryUsedInput);
|
||||
|
||||
this.collationInput = this.createInputBox(localizedConstants.CollationText, async () => { }, this.objectInfo.collationName, this.options.isNewObject);
|
||||
const collationContainer = this.createLabelInputContainer(localizedConstants.CollationText, this.collationInput);
|
||||
|
||||
this.databaseSection = this.createGroup(localizedConstants.DatabaseSectionHeader, [
|
||||
nameContainer,
|
||||
statusContainer,
|
||||
ownerContainer,
|
||||
collationContainer,
|
||||
dateCreatedContainer,
|
||||
sizeContainer,
|
||||
spaceAvailabeContainer,
|
||||
numberOfUsersContainer,
|
||||
memoryAllocatedContainer,
|
||||
memoryUsedContainer
|
||||
], true);
|
||||
}
|
||||
//#endregion
|
||||
|
||||
private initializeConfigureSLOSection(): azdata.GroupContainer {
|
||||
let containers: azdata.Component[] = [];
|
||||
|
||||
@@ -42,3 +42,8 @@ export function isValidSQLPassword(password: string, userName: string = 'sa'): b
|
||||
const hasNonAlphas = /\W/.test(password) ? 1 : 0;
|
||||
return !containsUserName && password.length >= 8 && password.length <= 128 && (hasUpperCase + hasLowerCase + hasNumbers + hasNonAlphas >= 3);
|
||||
}
|
||||
|
||||
// Converts number to two decimal placed string
|
||||
export function convertNumToTwoDecimalStringinMB(value: number): string {
|
||||
return localizedConstants.StringValueInMB(value?.toFixed(2));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user