Skip to content

Commit 8f97a2f

Browse files
Andrea Barbassoatarix83
authored andcommitted
Merged in task/dspace-cris-2023_02_x/DSC-2142 (pull request DSpace#2699)
Task/dspace cris 2023 02 x/DSC-2142 Approved-by: Francesco Molinaro Approved-by: Giuseppe Digilio
2 parents 0d74d4b + 3b44d22 commit 8f97a2f

9 files changed

Lines changed: 328 additions & 117 deletions

src/app/bitstream-page/bitstream-download-page/bitstream-download-page.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<div class="container">
22
<div class="d-flex justify-content-between">
3-
<h1 class="h3">{{'bitstream.download.page' | translate:{ bitstream: dsoNameService.getName((bitstream$ | async)) } }}</h1>
3+
<h1 class="h3">{{'bitstream.download.page' | translate:{ bitstream: (fileName$ | async) } }}</h1>
44
<div class="pt-3">
55
<button *ngIf="hasHistory" (click)="back()" class="btn btn-outline-secondary">
66
<i class="fas fa-arrow-left"></i> {{'bitstream.download.page.back' | translate}}

src/app/bitstream-page/bitstream-download-page/bitstream-download-page.component.spec.ts

Lines changed: 0 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import { AuthorizationDataService } from '../../core/data/feature-authorization/
88
import { HardRedirectService } from '../../core/services/hard-redirect.service';
99
import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils';
1010
import { ActivatedRoute, Router } from '@angular/router';
11-
import { getForbiddenRoute } from '../../app-routing-paths';
1211
import { TranslateModule } from '@ngx-translate/core';
1312
import { CommonModule } from '@angular/common';
1413
import { SignpostingDataService } from '../../core/data/signposting-data.service';
@@ -123,72 +122,4 @@ describe('BitstreamDownloadPageComponent', () => {
123122
expect(component).toBeTruthy();
124123
});
125124
});
126-
127-
describe('bitstream retrieval', () => {
128-
describe('when the user is authorized and not logged in', () => {
129-
beforeEach(waitForAsync(() => {
130-
init();
131-
(authService.isAuthenticated as jasmine.Spy).and.returnValue(observableOf(false));
132-
133-
initTestbed();
134-
}));
135-
beforeEach(() => {
136-
fixture = TestBed.createComponent(BitstreamDownloadPageComponent);
137-
component = fixture.componentInstance;
138-
fixture.detectChanges();
139-
});
140-
it('should redirect to the content link', () => {
141-
expect(hardRedirectService.redirect).toHaveBeenCalledWith('bitstream-content-link');
142-
});
143-
it('should add the signposting links', () => {
144-
expect(serverResponseService.setHeader).toHaveBeenCalled();
145-
});
146-
});
147-
describe('when the user is authorized and logged in', () => {
148-
beforeEach(waitForAsync(() => {
149-
init();
150-
initTestbed();
151-
}));
152-
beforeEach(() => {
153-
fixture = TestBed.createComponent(BitstreamDownloadPageComponent);
154-
component = fixture.componentInstance;
155-
fixture.detectChanges();
156-
});
157-
it('should redirect to an updated content link', () => {
158-
expect(hardRedirectService.redirect).toHaveBeenCalledWith('content-url-with-headers');
159-
});
160-
});
161-
describe('when the user is not authorized and logged in', () => {
162-
beforeEach(waitForAsync(() => {
163-
init();
164-
(authorizationService.isAuthorized as jasmine.Spy).and.returnValue(observableOf(false));
165-
initTestbed();
166-
}));
167-
beforeEach(() => {
168-
fixture = TestBed.createComponent(BitstreamDownloadPageComponent);
169-
component = fixture.componentInstance;
170-
fixture.detectChanges();
171-
});
172-
it('should navigate to the forbidden route', () => {
173-
expect(router.navigateByUrl).toHaveBeenCalledWith(getForbiddenRoute(), { skipLocationChange: true });
174-
});
175-
});
176-
describe('when the user is not authorized and not logged in', () => {
177-
beforeEach(waitForAsync(() => {
178-
init();
179-
(authService.isAuthenticated as jasmine.Spy).and.returnValue(observableOf(false));
180-
(authorizationService.isAuthorized as jasmine.Spy).and.returnValue(observableOf(false));
181-
initTestbed();
182-
}));
183-
beforeEach(() => {
184-
fixture = TestBed.createComponent(BitstreamDownloadPageComponent);
185-
component = fixture.componentInstance;
186-
fixture.detectChanges();
187-
});
188-
it('should navigate to the login page', () => {
189-
expect(authService.setRedirectUrl).toHaveBeenCalled();
190-
expect(router.navigateByUrl).toHaveBeenCalledWith('login');
191-
});
192-
});
193-
});
194125
});

