Skip to content

Commit d4a5308

Browse files
committed
Refactor to use HardRedirectService. Update its redirect method to support optional statusCode
1 parent 0fe33ee commit d4a5308

5 files changed

Lines changed: 44 additions & 57 deletions

File tree

src/app/core/data/dso-redirect.service.spec.ts

Lines changed: 12 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,15 @@ import { RequestService } from './request.service';
1010
import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils';
1111
import { Item } from '../shared/item.model';
1212
import { EMBED_SEPARATOR } from './base/base-data.service';
13+
import { HardRedirectService } from '../services/hard-redirect.service';
1314

1415
describe('DsoRedirectService', () => {
1516
let scheduler: TestScheduler;
1617
let service: DsoRedirectService;
1718
let halService: HALEndpointService;
1819
let requestService: RequestService;
1920
let rdbService: RemoteDataBuildService;
20-
let router;
21+
let redirectService: HardRedirectService;
2122
let remoteData;
2223
const dsoUUID = '9b4f22f4-164a-49db-8817-3316b6ee5746';
2324
const dsoHandle = '1234567789/22';
@@ -27,9 +28,6 @@ describe('DsoRedirectService', () => {
2728
const requestUUIDURL = `https://rest.api/rest/api/pid/find?id=${dsoUUID}`;
2829
const requestUUID = '34cfed7c-f597-49ef-9cbe-ea351f0023c2';
2930
const objectCache = {} as ObjectCacheService;
30-
const mockResponse = jasmine.createSpyObj(['redirect']);
31-
const mockPlatformBrowser = 'browser';
32-
const mockPlatformServer = 'server';
3331

3432
beforeEach(() => {
3533
scheduler = getTestScheduler();
@@ -41,9 +39,6 @@ describe('DsoRedirectService', () => {
4139
generateRequestId: requestUUID,
4240
send: true
4341
});
44-
router = {
45-
navigate: jasmine.createSpy('navigate')
46-
};
4742

4843
remoteData = createSuccessfulRemoteDataObject(Object.assign(new Item(), {
4944
type: 'item',
@@ -55,14 +50,17 @@ describe('DsoRedirectService', () => {
5550
a: remoteData
5651
})
5752
});
53+
54+
redirectService = jasmine.createSpyObj('redirectService', {
55+
redirect: {}
56+
});
57+
5858
service = new DsoRedirectService(
5959
requestService,
6060
rdbService,
6161
objectCache,
6262
halService,
63-
router,
64-
mockResponse,
65-
mockPlatformBrowser // default to CSR except where explicitly SSR below
63+
redirectService
6664
);
6765
});
6866

@@ -109,7 +107,7 @@ describe('DsoRedirectService', () => {
109107
redir.subscribe();
110108
scheduler.schedule(() => redir);
111109
scheduler.flush();
112-
expect(router.navigate).toHaveBeenCalledWith(['/items/' + remoteData.payload.uuid]);
110+
expect(redirectService.redirect).toHaveBeenCalledWith('/items/' + remoteData.payload.uuid, 301);
113111
});
114112
it('should navigate to entities route with the corresponding entity type', () => {
115113
remoteData.payload.type = 'item';
@@ -126,7 +124,7 @@ describe('DsoRedirectService', () => {
126124
redir.subscribe();
127125
scheduler.schedule(() => redir);
128126
scheduler.flush();
129-
expect(router.navigate).toHaveBeenCalledWith(['/entities/publication/' + remoteData.payload.uuid]);
127+
expect(redirectService.redirect).toHaveBeenCalledWith('/entities/publication/' + remoteData.payload.uuid, 301);
130128
});
131129

132130
it('should navigate to collections route', () => {
@@ -135,7 +133,7 @@ describe('DsoRedirectService', () => {
135133
redir.subscribe();
136134
scheduler.schedule(() => redir);
137135
scheduler.flush();
138-
expect(router.navigate).toHaveBeenCalledWith(['/collections/' + remoteData.payload.uuid]);
136+
expect(redirectService.redirect).toHaveBeenCalledWith('/collections/' + remoteData.payload.uuid, 301);
139137
});
140138

141139
it('should navigate to communities route', () => {
@@ -144,26 +142,7 @@ describe('DsoRedirectService', () => {
144142
redir.subscribe();
145143
scheduler.schedule(() => redir);
146144
scheduler.flush();
147-
expect(router.navigate).toHaveBeenCalledWith(['/communities/' + remoteData.payload.uuid]);
148-
});
149-
150-
it('should return 301 redirect when SSR is used', () => {
151-
service = new DsoRedirectService(
152-
requestService,
153-
rdbService,
154-
objectCache,
155-
halService,
156-
router,
157-
mockResponse,
158-
mockPlatformServer // explicitly SSR mode
159-
);
160-
remoteData.payload.type = 'item';
161-
const redir = service.findByIdAndIDType(dsoHandle, IdentifierType.HANDLE);
162-
// The framework would normally subscribe but do it here so we can test navigation.
163-
redir.subscribe();
164-
scheduler.schedule(() => redir);
165-
scheduler.flush();
166-
expect(mockResponse.redirect).toHaveBeenCalledWith(301, '/items/' + remoteData.payload.uuid);
145+
expect(redirectService.redirect).toHaveBeenCalledWith('/communities/' + remoteData.payload.uuid, 301);
167146
});
168147
});
169148

src/app/core/data/dso-redirect.service.ts

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66
* http://www.dspace.org/license/
77
*/
88
/* eslint-disable max-classes-per-file */
9-
import { Inject, Injectable, Optional, PLATFORM_ID } from '@angular/core';
10-
import { Router } from '@angular/router';
9+
import { Injectable } from '@angular/core';
1110
import { Observable } from 'rxjs';
1211
import { tap } from 'rxjs/operators';
1312
import { hasValue } from '../../shared/empty.util';
@@ -21,8 +20,7 @@ import { getFirstCompletedRemoteData } from '../shared/operators';
2120
import { DSpaceObject } from '../shared/dspace-object.model';
2221
import { IdentifiableDataService } from './base/identifiable-data.service';
2322
import { getDSORoute } from '../../app-routing-paths';
24-
import { RESPONSE } from '@nguniversal/express-engine/tokens';
25-
import { isPlatformServer } from '@angular/common';
23+
import { HardRedirectService } from '../services/hard-redirect.service';
2624

2725
const ID_ENDPOINT = 'pid';
2826
const UUID_ENDPOINT = 'dso';
@@ -76,9 +74,7 @@ export class DsoRedirectService {
7674
protected rdbService: RemoteDataBuildService,
7775
protected objectCache: ObjectCacheService,
7876
protected halService: HALEndpointService,
79-
private router: Router,
80-
@Optional() @Inject(RESPONSE) private response: any,
81-
@Inject(PLATFORM_ID) private platformId: any
77+
private hardRedirectService: HardRedirectService
8278
) {
8379
this.dataService = new DsoByIdOrUUIDDataService(requestService, rdbService, objectCache, halService);
8480
}
@@ -88,9 +84,6 @@ export class DsoRedirectService {
8884
* This is used to redirect paths like "/handle/[prefix]/[suffix]" to the object's path (e.g. /items/[uuid]).
8985
* See LookupGuard for more examples.
9086
*
91-
* If this is called server side (via SSR), it performs a 301 Redirect.
92-
* If this is called client side (via CSR), it simply uses the Angular router to do the redirect.
93-
*
9487
* @param id the identifier of the object to retrieve
9588
* @param identifierType the type of the given identifier (defaults to UUID)
9689
*/
@@ -104,12 +97,8 @@ export class DsoRedirectService {
10497
if (hasValue(dso.uuid)) {
10598
let newRoute = getDSORoute(dso);
10699
if (hasValue(newRoute)) {
107-
// If running via SSR, perform a "301 Moved Permanently" redirect for SEO purposes.
108-
if (isPlatformServer(this.platformId)) {
109-
this.response.redirect(301, newRoute);
110-
} else {
111-
this.router.navigate([newRoute]);
112-
}
100+
// Use a "301 Moved Permanently" redirect for SEO purposes
101+
this.hardRedirectService.redirect(newRoute, 301);
113102
}
114103
}
115104
}

src/app/core/services/hard-redirect.service.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ export abstract class HardRedirectService {
1111
*
1212
* @param url
1313
* the page to redirect to
14+
* @param statusCode
15+
* optional HTTP status code to use for redirect (default = 302, which is a temporary redirect)
1416
*/
15-
abstract redirect(url: string);
17+
abstract redirect(url: string, statusCode?: number);
1618

1719
/**
1820
* Get the current route, with query params included

src/app/core/services/server-hard-redirect.service.spec.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,33 @@ describe('ServerHardRedirectService', () => {
2222
expect(service).toBeTruthy();
2323
});
2424

25-
describe('when performing a redirect', () => {
26-
25+
describe('when performing a default redirect', () => {
2726
const redirect = 'test redirect';
2827

2928
beforeEach(() => {
3029
service.redirect(redirect);
3130
});
3231

33-
it('should update the response object', () => {
32+
it('should perform a 302 redirect', () => {
3433
expect(mockResponse.redirect).toHaveBeenCalledWith(302, redirect);
3534
expect(mockResponse.end).toHaveBeenCalled();
3635
});
3736
});
3837

38+
describe('when performing a 301 redirect', () => {
39+
const redirect = 'test 301 redirect';
40+
const redirectStatusCode = 301;
41+
42+
beforeEach(() => {
43+
service.redirect(redirect, redirectStatusCode);
44+
});
45+
46+
it('should redirect with passed in status code', () => {
47+
expect(mockResponse.redirect).toHaveBeenCalledWith(redirectStatusCode, redirect);
48+
expect(mockResponse.end).toHaveBeenCalled();
49+
});
50+
});
51+
3952
describe('when requesting the current route', () => {
4053

4154
beforeEach(() => {

src/app/core/services/server-hard-redirect.service.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,14 @@ export class ServerHardRedirectService extends HardRedirectService {
1717
}
1818

1919
/**
20-
* Perform a hard redirect to URL
20+
* Perform a hard redirect to a given location.
21+
*
2122
* @param url
23+
* the page to redirect to
24+
* @param statusCode
25+
* optional HTTP status code to use for redirect (default = 302, which is a temporary redirect)
2226
*/
23-
redirect(url: string) {
27+
redirect(url: string, statusCode?: number) {
2428

2529
if (url === this.req.url) {
2630
return;
@@ -38,8 +42,8 @@ export class ServerHardRedirectService extends HardRedirectService {
3842
process.exit(1);
3943
}
4044
} else {
41-
// attempt to use the already set status
42-
let status = this.res.statusCode || 0;
45+
// attempt to use passed in statusCode or the already set status (in request)
46+
let status = statusCode || this.res.statusCode || 0;
4347
if (status < 300 || status >= 400) {
4448
// temporary redirect
4549
status = 302;

0 commit comments

Comments
 (0)