Skip to content

Commit 8ebaef6

Browse files
vins01-4scienceAdamF42
authored andcommitted
Merged in task/dspace-cris-2024_02_x/DSC-2753 (pull request DSpace#4134)
DSC-2753 Approved-by: Fapohunda, Adamo
2 parents 96d9631 + 3822e2d commit 8ebaef6

File tree

7 files changed

+228
-136
lines changed

7 files changed

+228
-136
lines changed
Lines changed: 56 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,69 @@
11
<div class="container">
2-
<div class="d-flex">
3-
<h2 class="flex-grow-1">{{'audit.object.overview.title' | translate}}</h2>
4-
</div>
2+
<div class="d-flex">
3+
<h2 class="flex-grow-1">{{ 'audit.object.overview.title' | translate }}</h2>
4+
</div>
55

6-
<ng-container *ngIf="object">
7-
<h4 class="mt-4 mb-4">{{ object.name }} (<em>{{object.type}}</em>)</h4>
6+
<ng-container *ngIf="object">
7+
<h4 class="mt-4 mb-4">{{ object.name }} (<em>{{ object.type }}</em>)</h4>
88

9-
<ng-container *ngIf="(auditsRD$ | async)?.payload as audits">
9+
<ng-container *ngIf="(auditsRD$ | async)?.payload as audits">
1010

11-
<div *ngIf="audits.totalElements === 0">
12-
No audits found.
13-
</div>
14-
15-
<ds-pagination *ngIf="audits.totalElements > 0"
16-
[paginationOptions]="pageConfig"
17-
[collectionSize]="audits.totalElements"
18-
[hideGear]="true"
19-
[hidePagerWhenSinglePage]="true">
11+
<div *ngIf="audits.totalElements === 0">
12+
No audits found.
13+
</div>
2014

21-
<div class="table-responsive">
22-
<table class="table table-striped table-hover">
23-
<thead>
24-
<tr>
25-
<!-- <th>{{ 'audit.overview.table.id' | translate }}</th> -->
26-
<th>{{ 'audit.overview.table.entityType' | translate }}</th>
27-
<th>{{ 'audit.overview.table.eperson' | translate }}</th>
28-
<th>{{ 'audit.overview.table.timestamp' | translate }}</th>
29-
<th>Other Object</th>
30-
</tr>
31-
</thead>
32-
<tbody>
33-
<tr *ngFor="let audit of audits.page">
34-
<!-- <td><a [routerLink]="['/auditlogs/', audit.id]">{{audit.id}}</a></td> -->
35-
<td>{{ audit.eventType }}</td>
36-
<td *ngVar="(getEpersonName(audit) | async) as ePersonName">{{ePersonName}}</td>
37-
<td>{{ audit.timeStamp | date:dateFormat}}</td>
38-
<td>
39-
<span *ngIf="object.id === audit.objectUUID">
40-
<!-- object.id === audit.objectUUID -->
41-
<ng-container *ngIf="(getOtherObject(audit, object.id) | async) as subject">
42-
<ng-container *ngIf="subject">
43-
{{ subject.name }} <em>({{ subject.type }})</em>
44-
</ng-container>
45-
</ng-container>
46-
</span>
15+
<ds-pagination *ngIf="audits.totalElements > 0"
16+
[paginationOptions]="pageConfig"
17+
[collectionSize]="audits.totalElements"
18+
[hideGear]="true"
19+
[hidePagerWhenSinglePage]="true">
4720

48-
<span *ngIf="object.id === audit.subjectUUID">
49-
<!-- object.id === audit.subjectUUID -->
50-
</span>
51-
</td>
52-
</tr>
53-
</tbody>
54-
</table>
55-
</div>
56-
</ds-pagination>
21+
<div class="table-responsive">
22+
<table class="table table-striped table-hover">
23+
<thead>
24+
<tr>
25+
<!-- <th>{{ 'audit.overview.table.id' | translate }}</th> -->
26+
<th>{{ 'audit.overview.table.entityType' | translate }}</th>
27+
<th>{{ 'audit.overview.table.eperson' | translate }}</th>
28+
<th>{{ 'audit.overview.table.timestamp' | translate }}</th>
29+
<th>Other Object</th>
30+
</tr>
31+
</thead>
32+
<tbody>
33+
<tr *ngFor="let audit of audits?.page; trackBy: trackById">
34+
<!-- <td><a [routerLink]="['/auditlogs/', audit.id]">{{audit.id}}</a></td> -->
35+
<td>{{ audit.eventType }}</td>
36+
<td>
37+
<span *ngIf="(audit.epersonName | async) as epersonName; else noEperson">{{epersonName}}</span>
38+
<ng-template #noEperson><em>{{ 'audit.overview.table.eperson.anonymous' | translate }}</em></ng-template>
39+
</td>
40+
<td>{{ audit.timeStamp | date:dateFormat }}</td>
41+
<td>
42+
<span *ngIf="object.id === audit.objectUUID">
43+
<!-- object.id === audit.objectUUID -->
44+
<ng-container *ngIf="(audit.subject | async) as subject">
45+
{{ subject.name }} <em>({{ subject.type }})</em>
46+
</ng-container>
47+
</span>
48+
<span *ngIf="object.id === audit.subjectUUID">
49+
<!-- object.id === audit.subjectUUID -->
50+
</span>
51+
</td>
52+
</tr>
53+
</tbody>
54+
</table>
55+
</div>
56+
</ds-pagination>
5757

58-
<a class="btn btn-light mt-3" [routerLink]="['/items', object.id]"><i class="fas fa-arrow-left"></i> Back to Item</a>
58+
<a class="btn btn-light mt-3" [routerLink]="['/items', object.id]"><i class="fas fa-arrow-left"></i> Back to Item</a>
5959

60-
<!-- <a class="btn btn-light mt-3" [routerLink]="'/auditlogs'">{{'audit.detail.back' | translate}}</a> -->
60+
<!-- <a class="btn btn-light mt-3" [routerLink]="'/auditlogs'">{{'audit.detail.back' | translate}}</a> -->
6161

62-
</ng-container>
62+
</ng-container>
6363

64-
<h4 class="mt-4 mb-4" *ngIf="(auditsRD$ | async)?.statusCode === 404">{{'audit.object.overview.disabled.message' | translate}}</h4>
64+
<h4 class="mt-4 mb-4"
65+
*ngIf="(auditsRD$ | async)?.statusCode === 404">{{ 'audit.object.overview.disabled.message' | translate }}</h4>
6566

66-
</ng-container>
67+
</ng-container>
6768

6869
</div>

src/app/audit-page/object-audit-overview/object-audit-overview.component.ts

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@ import {
2828
} from 'rxjs/operators';
2929

3030
import { COLLECTION_PAGE_LINKS_TO_FOLLOW } from '../../collection-page/collection-page.resolver';
31-
import { AuditDataService } from '../../core/audit/audit-data.service';
32-
import { Audit } from '../../core/audit/model/audit.model';
31+
import {
32+
AuditDataService,
33+
AuditDetails,
34+
} from '../../core/audit/audit-data.service';
3335
import { AuthService } from '../../core/auth/auth.service';
3436
import { SortDirection } from '../../core/cache/models/sort-options.model';
3537
import { CollectionDataService } from '../../core/data/collection-data.service';
@@ -46,7 +48,6 @@ import { Item } from '../../core/shared/item.model';
4648
import { getFirstCompletedRemoteData } from '../../core/shared/operators';
4749
import { PaginationComponent } from '../../shared/pagination/pagination.component';
4850
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
49-
import { VarDirective } from '../../shared/utils/var.directive';
5051

5152
/**
5253
* Component displaying a list of all audit about a object in a paginated table
@@ -60,7 +61,6 @@ import { VarDirective } from '../../shared/utils/var.directive';
6061
AsyncPipe,
6162
TranslateModule,
6263
NgForOf,
63-
VarDirective,
6464
RouterLink,
6565
DatePipe,
6666
],
@@ -76,7 +76,7 @@ export class ObjectAuditOverviewComponent implements OnInit {
7676
/**
7777
* List of all audits
7878
*/
79-
auditsRD$: Observable<RemoteData<PaginatedList<Audit>>>;
79+
auditsRD$: Observable<RemoteData<PaginatedList<AuditDetails>>>;
8080

8181
/**
8282
* The current pagination configuration for the page used by the FindAll method
@@ -112,7 +112,8 @@ export class ObjectAuditOverviewComponent implements OnInit {
112112
protected authorizationService: AuthorizationDataService,
113113
protected paginationService: PaginationService,
114114
protected collectionDataService: CollectionDataService,
115-
) {}
115+
) {
116+
}
116117

117118
ngOnInit(): void {
118119
this.route.paramMap.pipe(
@@ -148,16 +149,21 @@ export class ObjectAuditOverviewComponent implements OnInit {
148149

149150

150151
this.auditsRD$ = combineLatest([isAdmin$, config$, this.owningCollection$, parentCommunity$]).pipe(
151-
mergeMap(([isAdmin, config, owningCollection, parentCommunity]) => {
152+
mergeMap(([isAdmin, config, owningCollection, parentCommunity]) => {
152153
if (isAdmin) {
153-
return this.auditService.findByObject(this.object.id, config, owningCollection.id, parentCommunity.id);
154+
return this.auditService.findByObject(this.object.id, config, owningCollection.id, parentCommunity.id)
155+
.pipe(
156+
getFirstCompletedRemoteData(),
157+
map(data => this.auditService.mapToAuditDetails(data)),
158+
);
154159
}
155160

156161
return of(null);
157162
}),
158163
);
159164
}
160165

166+
161167
isCurrentUserAdmin(): Observable<boolean> {
162168
return combineLatest([
163169
this.authorizationService.isAuthorized(FeatureID.IsCollectionAdmin),
@@ -172,15 +178,11 @@ export class ObjectAuditOverviewComponent implements OnInit {
172178
}
173179

174180
/**
175-
* Get the name of an EPerson by ID
176-
* @param audit Audit object
181+
* Method to prevent unnecessary for loop re-rendering
177182
*/
178-
getEpersonName(audit: Audit): Observable<string> {
179-
return this.auditService.getEpersonName(audit);
183+
trackById(index: number, audit: AuditDetails): string {
184+
return audit.id;
180185
}
181186

182-
getOtherObject(audit: Audit, contextObjectId: string): Observable<any> {
183-
return this.auditService.getOtherObject(audit, contextObjectId);
184-
}
185187

186188
}

src/app/audit-page/overview/audit-overview.component.html

Lines changed: 40 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,45 +3,50 @@
33
<h2 class="flex-grow-1">{{'audit.overview.title' | translate}}</h2>
44
</div>
55

6-
<ng-container *ngIf="isAdmin$ | async">
7-
<div *ngIf="(auditsRD$ | async)?.payload?.totalElements === 0">
6+
<ng-container *ngIf="isAdmin$ | async as isAdmin">
7+
<ng-container *ngIf="(auditsRD$ | async)?.payload as audits">
8+
<div *ngIf="audits?.totalElements === 0">
89
No audits found.
9-
</div>
10+
</div>
1011

11-
<ds-pagination *ngIf="(auditsRD$ | async)?.payload?.totalElements > 0"
12-
[paginationOptions]="pageConfig"
13-
[collectionSize]="(auditsRD$ | async)?.payload?.totalElements"
14-
[hideGear]="true"
15-
[hidePagerWhenSinglePage]="true">
12+
<ds-pagination *ngIf="audits?.totalElements > 0"
13+
[paginationOptions]="pageConfig"
14+
[collectionSize]="audits?.totalElements"
15+
[hideGear]="true"
16+
[hidePagerWhenSinglePage]="true">
1617
<div class="table-responsive">
17-
<table class="table table-striped table-hover">
18-
<thead>
19-
<tr>
20-
<th>{{ 'audit.overview.table.id' | translate }}</th>
21-
<th>{{ 'audit.overview.table.entityType' | translate }}</th>
22-
<th>{{ 'audit.overview.table.objectUUID' | translate }}</th>
23-
<th>{{ 'audit.overview.table.objectType' | translate }}</th>
24-
<th>{{ 'audit.overview.table.subjectUUID' | translate }}</th>
25-
<th>{{ 'audit.overview.table.subjectType' | translate }}</th>
26-
<th>{{ 'audit.overview.table.eperson' | translate }}</th>
27-
<th>{{ 'audit.overview.table.timestamp' | translate }}</th>
28-
</tr>
29-
</thead>
30-
<tbody>
31-
<tr *ngFor="let audit of (auditsRD$ | async)?.payload?.page">
32-
<td><a [routerLink]="['/auditlogs/', audit.id]">{{audit.id}}</a></td>
33-
<td>{{ audit.eventType }}</td>
34-
<td><a *ngIf="audit.objectUUID" [routerLink]="['/auditlogs/object/', audit.objectUUID]">{{audit.objectUUID}}</a></td>
35-
<td>{{ audit.objectType }}</td>
36-
<td>{{ audit.subjectUUID }}</td>
37-
<td>{{ audit.subjectType }}</td>
38-
<td *ngVar="(getEpersonName(audit) | async) as ePersonName">{{ePersonName}}</td>
39-
<td>{{ audit.timeStamp | date:dateFormat}}</td>
40-
</tr>
41-
</tbody>
42-
</table>
18+
<table class="table table-striped table-hover">
19+
<thead>
20+
<tr>
21+
<th>{{ 'audit.overview.table.id' | translate }}</th>
22+
<th>{{ 'audit.overview.table.entityType' | translate }}</th>
23+
<th>{{ 'audit.overview.table.objectUUID' | translate }}</th>
24+
<th>{{ 'audit.overview.table.objectType' | translate }}</th>
25+
<th>{{ 'audit.overview.table.subjectUUID' | translate }}</th>
26+
<th>{{ 'audit.overview.table.subjectType' | translate }}</th>
27+
<th>{{ 'audit.overview.table.eperson' | translate }}</th>
28+
<th>{{ 'audit.overview.table.timestamp' | translate }}</th>
29+
</tr>
30+
</thead>
31+
<tbody>
32+
<tr *ngFor="let audit of audits?.page; trackBy: trackById">
33+
<td><a [routerLink]="['/auditlogs/', audit.id]">{{audit.id}}</a></td>
34+
<td>{{ audit.eventType }}</td>
35+
<td><a *ngIf="audit.objectUUID" [routerLink]="['/auditlogs/object/', audit.objectUUID]">{{audit.objectUUID}}</a></td>
36+
<td>{{ audit.objectType }}</td>
37+
<td>{{ audit.subjectUUID }}</td>
38+
<td>{{ audit.subjectType }}</td>
39+
<td>
40+
<span *ngIf="(audit.epersonName | async) as epersonName; else noEperson">{{epersonName}}</span>
41+
<ng-template #noEperson><em>{{ 'audit.overview.table.eperson.anonymous' | translate }}</em></ng-template>
42+
</td>
43+
<td>{{ audit.timeStamp | date:dateFormat}}</td>
44+
</tr>
45+
</tbody>
46+
</table>
4347
</div>
44-
</ds-pagination>
48+
</ds-pagination>
49+
</ng-container>
4550
</ng-container>
4651

4752
</div>

src/app/audit-page/overview/audit-overview.component.spec.ts

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { NO_ERRORS_SCHEMA } from '@angular/core';
22
import {
33
ComponentFixture,
4+
fakeAsync,
45
TestBed,
56
waitForAsync,
67
} from '@angular/core/testing';
@@ -9,13 +10,22 @@ import { RouterTestingModule } from '@angular/router/testing';
910
import { TranslateModule } from '@ngx-translate/core';
1011
import { of } from 'rxjs';
1112

12-
import { AuditDataService } from '../../core/audit/audit-data.service';
13+
import {
14+
AuditDataService,
15+
AuditDetails,
16+
} from '../../core/audit/audit-data.service';
1317
import { Audit } from '../../core/audit/model/audit.model';
1418
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
1519
import { PaginationService } from '../../core/pagination/pagination.service';
1620
import { PaginationComponent } from '../../shared/pagination/pagination.component';
17-
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
18-
import { AuditMock } from '../../shared/testing/audit.mock';
21+
import {
22+
createSuccessfulRemoteDataObject,
23+
createSuccessfulRemoteDataObject$,
24+
} from '../../shared/remote-data.utils';
25+
import {
26+
AuditDetailsMock,
27+
AuditMock,
28+
} from '../../shared/testing/audit.mock';
1929
import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub';
2030
import { createPaginatedList } from '../../shared/testing/utils.test';
2131
import { VarDirective } from '../../shared/utils/var.directive';
@@ -28,15 +38,21 @@ describe('AuditOverviewComponent', () => {
2838
let auditService: AuditDataService;
2939
let authorizationService: any;
3040
let audits: Audit[];
41+
let auditDetails: AuditDetails[];
3142
const paginationService = new PaginationServiceStub();
3243

3344
function init() {
34-
audits = [ AuditMock, AuditMock, AuditMock ];
35-
auditService = jasmine.createSpyObj('processService', {
45+
audits = [AuditMock, AuditMock, AuditMock];
46+
auditDetails = [AuditDetailsMock, AuditDetailsMock, AuditDetailsMock];
47+
auditService = jasmine.createSpyObj('auditService', {
3648
findAll: createSuccessfulRemoteDataObject$(createPaginatedList(audits)),
3749
getEpersonName: of('Eperson Name'),
50+
mapToAuditDetails: createSuccessfulRemoteDataObject(createPaginatedList(auditDetails)),
51+
});
52+
53+
authorizationService = jasmine.createSpyObj('authorizationService', {
54+
isAuthorized: jasmine.createSpy('isAuthorized'),
3855
});
39-
authorizationService = jasmine.createSpyObj('authorizationService', ['isAuthorized']);
4056
}
4157

4258
beforeEach(waitForAsync(() => {
@@ -54,13 +70,13 @@ describe('AuditOverviewComponent', () => {
5470

5571
describe('if the current user is an admin', () => {
5672

57-
beforeEach(() => {
58-
authorizationService.isAuthorized.and.callFake(() => of(true));
73+
beforeEach(fakeAsync(() => {
74+
authorizationService.isAuthorized.and.returnValue(of(true));
5975

6076
fixture = TestBed.createComponent(AuditOverviewComponent);
6177
component = fixture.componentInstance;
6278
fixture.detectChanges();
63-
});
79+
}));
6480

6581
describe('table structure', () => {
6682
let rowElements;
@@ -118,7 +134,7 @@ describe('AuditOverviewComponent', () => {
118134
it('should display the eperson name in the seventh column', () => {
119135
rowElements.forEach((rowElement, index) => {
120136
const el = rowElement.query(By.css('td:nth-child(7)')).nativeElement;
121-
expect(el.textContent).toContain('Eperson Name');
137+
expect(el.textContent).toContain('Eperson Test');
122138
});
123139
});
124140

0 commit comments

Comments
 (0)