src/app/bitstream-page/bitstream-download-page/bitstream-download-page.component.ts

Lines changed: 6 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
11
import { Component, Inject, OnInit, PLATFORM_ID } from '@angular/core';
2-
import { filter, map, startWith, switchMap, take } from 'rxjs/operators';
2+
import { map, startWith, take } from 'rxjs/operators';
33
import { ActivatedRoute, Router } from '@angular/router';
4-
import { hasValue, isNotEmpty } from '../../shared/empty.util';
4+
import { isNotEmpty } from '../../shared/empty.util';
55
import { getRemoteDataPayload } from '../../core/shared/operators';
66
import { Bitstream } from '../../core/shared/bitstream.model';
77
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
8-
import { FeatureID } from '../../core/data/feature-authorization/feature-id';
98
import { AuthService } from '../../core/auth/auth.service';
10-
import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs';
9+
import { Observable } from 'rxjs';
1110
import { FileService } from '../../core/shared/file.service';
1211
import { HardRedirectService } from '../../core/services/hard-redirect.service';
13-
import { getForbiddenRoute } from '../../app-routing-paths';
1412
import { RemoteData } from '../../core/data/remote-data';
15-
import { redirectOn4xx } from '../../core/shared/authorized.operators';
1613
import { isPlatformServer, Location } from '@angular/common';
1714
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
1815
import { SignpostingDataService } from '../../core/data/signposting-data.service';
@@ -62,52 +59,18 @@ export class BitstreamDownloadPageComponent implements OnInit {
6259
}
6360

6461
ngOnInit(): void {
65-
6662
this.bitstreamRD$ = this.route.data.pipe(
67-
map((data) => data.bitstream));
63+
map((data) => data.bitstream)
64+
);
6865

6966
this.bitstream$ = this.bitstreamRD$.pipe(
70-
redirectOn4xx(this.router, this.auth),
7167
getRemoteDataPayload()
7268
);
7369

7470
this.fileName$ = this.bitstream$.pipe(
7571
map((bitstream: Bitstream) => this.dsoNameService.getName(bitstream)),
76-
startWith('file'),
72+
startWith('file')
7773
);
78-
79-
this.bitstream$.pipe(
80-
switchMap((bitstream: Bitstream) => {
81-
const isAuthorized$ = this.authorizationService.isAuthorized(FeatureID.CanDownload, isNotEmpty(bitstream) ? bitstream.self : undefined);
82-
const isLoggedIn$ = this.auth.isAuthenticated();
83-
return observableCombineLatest([isAuthorized$, isLoggedIn$, observableOf(bitstream)]);
84-
}),
85-
filter(([isAuthorized, isLoggedIn, bitstream]: [boolean, boolean, Bitstream]) => hasValue(isAuthorized) && hasValue(isLoggedIn)),
86-
take(1),
87-
switchMap(([isAuthorized, isLoggedIn, bitstream]: [boolean, boolean, Bitstream]) => {
88-
if (isAuthorized && isLoggedIn) {
89-
return this.fileService.retrieveFileDownloadLink(bitstream._links.content.href).pipe(
90-
filter((fileLink) => hasValue(fileLink)),
91-
take(1),
92-
map((fileLink) => {
93-
return [isAuthorized, isLoggedIn, bitstream, fileLink];
94-
}));
95-
} else {
96-
return [[isAuthorized, isLoggedIn, bitstream, '']];
97-
}
98-
})
99-
).subscribe(([isAuthorized, isLoggedIn, bitstream, fileLink]: [boolean, boolean, Bitstream, string]) => {
100-
if (isAuthorized && isLoggedIn && isNotEmpty(fileLink)) {
101-
this.hardRedirectService.redirect(fileLink);
102-
} else if (isAuthorized && !isLoggedIn) {
103-
this.hardRedirectService.redirect(bitstream._links.content.href);
104-
} else if (!isAuthorized && isLoggedIn) {
105-
this.router.navigateByUrl(getForbiddenRoute(), {skipLocationChange: true});
106-
} else if (!isAuthorized && !isLoggedIn) {
107-
this.auth.setRedirectUrl(this.router.url);
108-
this.router.navigateByUrl('login');
109-
}
110-
});
11174
}
11275

