name: str
permission: str
icon: str
- routerLink: str # redirect to...
+ routerLink: str = '' # redirect to...
+ click: str = ''
class TableComponent(SerializableClass):
class Icon(Enum):
add = 'fa fa-plus'
+ destroy = 'fa fa-times'
class Validator(Enum):
self.permissions = []
self.actions = []
self.forms = []
+ self.columnKey = ''
self.detail_columns = []
doc: EndpointDoc
+# pylint: disable=R0902
class CRUDEndpoint:
# for testing purposes
CRUDClass: Optional[RESTController] = None
CRUDClassMetadata: Optional[RESTController] = None
- # pylint: disable=R0902
def __init__(self, router: APIRouter, doc: APIDoc,
set_column: Optional[Dict[str, Dict[str, str]]] = None,
actions: Optional[List[TableAction]] = None,
permissions: Optional[List[str]] = None, forms: Optional[List[Form]] = None,
+ column_key: Optional[str] = None,
meta: CRUDMeta = CRUDMeta(), get_all: Optional[CRUDCollectionMethod] = None,
create: Optional[CRUDCollectionMethod] = None,
+ delete: Optional[CRUDCollectionMethod] = None,
detail_columns: Optional[List[str]] = None):
self.router = router
self.doc = doc
self.meta = meta
self.get_all = get_all
self.create = create
+ self.delete = delete
self.permissions = permissions if permissions is not None else []
+ self.column_key = column_key if column_key is not None else ''
self.detail_columns = detail_columns if detail_columns is not None else []
def __call__(self, cls: Any):
def create(self, *args, **kwargs):
return outer_self.create.func(self, *args, **kwargs) # type: ignore
+ if self.delete:
+ @self.delete.doc
+ @wraps(self.delete.func)
+ def delete(self, *args, **kwargs):
+ return outer_self.delete.func(self, *args, **kwargs) # type: ignore
+
cls.CRUDClass = CRUDClass
def create_meta_class(self, cls):
self.generate_actions()
self.generate_forms()
self.set_permissions()
+ self.set_column_key()
self.get_detail_columns()
return serialize(self.__class__.outer_self.meta)
def set_permissions(self):
if self.__class__.outer_self.permissions:
self.outer_self.meta.permissions.extend(self.__class__.outer_self.permissions)
+
+ def set_column_key(self):
+ if self.__class__.outer_self.column_key:
+ self.outer_self.meta.columnKey = self.__class__.outer_self.column_key
+
class_name = self.router.path.replace('/', '')
meta_class = type(f'{class_name}_CRUDClassMetadata',
(RESTController,),
'generate_actions': generate_actions,
'generate_forms': generate_forms,
'set_permissions': set_permissions,
+ 'set_column_key': set_column_key,
'get_detail_columns': get_detail_columns,
'outer_self': self,
})
raise DashboardException(msg, code=500)
return f"Successfully created user '{user_entity}'"
+ @staticmethod
+ def user_delete(_, user_entity: str):
+ """
+ Delete a ceph user and it's defined capabilities.
+ :param user_entity: Entity to dlelete
+ """
+ logger.debug("Sending command 'auth del' of entity '%s'", user_entity)
+ try:
+ CephService.send_command('mon', 'auth del', entity=user_entity)
+ except SendCommandError as ex:
+ msg = f'{ex} in command {ex.prefix}'
+ if ex.errno == -EINVAL:
+ raise DashboardException(msg, code=400)
+ raise DashboardException(msg, code=500)
+ return f"Successfully eleted user '{user_entity}'"
+
+
create_cap_container = ArrayHorizontalContainer('Capabilities', 'capabilities', fields=[
FormField('Entity', 'entity',
field_type=str),
set_column={"caps": {"cellTemplate": "badgeDict"}},
actions=[
TableAction(name='create', permission='create', icon=Icon.add.value,
- routerLink='/cluster/user/create')
+ routerLink='/cluster/user/create'),
+ TableAction(name='Delete', permission='delete', icon=Icon.destroy.value,
+ click='delete')
],
permissions=[Scope.CONFIG_OPT],
forms=[create_form],
+ column_key='entity',
get_all=CRUDCollectionMethod(
func=CephUserEndpoints.user_list,
doc=EndpointDoc("Get Ceph Users")
func=CephUserEndpoints.user_create,
doc=EndpointDoc("Create Ceph User")
),
+ delete=CRUDCollectionMethod(
+ func=CephUserEndpoints.user_delete,
+ doc=EndpointDoc("Delete Ceph User")
+ ),
meta=CRUDMeta()
)
class CephUser(NamedTuple):
import { NgbDropdownModule, NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap';
import { NgxDatatableModule } from '@swimlane/ngx-datatable';
import { NgxPipeFunctionModule } from 'ngx-pipe-function';
+import { ToastrModule } from 'ngx-toastr';
import { ComponentsModule } from '~/app/shared/components/components.module';
import { PipesModule } from '~/app/shared/pipes/pipes.module';
NgbTooltipModule,
RouterTestingModule,
NgxPipeFunctionModule,
- HttpClientTestingModule
+ HttpClientTestingModule,
+ ToastrModule.forRoot()
]
});
beforeEach(() => {
import { DataGatewayService } from '~/app/shared/services/data-gateway.service';
import { TimerService } from '~/app/shared/services/timer.service';
import { CdTableSelection } from '../../models/cd-table-selection';
+import { FinishedTask } from '../../models/finished-task';
import { Permission, Permissions } from '../../models/permissions';
import { AuthStorageService } from '../../services/auth-storage.service';
+import { TaskWrapperService } from '../../services/task-wrapper.service';
+import { ModalService } from '../../services/modal.service';
+import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
+import { CriticalConfirmationModalComponent } from '../../components/critical-confirmation-modal/critical-confirmation-modal.component';
@Component({
selector: 'cd-crud-table',
selection = new CdTableSelection();
expandedRow: any = null;
tabs = {};
+ resource: string;
+ modalRef: NgbModalRef;
constructor(
private authStorageService: AuthStorageService,
private timerService: TimerService,
private dataGatewayService: DataGatewayService,
- private activatedRoute: ActivatedRoute
+ private activatedRoute: ActivatedRoute,
+ private taskWrapper: TaskWrapperService,
+ private modalService: ModalService
) {
this.permissions = this.authStorageService.getPermissions();
}
.subscribe((response: CrudMetadata) => this.processMeta(response));
this.data$ = this.timerService.get(() => this.dataGatewayService.list(resource));
});
+ this.activatedRoute.data.subscribe((data: any) => {
+ this.resource = data.resource;
+ });
}
processMeta(meta: CrudMetadata) {
return !col['isHidden'];
});
this.meta = meta;
+ for (let i = 0; i < this.meta.actions.length; i++) {
+ if (this.meta.actions[i].click.toString() !== '') {
+ this.meta.actions[i].click = this[this.meta.actions[i].click.toString()].bind(this);
+ }
+ }
+ }
+
+ delete() {
+ const selectedKey = this.selection.first()[this.meta.columnKey];
+ this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
+ itemDescription: $localize`${this.meta.columnKey}`,
+ itemNames: [selectedKey],
+ submitAction: () => {
+ this.taskWrapper
+ .wrapTaskAroundCall({
+ task: new FinishedTask('crud-component/id', selectedKey),
+ call: this.dataGatewayService.delete(this.resource, selectedKey)
+ })
+ .subscribe({
+ error: () => {
+ this.modalRef.close();
+ },
+ complete: () => {
+ this.modalRef.close();
+ }
+ });
+ }
+ });
}
updateSelection(selection: CdTableSelection) {
permissions: string[];
actions: CdTableAction[];
forms: any;
+ columnKey: string;
}
});
}
+ delete(dataPath: string, key: string): Observable<any> {
+ const { url, version } = this.getUrlAndVersion(dataPath);
+
+ return this.http.delete<any>(`${url}/${key}`, {
+ headers: { Accept: `application/vnd.ceph.api.v${version}+json` },
+ observe: 'response'
+ });
+ }
+
form(dataPath: string): Observable<JsonFormUISchema> {
const cacheable = this.getCacheable(dataPath, 'get');
if (this.cache[cacheable] === undefined) {
),
'crud-component': this.newTaskMessage(this.commonOperations.create, (metadata) =>
this.crudMessage(metadata)
+ ),
+ 'crud-component/id': this.newTaskMessage(this.commonOperations.delete, (id) =>
+ this.crudMessageId(id)
)
};
return $localize`${message}`;
}
+ crudMessageId(id: string) {
+ return $localize`${id}`;
+ }
+
_getTaskTitle(task: Task) {
if (task.name && task.name.startsWith('progress/')) {
// we don't fill the failure string because, at least for now, all
summary: Create Ceph User
tags:
- Cluster
+ /api/cluster/user/{user_entity}:
+ delete:
+ description: "\n Delete a ceph user and it's defined capabilities.\n\
+ \ :param user_entity: Entity to dlelete\n "
+ parameters:
+ - in: path
+ name: user_entity
+ required: true
+ schema:
+ type: string
+ responses:
+ '202':
+ content:
+ application/vnd.ceph.api.v1.0+json:
+ type: object
+ description: Operation is still executing. Please check the task queue.
+ '204':
+ content:
+ application/vnd.ceph.api.v1.0+json:
+ type: object
+ description: Resource deleted.
+ '400':
+ description: Operation exception. Please check the response body for details.
+ '401':
+ description: Unauthenticated access. Please login first.
+ '403':
+ description: Unauthorized access. Please check your permissions.
+ '500':
+ description: Unexpected error. Please check the response body for the stack
+ trace.
+ security:
+ - jwt: []
+ summary: Delete Ceph User
+ tags:
+ - Cluster
/api/cluster_conf:
get:
parameters: []