Files
azuredatastudio/extensions/mssql/src/objectManagement/objectManagementService.ts
Sai Avishkar Sreerama acfa93fbb8 Enabling options tab for database properties (#23448)
* 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

* initial updates

* need to fix the error!

* rror fixed and options are loading with sample values

* need to load real values for all felds and map values to dropdown options

* Dd loading correctly, need to add validation to recovery field and save options back

* refactor

* removing nullable property

* All changes done except recoveryTime validation

* Refactoring

* all values are saving as expected, isDirty flag is fixed. Todo: recovery validation and tests

* added general section for the options tab

* modifying pageVerify and userAccess to string array

* updates to general section of options tb

* disabling couple of general properties for MI

* Adding server edition conditions and toggling the UI options

* adding numeric validation to the recovey time

* Removing serveInfo logic and using the unsupported options approach from STS

* addressing comments and little code refactor

* changes with nullOrUndefined helper method

* replacing dropdowns with checkboxes

* adding unit test for helper method

* removed commented sample code and added comments
2023-07-06 18:22:20 -05:00

498 lines
16 KiB
TypeScript

/*---------------------------------------------------------------------------------------------
* 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, 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';
import { BaseService, ISqlOpsFeature, SqlOpsDataClient } from 'dataprotocol-client';
import { ObjectManagement, IObjectManagementService } from 'mssql';
import { ClientCapabilities } from 'vscode-languageclient';
import { AppContext } from '../appContext';
export class ObjectManagementService extends BaseService implements IObjectManagementService {
public static asFeature(context: AppContext): ISqlOpsFeature {
return class extends ObjectManagementService {
constructor(client: SqlOpsDataClient) {
super(context, client);
}
fillClientCapabilities(capabilities: ClientCapabilities): void {
Utils.ensure(capabilities, 'objectManagement')!.objectManagement = true;
}
initialize(): void {
}
};
}
async initializeView(contextId: string, objectType: ObjectManagement.NodeType, connectionUri: string, database: string, isNewObject: boolean, parentUrn: string, objectUrn: string): Promise<ObjectManagement.ObjectViewInfo<ObjectManagement.SqlObject>> {
const params: contracts.InitializeViewRequestParams = { connectionUri, contextId, isNewObject, objectType, database, parentUrn, objectUrn };
return this.runWithErrorHandling(contracts.InitializeViewRequest.type, params);
}
private constructor(context: AppContext, client: SqlOpsDataClient) {
super(client);
context.registerService(constants.ObjectManagementService, this);
}
async save(contextId: string, object: ObjectManagement.SqlObject): Promise<void> {
const params: contracts.SaveObjectRequestParams = { contextId, object };
return this.runWithErrorHandling(contracts.SaveObjectRequest.type, params);
}
async script(contextId: string, object: ObjectManagement.SqlObject): Promise<string> {
const params: contracts.ScriptObjectRequestParams = { contextId, object };
return this.runWithErrorHandling(contracts.ScriptObjectRequest.type, params);
}
async disposeView(contextId: string): Promise<void> {
const params: contracts.DisposeViewRequestParams = { contextId };
return this.runWithErrorHandling(contracts.DisposeViewRequest.type, params);
}
async rename(connectionUri: string, objectType: ObjectManagement.NodeType, objectUrn: string, newName: string): Promise<void> {
const params: contracts.RenameObjectRequestParams = { connectionUri, objectUrn, newName, objectType };
return this.runWithErrorHandling(contracts.RenameObjectRequest.type, params);
}
async drop(connectionUri: string, objectType: ObjectManagement.NodeType, objectUrn: string): Promise<void> {
const params: contracts.DropObjectRequestParams = { connectionUri, objectUrn, objectType };
return this.runWithErrorHandling(contracts.DropObjectRequest.type, params);
}
async search(contextId: string, objectTypes: ObjectManagement.NodeType[], searchText?: string, schema?: string): Promise<ObjectManagement.SearchResultItem[]> {
const params: contracts.SearchObjectRequestParams = { contextId, searchText, objectTypes, schema };
return this.runWithErrorHandling(contracts.SearchObjectRequest.type, params);
}
async detachDatabase(connectionUri: string, objectUrn: string, dropConnections: boolean, updateStatistics: boolean, generateScript: boolean): Promise<string> {
const params: contracts.DetachDatabaseRequestParams = { connectionUri, objectUrn, dropConnections, updateStatistics, generateScript };
return this.runWithErrorHandling(contracts.DetachDatabaseRequest.type, params);
}
}
const ServerLevelSecurableTypes: SecurableTypeMetadata[] = [
{
name: 'Server',
displayName: 'Server',
permissions: [{
name: 'CONNECT SQL',
displayName: 'CONNECT SQL'
}, {
name: 'VIEW ANY DATABASE',
displayName: 'VIEW ANY DATABASE'
}]
}, {
name: 'ServerRole',
displayName: 'Server Role',
permissions: [{
name: 'ALTER',
displayName: 'ALTER'
}, {
name: 'CONTROL',
displayName: 'CONTROL'
}, {
name: 'TAKE OWNERSHIP',
displayName: 'TAKE OWNERSHIP'
}]
}
];
const DatabaseLevelSecurableTypes: SecurableTypeMetadata[] = [
{
name: 'AggregateFunction',
displayName: 'Aggregate Function',
permissions: [{
name: 'EXECUTE',
displayName: 'EXECUTE'
}, {
name: 'ALTER',
displayName: 'ALTER'
}]
}, {
name: 'Table',
displayName: 'Table',
permissions: [{
name: 'SELECT',
displayName: 'SELECT'
}, {
name: 'ALTER',
displayName: 'ALTER'
}, {
name: 'CONTROL',
displayName: 'CONTROL'
}, {
name: 'TAKE OWNERSHIP',
displayName: 'TAKE OWNERSHIP'
}]
}, {
name: 'View',
displayName: 'View',
permissions: [{
name: 'ALTER',
displayName: 'ALTER'
}, {
name: 'CONTROL',
displayName: 'CONTROL'
}, {
name: 'TAKE OWNERSHIP',
displayName: 'TAKE OWNERSHIP'
}]
}
]
const ServerLevelPermissions: SecurablePermissions[] = [
{
name: 'Server',
type: 'Server',
permissions: [
{
permission: 'CONNECT SQL',
grant: true,
grantor: 'sa',
withGrant: undefined
}, {
permission: 'VIEW ANY DATABASE',
grant: false,
grantor: 'sa',
withGrant: undefined
}
],
effectivePermissions: ['CONNECT SQL', 'VIEW ANY DATABASE']
}
];
const DatabaseLevelPermissions: SecurablePermissions[] = [
{
name: 'table1',
type: 'Table',
schema: 'dbo',
permissions: [
{
permission: 'SELECT',
grant: true,
grantor: '',
withGrant: undefined
}
],
effectivePermissions: ['SELECT']
}, {
name: 'view1',
type: 'View',
schema: 'Sales',
permissions: [
{
permission: 'ALTER',
grant: true,
grantor: '',
withGrant: undefined
}
],
effectivePermissions: ['ALTER']
}
];
export class TestObjectManagementService implements IObjectManagementService {
initializeView(contextId: string, objectType: ObjectManagement.NodeType, connectionUri: string, database: string, isNewObject: boolean, parentUrn: string, objectUrn: string): Thenable<ObjectManagement.ObjectViewInfo<ObjectManagement.SqlObject>> {
let obj;
if (objectType === ObjectManagement.NodeType.ApplicationRole) {
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) {
obj = this.getServerRoleView(isNewObject, objectUrn);
} else if (objectType === ObjectManagement.NodeType.User) {
obj = this.getUserView(isNewObject, objectUrn);
}
else {
throw Error('Not implemented');
}
return this.delayAndResolve(obj);
}
save(contextId: string, object: ObjectManagement.SqlObject): Thenable<void> {
return this.delayAndResolve();
}
script(contextId: string, object: ObjectManagement.SqlObject): Thenable<string> {
return this.delayAndResolve('test script');
}
disposeView(contextId: string): Thenable<void> {
return this.delayAndResolve();
}
async rename(connectionUri: string, objectType: ObjectManagement.NodeType, objectUrn: string, newName: string): Promise<void> {
return this.delayAndResolve();
}
async drop(connectionUri: string, objectType: ObjectManagement.NodeType, objectUrn: string): Promise<void> {
return this.delayAndResolve();
}
async search(contextId: string, objectTypes: ObjectManagement.NodeType[], searchText?: string, schema?: string): Promise<ObjectManagement.SearchResultItem[]> {
const items: ObjectManagement.SearchResultItem[] = [];
objectTypes.forEach(type => {
items.push(...this.generateSearchResult(type, schema, 15));
});
return this.delayAndResolve(items);
}
async detachDatabase(connectionUri: string, objectUrn: string, dropConnections: boolean, updateStatistics: boolean, generateScript: boolean): Promise<string> {
return this.delayAndResolve('');
}
private generateSearchResult(objectType: ObjectManagement.NodeType, schema: string | undefined, count: number): ObjectManagement.SearchResultItem[] {
let items: ObjectManagement.SearchResultItem[] = [];
for (let i = 0; i < count; i++) {
items.push(<ObjectManagement.SearchResultItem>{ name: `${objectType} ${i}`, schema: schema, type: objectType });
}
return items;
}
private getLoginView(isNewObject: boolean, name: string): LoginViewInfo {
const serverRoles = ['sysadmin', 'public', 'bulkadmin', 'dbcreator', 'diskadmin', 'processadmin', 'securityadmin', 'serveradmin'];
const languages = ['<default>', 'English'];
const databases = ['master', 'db1', 'db2'];
let login: LoginViewInfo;
if (isNewObject) {
login = <LoginViewInfo>{
objectInfo: {
name: '',
authenticationType: AuthenticationType.Sql,
enforcePasswordPolicy: true,
enforcePasswordExpiration: true,
mustChangePassword: true,
defaultDatabase: 'master',
defaultLanguage: '<default>',
serverRoles: ['public', 'bulkadmin'],
connectPermission: true,
isEnabled: true,
isLockedOut: false,
securablePermissions: []
},
authenticationTypes: [AuthenticationType.Sql, AuthenticationType.Windows],
supportAdvancedOptions: true,
supportAdvancedPasswordOptions: true,
canEditLockedOutState: false,
languages: languages,
databases: databases,
serverRoles: serverRoles,
supportedSecurableTypes: ServerLevelSecurableTypes
};
} else {
login = <LoginViewInfo>{
objectInfo: {
name: name,
authenticationType: AuthenticationType.Sql,
enforcePasswordPolicy: true,
enforcePasswordExpiration: true,
mustChangePassword: true,
defaultDatabase: 'master',
defaultLanguage: '<default>',
serverRoles: ['public'],
connectPermission: true,
isEnabled: true,
isLockedOut: false,
password: '******************',
securablePermissions: ServerLevelPermissions
},
authenticationTypes: [AuthenticationType.Sql, AuthenticationType.Windows],
supportAdvancedOptions: true,
supportAdvancedPasswordOptions: true,
canEditLockedOutState: false,
languages: languages,
databases: databases,
serverRoles: serverRoles,
supportedSecurableTypes: ServerLevelSecurableTypes
};
}
return login;
}
private getUserView(isNewObject: boolean, name: string): UserViewInfo {
let viewInfo: UserViewInfo;
const languages = ['<default>', 'English'];
const schemas = ['dbo', 'sys', 'alanren'];
const logins = ['sa', 'alanren', 'alanren@microsoft.com'];
const databaseRoles = ['dbmanager', 'loginmanager', 'bulkadmin', 'sysadmin', 'tablemanager', 'viewmanager'];
if (isNewObject) {
viewInfo = {
objectInfo: <User>{
name: '',
type: UserType.LoginMapped,
defaultSchema: 'dbo',
defaultLanguage: '<default>',
authenticationType: AuthenticationType.Sql,
loginName: 'sa',
ownedSchemas: [],
databaseRoles: [],
password: '',
securablePermissions: []
},
languages: languages,
schemas: schemas,
logins: logins,
databaseRoles: databaseRoles,
userTypes: [
UserType.LoginMapped,
UserType.AADAuthentication,
UserType.SqlAuthentication,
UserType.NoLoginAccess
],
supportedSecurableTypes: DatabaseLevelSecurableTypes
};
} else {
viewInfo = {
objectInfo: <User>{
name: name,
type: UserType.LoginMapped,
defaultSchema: 'dbo',
defaultLanguage: '<default>',
loginName: 'sa',
ownedSchemas: ['dbo'],
databaseRoles: ['dbmanager', 'bulkadmin'],
securablePermissions: DatabaseLevelPermissions
},
languages: languages,
schemas: schemas,
logins: logins,
databaseRoles: databaseRoles,
userTypes: [
UserType.LoginMapped,
UserType.AADAuthentication,
UserType.SqlAuthentication,
UserType.NoLoginAccess
],
supportedSecurableTypes: DatabaseLevelSecurableTypes
};
}
return viewInfo;
}
private getServerRoleView(isNewObject: boolean, name: string): ServerRoleViewInfo {
return isNewObject ? <ServerRoleViewInfo>{
objectInfo: {
name: '',
members: [],
owner: '',
memberships: [],
securablePermissions: []
},
isFixedRole: false,
serverRoles: ['ServerLevelServerRole 1', 'ServerLevelServerRole 2', 'ServerLevelServerRole 3', 'ServerLevelServerRole 4'],
supportedSecurableTypes: ServerLevelSecurableTypes
} : <ServerRoleViewInfo>{
objectInfo: {
name: 'ServerLevelServerRole 1',
members: ['ServerLevelLogin 1', 'ServerLevelServerRole 2'],
owner: 'ServerLevelLogin 2',
memberships: ['ServerLevelServerRole 3', 'ServerLevelServerRole 4'],
securablePermissions: ServerLevelPermissions
},
isFixedRole: false,
serverRoles: ['ServerLevelServerRole 2', 'ServerLevelServerRole 3', 'ServerLevelServerRole 4'],
supportedSecurableTypes: ServerLevelSecurableTypes
};
}
private getApplicationRoleView(isNewObject: boolean, name: string): ApplicationRoleViewInfo {
return isNewObject ? <ApplicationRoleViewInfo>{
objectInfo: {
name: '',
defaultSchema: 'dbo',
ownedSchemas: [],
securablePermissions: []
},
schemas: ['dbo', 'sys', 'admin'],
supportedSecurableTypes: []
} : <ApplicationRoleViewInfo>{
objectInfo: {
name: 'app role1',
password: '******************',
defaultSchema: 'dbo',
ownedSchemas: ['dbo'],
securablePermissions: DatabaseLevelPermissions
},
schemas: ['dbo', 'sys', 'admin'],
supportedSecurableTypes: DatabaseLevelSecurableTypes
};
}
private getDatabaseRoleView(isNewObject: boolean, name: string): DatabaseRoleViewInfo {
return isNewObject ? <DatabaseRoleViewInfo>{
objectInfo: {
name: '',
owner: '',
members: [],
ownedSchemas: [],
securablePermissions: []
},
schemas: ['dbo', 'sys', 'admin'],
supportedSecurableTypes: DatabaseLevelSecurableTypes
} : <DatabaseRoleViewInfo>{
objectInfo: {
name: 'db role1',
owner: '',
members: [],
ownedSchemas: ['dbo'],
securablePermissions: DatabaseLevelPermissions
},
schemas: ['dbo', 'sys', 'admin'],
supportedSecurableTypes: DatabaseLevelSecurableTypes
};
}
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',
autoCreateIncrementalStatistics: false,
autoCreateStatistics: true,
autoShrink: false,
autoUpdateStatistics: true,
autoUpdateStatisticsAsynchronously: false,
isLedgerDatabase: false,
pageVerify: 'CHECKSUM',
targetRecoveryTimeInSec: 60,
databaseReadOnly: true,
encryptionEnabled: false,
restrictAccess: 'SINGLE_USER',
}
};
}
private delayAndResolve(obj?: any): Promise<any> {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(obj);
}, 1000);
});
}
}