Pull request #213: Bulk delete templates & dictionaries & other fixes

Merge in RED/ui from RED-1323 to master

* commit 'd2bd5e848429451af55da0d9b62a56b3168978c0':
  Bulk delete templates & dictionaries & other fixes
This commit is contained in:
Adina Teudan 2021-06-16 15:22:59 +02:00
commit a17ba51690
11 changed files with 324 additions and 48 deletions

View File

@ -7,7 +7,7 @@ To re-generate http rune swagger
YOu need swagger-codegen installed `brew install swagger-codegen`
```
BASE=https://dev-08.iqser.cloud/
BASE=https://dev-06.iqser.cloud/
URL="$BASE"redaction-gateway-v1/v2/api-docs?group=redaction-gateway-v1
rm -Rf /tmp/swagger
mkdir -p /tmp/swagger
@ -17,10 +17,8 @@ cd /tmp/swagger
## To Create a new Stack in rancher
Goto rancher.iqser.com: Select Cluster `Development`,
go to apps, click launch and select `Redaction` from the `dev` section.
Add a new name and a new namespace.
Select `answers-development.yaml` and add it to answers `Edit as yaml`.
Goto rancher.iqser.com: Select Cluster `Development`, go to apps, click launch and select `Redaction` from the `dev`
section. Add a new name and a new namespace. Select `answers-development.yaml` and add it to answers `Edit as yaml`.
For HTTPS / Cloudflare domain go to `workloads` -> `Loadbalancing` -> `select your stack`
Add cloudflare certificate and specify a hostname to use `timo-redaction-dev.iqser.cloud`

View File

@ -36,6 +36,18 @@
}}
</span>
<ng-container *ngIf="areSomeEntitiesSelected && !loading">
<redaction-circle-button
(action)="openDeleteDictionariesDialog($event)"
*ngIf="permissionsService.isAdmin()"
icon="red:trash"
tooltip="dictionary-listing.bulk.delete"
type="dark-bg"
></redaction-circle-button>
</ng-container>
<mat-spinner *ngIf="loading" diameter="15"></mat-spinner>
<div class="attributes-actions-container">
<redaction-input-with-action
[form]="searchForm"
@ -151,7 +163,7 @@
<div class="actions-container">
<div class="action-buttons">
<redaction-circle-button
(action)="openDeleteDictionaryDialog($event, dict)"
(action)="openDeleteDictionariesDialog($event, [dict.type])"
*ngIf="permissionsService.isAdmin()"
icon="red:trash"
tooltip="dictionary-listing.action.delete"

View File

@ -21,6 +21,7 @@ export class DictionaryListingScreenComponent
{
viewReady = false;
chartData: DoughnutChartConfig[] = [];
loading = false;
protected readonly _searchKey = 'label';
protected readonly _selectionKey = 'type';
protected readonly _sortKey = 'dictionary-listing';
@ -43,6 +44,22 @@ export class DictionaryListingScreenComponent
this._loadDictionaryData();
}
openDeleteDictionariesDialog($event?: MouseEvent, types = this.selectedEntitiesIds) {
$event?.stopPropagation();
this._dialogService.openDeleteDictionariesDialog(
$event,
types,
this._appStateService.activeDossierTemplateId,
async () => {
this.selectedEntitiesIds = [];
this.loading = true;
await this._appStateService.loadDictionaryData();
this._loadDictionaryData();
this.loading = false;
}
);
}
openAddEditDictionaryDialog($event?: MouseEvent, dict?: TypeValueWrapper) {
$event?.stopPropagation();
this._dialogService.openAddEditDictionaryDialog(
@ -57,18 +74,6 @@ export class DictionaryListingScreenComponent
);
}
openDeleteDictionaryDialog($event: any, dict: TypeValueWrapper) {
this._dialogService.openDeleteDictionaryDialog(
$event,
dict,
this._appStateService.activeDossierTemplateId,
async () => {
await this._appStateService.loadDictionaryData();
this._loadDictionaryData();
}
);
}
private _loadDictionaryData() {
const appStateDictionaryData =
this._appStateService.dictionaryData[this._appStateService.activeDossierTemplateId];

View File

@ -65,13 +65,19 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
}
openDeleteDictionaryDialog($event: any) {
this._dialogService.openDeleteDictionaryDialog(
this._dialogService.openDeleteDictionariesDialog(
$event,
this.dictionary,
[this.dictionary.type],
this.dictionary.dossierTemplateId,
async () => {
await this._appStateService.loadDictionaryData();
this._router.navigate(['..']);
this._router.navigate([
'/main',
'admin',
'dossier-templates',
this._appStateService.activeDossierTemplateId,
'dictionaries'
]);
}
);
}

View File

@ -34,6 +34,18 @@
}}
</span>
<ng-container *ngIf="areSomeEntitiesSelected && !loading">
<redaction-circle-button
(action)="openDeleteTemplatesDialog($event)"
*ngIf="permissionsService.isAdmin()"
icon="red:trash"
tooltip="dossier-templates-listing.bulk.delete"
type="dark-bg"
></redaction-circle-button>
</ng-container>
<mat-spinner *ngIf="loading" diameter="15"></mat-spinner>
<div class="actions flex-1">
<redaction-input-with-action
[form]="searchForm"

View File

@ -15,6 +15,7 @@ export class DossierTemplatesListingScreenComponent
extends BaseListingComponent<DossierTemplateModelWrapper>
implements OnInit
{
loading = false;
protected readonly _searchKey = 'name';
protected readonly _selectionKey = 'dossierTemplateId';
protected readonly _sortKey = 'dossier-templates-listing';
@ -33,6 +34,22 @@ export class DossierTemplatesListingScreenComponent
this.loadDossierTemplatesData();
}
openDeleteTemplatesDialog($event?: MouseEvent) {
$event?.stopPropagation();
this._dialogService.openBulkDeleteDossierTemplatesDialog(
$event,
this.selectedEntitiesIds,
async () => {
this.selectedEntitiesIds = [];
this.loading = true;
await this._appStateService.loadAllDossierTemplates();
await this._appStateService.loadDictionaryData();
this.loadDossierTemplatesData();
this.loading = false;
}
);
}
loadDossierTemplatesData() {
this._appStateService.reset();
this.allEntities = this._appStateService.dossierTemplates;

View File

@ -53,9 +53,9 @@ export class AdminDialogService {
private readonly _manualRedactionControllerService: ManualRedactionControllerService
) {}
openDeleteDictionaryDialog(
openDeleteDictionariesDialog(
$event: MouseEvent,
dictionary: TypeValueWrapper,
dictionaryTypes: string[],
dossierTemplateId: string,
cb?: Function
): MatDialogRef<ConfirmationDialogComponent> {
@ -64,7 +64,7 @@ export class AdminDialogService {
ref.afterClosed().subscribe(async result => {
if (result) {
await this._dictionaryControllerService
.deleteType(dictionary.type, dossierTemplateId)
.deleteTypes(dictionaryTypes, dossierTemplateId)
.toPromise();
if (cb) cb();
}
@ -82,7 +82,25 @@ export class AdminDialogService {
ref.afterClosed().subscribe(async result => {
if (result) {
await this._dossierTemplateControllerService
.getAllDossierTemplates(dossierTemplate.dossierTemplateId)
.deleteDossierTemplate(dossierTemplate.dossierTemplateId)
.toPromise();
if (cb) await cb();
}
});
return ref;
}
openBulkDeleteDossierTemplatesDialog(
$event: MouseEvent,
dossierTemplateIds: string[],
cb?: Function
): MatDialogRef<ConfirmationDialogComponent> {
$event.stopPropagation();
const ref = this._dialog.open(ConfirmationDialogComponent, dialogConfig);
ref.afterClosed().subscribe(async result => {
if (result) {
await this._dossierTemplateControllerService
.deleteDossierTemplates(dossierTemplateIds)
.toPromise();
if (cb) await cb();
}

View File

@ -1,4 +1,4 @@
import { Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
import { Component, ElementRef, Input, OnChanges, ViewChild } from '@angular/core';
import { TypeValueWrapper } from '@models/file/type-value.wrapper';
@Component({
@ -6,7 +6,7 @@ import { TypeValueWrapper } from '@models/file/type-value.wrapper';
templateUrl: './annotation-icon.component.html',
styleUrls: ['./annotation-icon.component.scss']
})
export class AnnotationIconComponent implements OnInit {
export class AnnotationIconComponent implements OnChanges {
@Input() color: string;
@Input() type: 'square' | 'rhombus' | 'circle' | 'hexagon' | 'none';
@Input() label: string;
@ -32,7 +32,7 @@ export class AnnotationIconComponent implements OnInit {
return this.color || this.dictType?.hexColor;
}
ngOnInit() {
ngOnChanges() {
this.icon.nativeElement.style.setProperty('--color', this.backgroundColor);
}
}

View File

@ -776,6 +776,9 @@
"delete": "Delete Dictionary",
"edit": "Edit Dictionary"
},
"bulk": {
"delete": "Delete Selected Dictionaries"
},
"case-sensitive": "Case Sensitive",
"add-new": "New Dictionary",
"stats": {
@ -808,6 +811,9 @@
"table-header": {
"title": "{{length}} dossier templates"
},
"bulk": {
"delete": "Delete Selected Dossier Templates"
},
"entries": "{{length}} entries",
"dictionaries": "{{length}} dictionaries",
"action": {

View File

@ -27,9 +27,9 @@ import { Configuration } from '../configuration';
@Injectable()
export class DictionaryControllerService {
protected basePath = '';
public defaultHeaders = new HttpHeaders();
public configuration = new Configuration();
protected basePath = '';
constructor(
protected httpClient: HttpClient,
@ -45,20 +45,6 @@ export class DictionaryControllerService {
}
}
/**
* @param consumes string[] mime-types
* @return true: consumes contains 'multipart/form-data', false: otherwise
*/
private canConsumeForm(consumes: string[]): boolean {
const form = 'multipart/form-data';
for (const consume of consumes) {
if (form === consume) {
return true;
}
}
return false;
}
/**
* Add dictionary entries with entry type.
* None
@ -79,6 +65,7 @@ export class DictionaryControllerService {
observe?: 'body',
reportProgress?: boolean
): Observable<any>;
public addEntry(
body: Array<string>,
dossierTemplateId: string,
@ -88,6 +75,7 @@ export class DictionaryControllerService {
observe?: 'response',
reportProgress?: boolean
): Observable<HttpResponse<any>>;
public addEntry(
body: Array<string>,
dossierTemplateId: string,
@ -97,6 +85,7 @@ export class DictionaryControllerService {
observe?: 'events',
reportProgress?: boolean
): Observable<HttpEvent<any>>;
public addEntry(
body: Array<string>,
dossierTemplateId: string,
@ -185,18 +174,21 @@ export class DictionaryControllerService {
observe?: 'body',
reportProgress?: boolean
): Observable<any>;
public addType(
body: TypeValue,
dossierId?: string,
observe?: 'response',
reportProgress?: boolean
): Observable<HttpResponse<any>>;
public addType(
body: TypeValue,
dossierId?: string,
observe?: 'events',
reportProgress?: boolean
): Observable<HttpEvent<any>>;
public addType(
body: TypeValue,
dossierId?: string,
@ -267,6 +259,7 @@ export class DictionaryControllerService {
observe?: 'body',
reportProgress?: boolean
): Observable<any>;
public deleteEntries(
body: Array<string>,
dossierTemplateId: string,
@ -275,6 +268,7 @@ export class DictionaryControllerService {
observe?: 'response',
reportProgress?: boolean
): Observable<HttpResponse<any>>;
public deleteEntries(
body: Array<string>,
dossierTemplateId: string,
@ -283,6 +277,7 @@ export class DictionaryControllerService {
observe?: 'events',
reportProgress?: boolean
): Observable<HttpEvent<any>>;
public deleteEntries(
body: Array<string>,
dossierTemplateId: string,
@ -375,6 +370,7 @@ export class DictionaryControllerService {
observe?: 'body',
reportProgress?: boolean
): Observable<any>;
public deleteEntry(
dossierTemplateId: string,
entry: string,
@ -383,6 +379,7 @@ export class DictionaryControllerService {
observe?: 'response',
reportProgress?: boolean
): Observable<HttpResponse<any>>;
public deleteEntry(
dossierTemplateId: string,
entry: string,
@ -391,6 +388,7 @@ export class DictionaryControllerService {
observe?: 'events',
reportProgress?: boolean
): Observable<HttpEvent<any>>;
public deleteEntry(
dossierTemplateId: string,
entry: string,
@ -544,6 +542,106 @@ export class DictionaryControllerService {
);
}
/**
* Deletes entry types
* None
* @param body types
* @param dossierTemplateId dossierTemplateId
* @param dossierId dossierId
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
*/
public deleteTypes(
body: Array<string>,
dossierTemplateId: string,
dossierId?: string,
observe?: 'body',
reportProgress?: boolean
): Observable<any>;
public deleteTypes(
body: Array<string>,
dossierTemplateId: string,
dossierId?: string,
observe?: 'response',
reportProgress?: boolean
): Observable<HttpResponse<any>>;
public deleteTypes(
body: Array<string>,
dossierTemplateId: string,
dossierId?: string,
observe?: 'events',
reportProgress?: boolean
): Observable<HttpEvent<any>>;
public deleteTypes(
body: Array<string>,
dossierTemplateId: string,
dossierId?: string,
observe: any = 'body',
reportProgress: boolean = false
): Observable<any> {
if (body === null || body === undefined) {
throw new Error(
'Required parameter body was null or undefined when calling deleteTypes.'
);
}
if (dossierTemplateId === null || dossierTemplateId === undefined) {
throw new Error(
'Required parameter dossierTemplateId was null or undefined when calling deleteTypes.'
);
}
let queryParameters = new HttpParams({ encoder: new CustomHttpUrlEncodingCodec() });
if (dossierId !== undefined && dossierId !== null) {
queryParameters = queryParameters.set('dossierId', <any>dossierId);
}
let headers = this.defaultHeaders;
// authentication (RED-OAUTH) required
if (this.configuration.accessToken) {
const accessToken =
typeof this.configuration.accessToken === 'function'
? this.configuration.accessToken()
: this.configuration.accessToken;
headers = headers.set('Authorization', 'Bearer ' + accessToken);
}
// to determine the Accept header
const httpHeaderAccepts: string[] = [];
const httpHeaderAcceptSelected: string | undefined =
this.configuration.selectHeaderAccept(httpHeaderAccepts);
if (httpHeaderAcceptSelected !== undefined) {
headers = headers.set('Accept', httpHeaderAcceptSelected);
}
// to determine the Content-Type header
const consumes: string[] = ['application/json'];
const httpContentTypeSelected: string | undefined =
this.configuration.selectHeaderContentType(consumes);
if (httpContentTypeSelected !== undefined) {
headers = headers.set('Content-Type', httpContentTypeSelected);
}
return this.httpClient.request<any>(
'post',
`${this.basePath}/dictionary/type/${encodeURIComponent(
String(dossierTemplateId)
)}/delete`,
{
body: body,
params: queryParameters,
withCredentials: this.configuration.withCredentials,
headers: headers,
observe: observe,
reportProgress: reportProgress
}
);
}
/**
* Returns file containing the the dictionary entries for given type..
*
@ -560,6 +658,7 @@ export class DictionaryControllerService {
observe?: 'body',
reportProgress?: boolean
): Observable<any>;
public downloadDictionaryFile(
dossierTemplateId: string,
type: string,
@ -567,6 +666,7 @@ export class DictionaryControllerService {
observe?: 'response',
reportProgress?: boolean
): Observable<HttpResponse<any>>;
public downloadDictionaryFile(
dossierTemplateId: string,
type: string,
@ -574,6 +674,7 @@ export class DictionaryControllerService {
observe?: 'events',
reportProgress?: boolean
): Observable<HttpEvent<any>>;
public downloadDictionaryFile(
dossierTemplateId: string,
type: string,
@ -646,18 +747,21 @@ export class DictionaryControllerService {
observe?: 'body',
reportProgress?: boolean
): Observable<TypeResponse>;
public getAllTypes(
dossierTemplateId: string,
dossierId?: string,
observe?: 'response',
reportProgress?: boolean
): Observable<HttpResponse<TypeResponse>>;
public getAllTypes(
dossierTemplateId: string,
dossierId?: string,
observe?: 'events',
reportProgress?: boolean
): Observable<HttpEvent<TypeResponse>>;
public getAllTypes(
dossierTemplateId: string,
dossierId?: string,
@ -719,16 +823,19 @@ export class DictionaryControllerService {
observe?: 'body',
reportProgress?: boolean
): Observable<Colors>;
public getColors(
dossierTemplateId: string,
observe?: 'response',
reportProgress?: boolean
): Observable<HttpResponse<Colors>>;
public getColors(
dossierTemplateId: string,
observe?: 'events',
reportProgress?: boolean
): Observable<HttpEvent<Colors>>;
public getColors(
dossierTemplateId: string,
observe: any = 'body',
@ -787,6 +894,7 @@ export class DictionaryControllerService {
observe?: 'body',
reportProgress?: boolean
): Observable<Dictionary>;
public getDictionaryForType(
dossierTemplateId: string,
type: string,
@ -794,6 +902,7 @@ export class DictionaryControllerService {
observe?: 'response',
reportProgress?: boolean
): Observable<HttpResponse<Dictionary>>;
public getDictionaryForType(
dossierTemplateId: string,
type: string,
@ -801,6 +910,7 @@ export class DictionaryControllerService {
observe?: 'events',
reportProgress?: boolean
): Observable<HttpEvent<Dictionary>>;
public getDictionaryForType(
dossierTemplateId: string,
type: string,
@ -873,18 +983,21 @@ export class DictionaryControllerService {
observe?: 'body',
reportProgress?: boolean
): Observable<any>;
public setColors(
body: Colors,
dossierTemplateId: string,
observe?: 'response',
reportProgress?: boolean
): Observable<HttpResponse<any>>;
public setColors(
body: Colors,
dossierTemplateId: string,
observe?: 'events',
reportProgress?: boolean
): Observable<HttpEvent<any>>;
public setColors(
body: Colors,
dossierTemplateId: string,
@ -961,6 +1074,7 @@ export class DictionaryControllerService {
observe?: 'body',
reportProgress?: boolean
): Observable<any>;
public updateType(
body: UpdateTypeValue,
dossierTemplateId: string,
@ -969,6 +1083,7 @@ export class DictionaryControllerService {
observe?: 'response',
reportProgress?: boolean
): Observable<HttpResponse<any>>;
public updateType(
body: UpdateTypeValue,
dossierTemplateId: string,
@ -977,6 +1092,7 @@ export class DictionaryControllerService {
observe?: 'events',
reportProgress?: boolean
): Observable<HttpEvent<any>>;
public updateType(
body: UpdateTypeValue,
dossierTemplateId: string,
@ -1069,6 +1185,7 @@ export class DictionaryControllerService {
observe?: 'body',
reportProgress?: boolean
): Observable<any>;
public uploadDictionaryFileForm(
dossierTemplateId: string,
type: string,
@ -1077,6 +1194,7 @@ export class DictionaryControllerService {
observe?: 'response',
reportProgress?: boolean
): Observable<HttpResponse<any>>;
public uploadDictionaryFileForm(
dossierTemplateId: string,
type: string,
@ -1085,6 +1203,7 @@ export class DictionaryControllerService {
observe?: 'events',
reportProgress?: boolean
): Observable<HttpEvent<any>>;
public uploadDictionaryFileForm(
dossierTemplateId: string,
type: string,
@ -1162,4 +1281,18 @@ export class DictionaryControllerService {
}
);
}
/**
* @param consumes string[] mime-types
* @return true: consumes contains 'multipart/form-data', false: otherwise
*/
private canConsumeForm(consumes: string[]): boolean {
const form = 'multipart/form-data';
for (const consume of consumes) {
if (form === consume) {
return true;
}
}
return false;
}
}

View File

@ -117,31 +117,31 @@ export class DossierTemplateControllerService {
}
/**
* Get a specific DossierTemplate by its ID
* Delete a specific DossierTemplate by its ID
* None
* @param dossierTemplateId dossierTemplateId
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
*/
public getAllDossierTemplates(
public deleteDossierTemplate(
dossierTemplateId: string,
observe?: 'body',
reportProgress?: boolean
): Observable<any>;
public getAllDossierTemplates(
public deleteDossierTemplate(
dossierTemplateId: string,
observe?: 'response',
reportProgress?: boolean
): Observable<HttpResponse<any>>;
public getAllDossierTemplates(
public deleteDossierTemplate(
dossierTemplateId: string,
observe?: 'events',
reportProgress?: boolean
): Observable<HttpEvent<any>>;
public getAllDossierTemplates(
public deleteDossierTemplate(
dossierTemplateId: string,
observe: any = 'body',
reportProgress: boolean = false
@ -186,6 +186,75 @@ export class DossierTemplateControllerService {
);
}
/**
* Delete multiple DossierTemplates by their IDs
* None
* @param body dossierTemplateIds
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
*/
public deleteDossierTemplates(
body: Array<string>,
observe?: 'body',
reportProgress?: boolean
): Observable<any>;
public deleteDossierTemplates(
body: Array<string>,
observe?: 'response',
reportProgress?: boolean
): Observable<HttpResponse<any>>;
public deleteDossierTemplates(
body: Array<string>,
observe?: 'events',
reportProgress?: boolean
): Observable<HttpEvent<any>>;
public deleteDossierTemplates(
body: Array<string>,
observe: any = 'body',
reportProgress: boolean = false
): Observable<any> {
if (body === null || body === undefined) {
throw new Error(
'Required parameter body was null or undefined when calling deleteDossierTemplates.'
);
}
let headers = this.defaultHeaders;
// authentication (RED-OAUTH) required
if (this.configuration.accessToken) {
const accessToken =
typeof this.configuration.accessToken === 'function'
? this.configuration.accessToken()
: this.configuration.accessToken;
headers = headers.set('Authorization', 'Bearer ' + accessToken);
}
// to determine the Accept header
const httpHeaderAccepts: string[] = [];
const httpHeaderAcceptSelected: string | undefined =
this.configuration.selectHeaderAccept(httpHeaderAccepts);
if (httpHeaderAcceptSelected !== undefined) {
headers = headers.set('Accept', httpHeaderAcceptSelected);
}
// to determine the Content-Type header
const consumes: string[] = ['application/json'];
const httpContentTypeSelected: string | undefined =
this.configuration.selectHeaderContentType(consumes);
if (httpContentTypeSelected !== undefined) {
headers = headers.set('Content-Type', httpContentTypeSelected);
}
return this.httpClient.request<any>('post', `${this.basePath}/dossier-template/delete`, {
body: body,
withCredentials: this.configuration.withCredentials,
headers: headers,
observe: observe,
reportProgress: reportProgress
});
}
/**
* Lists all existing DossierTemplates.
* None