Skip to content

Commit 6026840

Browse files
KuliakHejdaJakub
authored andcommitted
fix(admin): correctly parse fedInfo in applications, show display names
* fedInfo can contain attributes not configured in config * we need to parse the values such that key:value is always separated by comma and keys themselves don't contain ':' * we still allow filtering only by known attributes * attribute display names are used in table header and config changing dialogue * fixed missing columns in export
1 parent ef9bdc5 commit 6026840

File tree

15 files changed

+225
-94
lines changed

15 files changed

+225
-94
lines changed

apps/admin-gui/src/app/shared/components/dialogs/applications-list-columns-change-dialog/applications-list-columns-change-dialog.component.html

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,34 +5,44 @@
55
<div *perunWebAppsLoader="loading; indicator: spinner">
66
<h1 mat-dialog-title>{{'DIALOGS.APPLICATIONS_LIST_COLUMNS_CHANGE.TITLE' | translate}}</h1>
77
<div mat-dialog-content>
8+
<div class="d-flex flex-row align-items-baseline justify-content-between">
9+
<perun-web-apps-string-search-select
10+
[values]="columnOptions"
11+
[preselectedValues]="selectedColumns"
12+
[selectPlaceholder]="'DIALOGS.APPLICATIONS_LIST_COLUMNS_CHANGE.FILTER_COLUMNS' | translate"
13+
[mainTextFunction]="columnTranslation"
14+
[disableDeselectButton]="true">
15+
</perun-web-apps-string-search-select>
16+
17+
<button *ngIf="!this.templateAttribute" (click)="default()" mat-stroked-button>
18+
{{'DIALOGS.APPLICATIONS_LIST_COLUMNS_CHANGE.SET_TO_DEFAULT' | translate}}
19+
</button>
20+
21+
<button
22+
[matMenuTriggerFor]="menu"
23+
*ngIf="this.templateAttribute"
24+
class="dropdown-toggle ml-auto"
25+
mat-stroked-button>
26+
{{'DIALOGS.APPLICATIONS_LIST_COLUMNS_CHANGE.SET_TO' | translate}}
27+
</button>
28+
<mat-menu #menu="matMenu">
29+
<button (click)="default()" mat-menu-item>
30+
{{'DIALOGS.APPLICATIONS_LIST_COLUMNS_CHANGE.DEFAULT' | translate}}
31+
</button>
32+
<button (click)="template()" mat-menu-item>
33+
{{'DIALOGS.APPLICATIONS_LIST_COLUMNS_CHANGE.VO_SETTINGS' | translate}}
34+
</button>
35+
</mat-menu>
36+
</div>
837
<perun-web-apps-alert alert_type="warn">
938
{{'DIALOGS.APPLICATIONS_LIST_COLUMNS_CHANGE.WARN' | translate}}
1039
</perun-web-apps-alert>
11-
<mat-form-field class="w-100">
12-
<mat-label
13-
>{{'DIALOGS.APPLICATIONS_LIST_COLUMNS_CHANGE.FILTER_COLUMNS' | translate}}</mat-label
14-
>
15-
<mat-select
16-
(closed)="attribute.value = selectedColumns.value"
17-
[formControl]="selectedColumns"
18-
multiple>
19-
<mat-option class="{{theme}}" *ngFor="let column of columnOptions" [value]="column">
20-
{{column | applicationColumnSelectLabel}}
21-
</mat-option>
22-
</mat-select>
23-
</mat-form-field>
24-
<perun-web-apps-alert alert_type="info">
25-
{{'DIALOGS.APPLICATIONS_LIST_COLUMNS_CHANGE.DEFAULT_TEXT' | translate}}
26-
</perun-web-apps-alert>
2740
</div>
2841
<div mat-dialog-actions>
29-
<button (click)="cancel()" mat-flat-button>
42+
<button (click)="cancel()" mat-flat-button class="ms-auto">
3043
{{'DIALOGS.APPLICATIONS_LIST_COLUMNS_CHANGE.CANCEL' | translate}}
3144
</button>
32-
<button (click)="default()" class="ms-auto" mat-flat-button>
33-
{{'DIALOGS.APPLICATIONS_LIST_COLUMNS_CHANGE.DEFAULT' | translate}}
34-
</button>
35-
<button (click)="confirm()" [disabled]="loading" class="ms-2" color="accent" mat-flat-button>
45+
<button (click)="confirm()" [disabled]="loading" color="accent" mat-flat-button>
3646
{{'DIALOGS.APPLICATIONS_LIST_COLUMNS_CHANGE.CONFIRM' | translate}}
3747
</button>
3848
</div>