11376
/**
@@ -124,7 +87,6 @@ export class BitstreamDownloadPageComponent implements OnInit {
12487
signpostingLinks.forEach((link: SignpostingLink) => {
12588
links = links + (isNotEmpty(links) ? ', ' : '') + `<${link.href}> ; rel="${link.rel}"` + (isNotEmpty(link.type) ? ` ; type="${link.type}" ` : ' ');
12689
});
127-
12890
this.responseService.setHeader('Link', links);
12991
});
13092
});
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
import { TestBed, waitForAsync } from '@angular/core/testing';
2+
import { Router } from '@angular/router';
3+
import { of as observableOf } from 'rxjs';
4+
import { AuthService } from '../core/auth/auth.service';
5+
import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service';
6+
import { FileService } from '../core/shared/file.service';
7+
import { HardRedirectService } from '../core/services/hard-redirect.service';
8+
import { Bitstream } from '../core/shared/bitstream.model';
9+
import { getForbiddenRoute } from '../app-routing-paths';
10+
import { SignpostingDataService } from '../core/data/signposting-data.service';
11+
import { ServerResponseService } from '../core/services/server-response.service';
12+
import { PLATFORM_ID } from '@angular/core';
13+
import { NativeWindowRef, NativeWindowService } from '../core/services/window.service';
14+
import { bitstreamDownloadRedirectGuard } from './bitstream-download-redirect.guard';
15+
import { ObjectCacheService } from '../core/cache/object-cache.service';
16+
import { UUIDService } from '../core/shared/uuid.service';
17+
import { Store } from '@ngrx/store';
18+
import { RemoteDataBuildService } from '../core/cache/builders/remote-data-build.service';
19+
import { HALEndpointService } from '../core/shared/hal-endpoint.service';
20+
import { DSOChangeAnalyzer } from '../core/data/dso-change-analyzer.service';
21+
import { BitstreamFormatDataService } from '../core/data/bitstream-format-data.service';
22+
import { NotificationsService } from '../shared/notifications/notifications.service';
23+
import { BitstreamDataService } from '../core/data/bitstream-data.service';
24+
import { createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils';
25+
26+
describe('BitstreamDownloadRedirectGuard', () => {
27+
let resolver: any;
28+
29+
let authService: AuthService;
30+
let authorizationService: AuthorizationDataService;
31+
let bitstreamDataService: BitstreamDataService;
32+
let fileService: FileService;
33+
let halEndpointService: HALEndpointService;
34+
let hardRedirectService: HardRedirectService;
35+
let remoteDataBuildService: RemoteDataBuildService;
36+
let uuidService: UUIDService;
37+
let objectCacheService: ObjectCacheService;
38+
let router: Router;
39+
let store: Store;
40+
let bitstream: Bitstream;
41+
let serverResponseService: jasmine.SpyObj<ServerResponseService>;
42+
let signpostingDataService: jasmine.SpyObj<SignpostingDataService>;
43+
44+
let route = {
45+
params: {},
46+
queryParams: {}
47+
};
48+
let state = {};
49+
50+
const mocklink = {
51+
href: 'http://test.org',
52+
rel: 'test',
53+
type: 'test'
54+
};
55+
56+
const mocklink2 = {
57+
href: 'http://test2.org',
58+
rel: 'test',
59+
type: 'test'
60+
};
61+
62+
function init() {
63+
authService = jasmine.createSpyObj('authService', {
64+
isAuthenticated: observableOf(true),
65+
setRedirectUrl: {}
66+
});
67+
authorizationService = jasmine.createSpyObj('authorizationSerivice', {
68+
isAuthorized: observableOf(true)
69+
});
70+
71+
fileService = jasmine.createSpyObj('fileService', {
72+
retrieveFileDownloadLink: observableOf('content-url-with-headers')
73+
});
74+
75+
hardRedirectService = jasmine.createSpyObj('fileService', {
76+
redirect: {}
77+
});
78+
79+
halEndpointService = jasmine.createSpyObj('halEndpointService', {
80+
getEndpoint: observableOf('https://rest.api/core')
81+
});
82+
83+
remoteDataBuildService = jasmine.createSpyObj('remoteDataBuildService', {
84+
buildSingle: observableOf(new Bitstream())
85+
});
86+
87+
uuidService = jasmine.createSpyObj('uuidService', {
88+
generate: 'test-id'
89+
});
90+
91+
bitstream = Object.assign(new Bitstream(), {
92+
uuid: 'bitstreamUuid',
93+
_links: {
94+
content: {href: 'bitstream-content-link'},
95+
self: {href: 'bitstream-self-link'},
96+
}
97+
});
98+
99+
router = jasmine.createSpyObj('router', ['navigateByUrl', 'createUrlTree']);
100+
101+
store = jasmine.createSpyObj('store', {
102+
dispatch: {},
103+
pipe: observableOf(true)
104+
});
105+
106+
serverResponseService = jasmine.createSpyObj('ServerResponseService', {
107+
setHeader: jasmine.createSpy('setHeader'),
108+
});
109+
110+
signpostingDataService = jasmine.createSpyObj('SignpostingDataService', {
111+
getLinks: observableOf([mocklink, mocklink2])
112+
});
113+
114+
objectCacheService = jasmine.createSpyObj('objectCacheService', {
115+
getByHref: observableOf(null)
116+
});
117+
118+
bitstreamDataService = jasmine.createSpyObj('bitstreamDataService', {
119+
findById: createSuccessfulRemoteDataObject$(Object.assign(new Bitstream(), {
120+
_links: {
121+
content: {href: 'bitstream-content-link'},
122+
self: {href: 'bitstream-self-link'},
123+
},
124+
}))
125+
});
126+
127+
resolver = bitstreamDownloadRedirectGuard;
128+
}
129+
130+
function initTestbed() {
131+
TestBed.configureTestingModule({
132+
providers: [
133+
{provide: NativeWindowService, useValue: new NativeWindowRef()},
134+
{provide: Router, useValue: router},
135+
{provide: AuthorizationDataService, useValue: authorizationService},
136+
{provide: AuthService, useValue: authService},
137+
{provide: FileService, useValue: fileService},
138+
{provide: HardRedirectService, useValue: hardRedirectService},
139+
{provide: ServerResponseService, useValue: serverResponseService},
140+
{provide: SignpostingDataService, useValue: signpostingDataService},
141+
{provide: ObjectCacheService, useValue: objectCacheService},
142+
{provide: PLATFORM_ID, useValue: 'server'},
143+
{provide: UUIDService, useValue: uuidService},
144+
{provide: Store, useValue: store},
145+
{provide: RemoteDataBuildService, useValue: remoteDataBuildService},
146+
{provide: HALEndpointService, useValue: halEndpointService},
147+
{provide: DSOChangeAnalyzer, useValue: {}},
148+
{provide: BitstreamFormatDataService, useValue: {}},
149+
{provide: NotificationsService, useValue: {}},
150+
{provide: BitstreamDataService, useValue: bitstreamDataService},
151+
]
152+
});
153+
}
154+
155+
describe('bitstream retrieval', () => {
156+
describe('when the user is authorized and not logged in', () => {
157+
beforeEach(() => {
158+
init();
159+
(authService.isAuthenticated as jasmine.Spy).and.returnValue(observableOf(false));
160+
initTestbed();
161+
});
162+
it('should redirect to the content link', waitForAsync(() => {
163+
TestBed.runInInjectionContext(() => {
164+
resolver(route, state).subscribe(() => {
165+
expect(hardRedirectService.redirect).toHaveBeenCalledWith('bitstream-content-link');
166+
}
167+
);
168+
});
169+
}));
170+
});
171+
describe('when the user is authorized and logged in', () => {
172+
beforeEach(() => {
173+
init();
174+
initTestbed();
175+
});
176+
it('should redirect to an updated content link', waitForAsync(() => {
177+
TestBed.runInInjectionContext(() => {
178+
resolver(route, state).subscribe(() => {
179+
expect(hardRedirectService.redirect).toHaveBeenCalledWith('content-url-with-headers');
180+
});
181+
});
182+
}));
183+
});
184+
describe('when the user is not authorized and logged in', () => {
185+
beforeEach(() => {
186+
init();
187+
(authorizationService.isAuthorized as jasmine.Spy).and.returnValue(observableOf(false));
188+
initTestbed();
189+
});
190+
it('should navigate to the forbidden route', waitForAsync(() => {
191+
TestBed.runInInjectionContext(() => {
192+
resolver(route, state).subscribe(() => {
193+
expect(router.createUrlTree).toHaveBeenCalledWith([getForbiddenRoute()]);
194+
});
195+
});
196+
}));
197+
});
198+
describe('when the user is not authorized and not logged in', () => {
199+
beforeEach(() => {
200+
init();
201+
(authService.isAuthenticated as jasmine.Spy).and.returnValue(observableOf(false));
202+
(authorizationService.isAuthorized as jasmine.Spy).and.returnValue(observableOf(false));
203+
initTestbed();
204+
});
205+
it('should navigate to the login page', waitForAsync(() => {
206+
207+
TestBed.runInInjectionContext(() => {
208+
resolver(route, state).subscribe(() => {
209+
expect(authService.setRedirectUrl).toHaveBeenCalled();
210+
expect(router.createUrlTree).toHaveBeenCalledWith(['login']);
211+
});
212+
});
213+
}));
214+
});
215+
});
216+
});

0 commit comments

Comments
 (0)