apps/admin-gui/src/app/shared/components/dialogs/applications-list-columns-change-dialog/applications-list-columns-change-dialog.component.ts

Lines changed: 72 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import {
66
} from '@perun-web-apps/perun/openapi';
77
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
88
import { NotificatorService, PerunTranslateService } from '@perun-web-apps/perun/services';
9-
import { FormControl } from '@angular/forms';
9+
import { ApplicationColumnSelectLabelPipe } from '@perun-web-apps/perun/pipes';
10+
import { containsExactlyInAnyOrder } from '@perun-web-apps/perun/utils';
1011

1112
export interface ApplicationsListColumnsChangeDialogData {
1213
theme: string;
@@ -17,85 +18,109 @@ export interface ApplicationsListColumnsChangeDialogData {
1718
selector: 'app-applications-list-columns-change-dialog',
1819
templateUrl: './applications-list-columns-change-dialog.component.html',
1920
styleUrls: ['./applications-list-columns-change-dialog.component.scss'],
21+
providers: [ApplicationColumnSelectLabelPipe],
2022
})
2123
export class ApplicationsListColumnsChangeDialogComponent implements OnInit {
2224
loading = false;
2325
theme: string;
2426
simpleColumns = ['createdAt', 'type', 'state', 'createdBy', 'modifiedBy'];
2527
columnOptions = ['createdAt', 'type', 'state', 'createdBy', 'modifiedBy'];
26-
selectedColumns = new FormControl<string[]>([]);
28+
selectedColumns: string[] = [];
2729
attribute: Attribute;
30+
templateAttribute: Attribute;
31+
fedAttributeDefs: AttributeDefinition[] = [];
2832

2933
constructor(
3034
private dialogRef: MatDialogRef<ApplicationsListColumnsChangeDialogComponent>,
3135
private attributesManager: AttributesManagerService,
3236
@Inject(MAT_DIALOG_DATA) private data: ApplicationsListColumnsChangeDialogData,
3337
private translate: PerunTranslateService,
34-
private notificator: NotificatorService
38+
private notificator: NotificatorService,
39+
public columnNamePipe: ApplicationColumnSelectLabelPipe
3540
) {}
3641

42+
columnTranslation = (name: string): string =>
43+
this.columnNamePipe.transform(this.friendlyToDisplayAttrName(name));
44+
3745
ngOnInit(): void {
3846
this.theme = this.data.theme;
47+
this.loading = true;
3948
if (this.data.groupId) {
4049
this.attributesManager
4150
.getIdpAttributeDefinitions()
4251
.subscribe((attrDefs: AttributeDefinition[]) => {
43-
attrDefs.forEach((attr) => {
44-
this.columnOptions.push(attr.friendlyName);
45-
});
52+
this.processFedAttributes(attrDefs);
4653
this.attributesManager
47-
.getGroupAttributeByName(
48-
this.data.groupId,
49-
'urn:perun:group:attribute-def:def:applicationViewPreferences'
54+
.getVoAttributeByName(
55+
this.data.voId,
56+
'urn:perun:vo:attribute-def:def:applicationViewPreferences'
5057
)
51-
.subscribe((attribute) => {
52-
this.attribute = attribute;
53-
const configuredColumns: string[] = attribute.value as string[];
54-
if (configuredColumns !== null && configuredColumns.length > 0) {
55-
this.selectedColumns.setValue(configuredColumns);
56-
} else {
57-
this.selectedColumns.setValue(this.simpleColumns);
58+
.subscribe((voAttr) => {
59+
if (voAttr.value !== null) {
60+
this.templateAttribute = voAttr;
5861
}
62+
this.attributesManager
63+
.getGroupAttributeByName(
64+
this.data.groupId,
65+
'urn:perun:group:attribute-def:def:applicationViewPreferences'
66+
)
67+
.subscribe((groupAttr) => {
68+
this.processCurrentSettings(groupAttr);
69+
this.loading = false;
70+
});
5971
});
6072
});
6173
} else {
6274
this.attributesManager
6375
.getIdpAttributeDefinitions()
6476
.subscribe((attrDefs: AttributeDefinition[]) => {
65-
attrDefs.forEach((attr) => {
66-
this.columnOptions.push(attr.friendlyName);
67-
});
77+
this.processFedAttributes(attrDefs);
6878
this.attributesManager
6979
.getVoAttributeByName(
7080
this.data.voId,
7181
'urn:perun:vo:attribute-def:def:applicationViewPreferences'
7282
)
7383
.subscribe((attribute) => {
74-
this.attribute = attribute;
75-
const configuredColumns: string[] = attribute.value as string[];
76-
if (configuredColumns !== null && configuredColumns.length > 0) {
77-
this.selectedColumns.setValue(configuredColumns);
78-
} else {
79-
this.selectedColumns.setValue(this.simpleColumns);
80-
}
84+
this.processCurrentSettings(attribute);
8185
});
86+
this.loading = false;
8287
});
8388
}
8489
}
8590

8691
confirm(): void {
92+
if (
93+
this.selectedColumns === null ||
94+
this.selectedColumns.length === 0 ||
95+
containsExactlyInAnyOrder(this.selectedColumns, this.simpleColumns)
96+
) {
97+
this.attribute.value = [];
98+
} else {
99+
this.attribute.value = [...this.selectedColumns];
100+
}
87101
if (this.data.groupId) {
88102
this.changeGroupAttribute();
89103
} else {
90104
this.changeVoAttribute();
91105
}
92106
}
107+
93108
cancel(): void {
94109
this.dialogRef.close(false);
95110
}
111+
96112
default(): void {
97-
this.attribute.value = [];
98-
this.confirm();
113+
this.selectedColumns = [...this.simpleColumns];
114+
}
115+
116+
template(): void {
117+
this.selectedColumns = this.templateAttribute.value as string[];
118+
}
119+
120+
private friendlyToDisplayAttrName(column: string): string {
121+
return (
122+
this.fedAttributeDefs.find((attr) => attr.friendlyName === column)?.displayName || column
123+
);
99124
}
100125

101126
private changeVoAttribute(): void {
@@ -139,4 +164,23 @@ export class ApplicationsListColumnsChangeDialogComponent implements OnInit {
139164
},
140165
});
141166
}
167+
168+
private processFedAttributes(attrDefs: AttributeDefinition[]): void {
169+
attrDefs = attrDefs.sort((a, b) => a.friendlyName.localeCompare(b.friendlyName));
170+
this.fedAttributeDefs = attrDefs;
171+
attrDefs.forEach((attr) => {
172+
this.columnOptions.push(attr.friendlyName);
173+
});
174+
this.columnOptions = [...this.columnOptions];
175+
}
176+
177+
private processCurrentSettings(attr: Attribute): void {
178+
this.attribute = attr;
179+
const configuredColumns: string[] = attr.value as string[];
180+
if (configuredColumns !== null && configuredColumns.length > 0) {
181+
this.selectedColumns = [...configuredColumns];
182+
} else {
183+
this.selectedColumns = [...this.simpleColumns];
184+
}
185+
}
142186
}

apps/admin-gui/src/app/vos/components/applications-dynamic-list/applications-dynamic-list.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,8 @@
115115
<td *matCellDef="let application" mat-cell>{{application.fedInfo}}</td>
116116
</ng-container>
117117

118-
<ng-container *ngFor="let i = index; let col of fedColumns;" matColumnDef="{{col}}">
119-
<th *matHeaderCellDef mat-header-cell>{{col}}</th>
118+
<ng-container *ngFor="let i = index; let col of fedColumnsFriendly;" matColumnDef="{{col}}">
119+
<th *matHeaderCellDef mat-header-cell>{{fedColumnsDisplay[i]}}</th>
120120
<td *matCellDef="let application" mat-cell>
121121
{{getFedValue(application.fedInfo, col)}}
122122
</td>

apps/admin-gui/src/app/vos/components/applications-dynamic-list/applications-dynamic-list.component.ts

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
ApplicationFormItemData,
1414
ApplicationsOrderColumn,
1515
AppState,
16+
AttributeDefinition,
1617
AttributesManagerService,
1718
Group,
1819
Member,
@@ -50,7 +51,7 @@ export class ApplicationsDynamicListComponent implements OnInit, OnChanges, Afte
5051

5152
@ViewChild(MatSort) sort: MatSort;
5253
@Input() displayedColumns: string[] = [];
53-
@Input() fedColumns: string[] = [];
54+
@Input() fedColumnsFriendly: string[] = [];
5455
@Input() tableId: string;
5556
@Input() disableRouting = false;
5657
@Input() searchString = '';
@@ -61,13 +62,17 @@ export class ApplicationsDynamicListComponent implements OnInit, OnChanges, Afte
6162
@Input() states: AppState[];
6263
@Input() dateTo: Date = new Date();
6364
@Input() dateFrom: Date = this.yearAgo();
65+
@Input() fedAttrs: AttributeDefinition[] = [];
6466
@Input() fedAttrNames: string[] = [];
6567
@Input() refreshTable = false;
6668
@Output() loading$: EventEmitter<Observable<boolean>> = new EventEmitter<Observable<boolean>>();
69+
6770
parsedColumns: string[] = [];
6871
dataSource: DynamicDataSource<Application>;
6972
pageSizeOptions = TABLE_ITEMS_COUNT_OPTIONS;
7073

74+
fedColumnsDisplay = [];
75+
7176
constructor(
7277
private authResolver: GuiAuthResolver,
7378
private tableConfigService: TableConfigService,
@@ -126,6 +131,13 @@ export class ApplicationsDynamicListComponent implements OnInit, OnChanges, Afte
126131
}
127132

128133
ngOnChanges(): void {
134+
this.fedColumnsDisplay = [];
135+
this.fedColumnsFriendly.forEach((name) =>
136+
this.fedColumnsDisplay.push(
137+
this.fedAttrs.find((attr) => attr.friendlyName === name)?.displayName || ''
138+
)
139+
);
140+
129141
this.refreshTable = false;
130142
if (this.dataSource) {
131143
this.child.paginator.pageIndex = 0;
@@ -216,7 +228,7 @@ export class ApplicationsDynamicListComponent implements OnInit, OnChanges, Afte
216228
case 'type':
217229
return data.type;
218230
case 'fedInfo':
219-
return data.fedInfo;
231+
return data.fedInfo ? this.deescapeMapEscapings(data.fedInfo) : '';
220232
case 'formData':
221233
return this.stringify((data as RichApplication).formData);
222234
case 'state':
@@ -236,7 +248,7 @@ export class ApplicationsDynamicListComponent implements OnInit, OnChanges, Afte
236248
case 'modifiedAt':
237249
return data.modifiedAt;
238250
default:
239-
return data[column] as string;
251+
return this.getFedValue(data.fedInfo, column);
240252
}
241253
}
242254

@@ -341,7 +353,6 @@ export class ApplicationsDynamicListComponent implements OnInit, OnChanges, Afte
341353

342354
getFedValue(fedInfo: string, colName: string): string {
343355
// looking for values between {,FED_INFO_ATTR_NAME:}
344-
const regexOtherValues = new RegExp(this.fedAttrNames.map((v) => ',' + v + ':').join('|'));
345356
if (fedInfo === null || fedInfo.length === 0) {
346357
return '';
347358
}
@@ -355,7 +366,18 @@ export class ApplicationsDynamicListComponent implements OnInit, OnChanges, Afte
355366
if (values.length < 2) {
356367
return '';
357368
}
358-
values = values[1].split(regexOtherValues);
359-
return values[0].endsWith(',') ? values[0].slice(0, -1) : values[0];
369+
// fedInfo should always end with comma - not escaped one though
370+
values[1] = values[1].replace(/\\,/gi, '#ESCAPED_COMMA');
371+
values = values[1].split(',');
372+
values[0] = values[0].replace('#ESCAPED_COMMA', '\\,');
373+
return this.deescapeMapEscapings(values[0]);
374+
}
375+
376+
// Fedinfo characters that are being escaped: '\', ':' and ','
377+
deescapeMapEscapings(value: string): string {
378+
let newValue = value.replace(/\\:/gi, ':');
379+
newValue = newValue.replace(/\\,/gi, ',');
380+
newValue = newValue.replace(/\\\\/gi, '\\');
381+
return newValue;
360382
}
361383
}

apps/admin-gui/src/app/vos/pages/group-detail-page/group-applications/group-applications.component.html

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,17 @@ <h1 class="page-subtitle">{{'VO_DETAIL.APPLICATION.TITLE' | translate}}</h1>
1212
<perun-web-apps-debounce-filter
1313
(filter)="applyFilter($event)"
1414
[placeholder]="'VO_DETAIL.APPLICATION.FILTER'"></perun-web-apps-debounce-filter>
15-
<perun-web-apps-date-range [endDate]="endDate" [startDate]="startDate">
15+
<perun-web-apps-date-range [endDate]="endDate" [startDate]="startDate" class="me-2">
1616
</perun-web-apps-date-range>
1717
<span
18-
[matTooltipDisabled]="!showAllDetails"
19-
matTooltip="{{'VO_DETAIL.APPLICATION.COLUMNS_TOOLTIP' | translate}}"
18+
matTooltip="{{showAllDetails ? ('VO_DETAIL.APPLICATION.COLUMNS_TOOLTIP' | translate) : ('VO_DETAIL.APPLICATION.SET_COLUMN_SETTINGS' | translate)}}"
2019
matTooltipPosition="above">
2120
<button
2221
mat-stroked-button
2322
*ngIf="columnsAuth"
2423
[disabled]="showAllDetails"
2524
(click)="this.setColumns()">
26-
{{'VO_DETAIL.APPLICATION.SET_COLUMN_SETTINGS' | translate}}
25+
<mat-icon>settings</mat-icon>
2726
</button>
2827
</span>
2928
</div>
@@ -37,8 +36,8 @@ <h1 class="page-subtitle">{{'VO_DETAIL.APPLICATION.TITLE' | translate}}</h1>
3736
[tableId]="showAllDetails ? detailTableId : tableId"
3837
[searchString]="filterValue"
3938
[displayedColumns]="currentColumns"
40-
[fedColumns]="configuredFedColumns"
41-
[fedAttrNames]="fedAttrNames"
39+
[fedColumnsFriendly]="showAllDetails ? [] : configuredFedColumns"
40+
[fedAttrs]="fedAttrs"
4241
[states]="currentStates"
4342
[dateFrom]="startDate.value"
4443
[dateTo]="endDate.value"

0 commit comments

Comments
 (0)