mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 18:14:17 +00:00
Merge pull request #2248 from 4Science/feature/CST-5729
Implement Signposting on angular side
This commit is contained in:
12
server.ts
12
server.ts
@@ -26,7 +26,6 @@ import * as ejs from 'ejs';
|
|||||||
import * as compression from 'compression';
|
import * as compression from 'compression';
|
||||||
import * as expressStaticGzip from 'express-static-gzip';
|
import * as expressStaticGzip from 'express-static-gzip';
|
||||||
/* eslint-enable import/no-namespace */
|
/* eslint-enable import/no-namespace */
|
||||||
|
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import LRU from 'lru-cache';
|
import LRU from 'lru-cache';
|
||||||
import isbot from 'isbot';
|
import isbot from 'isbot';
|
||||||
@@ -34,7 +33,7 @@ import { createCertificate } from 'pem';
|
|||||||
import { createServer } from 'https';
|
import { createServer } from 'https';
|
||||||
import { json } from 'body-parser';
|
import { json } from 'body-parser';
|
||||||
|
|
||||||
import { existsSync, readFileSync } from 'fs';
|
import { readFileSync } from 'fs';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
|
|
||||||
import { enableProdMode } from '@angular/core';
|
import { enableProdMode } from '@angular/core';
|
||||||
@@ -180,6 +179,15 @@ export function app() {
|
|||||||
changeOrigin: true
|
changeOrigin: true
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxy the linksets
|
||||||
|
*/
|
||||||
|
router.use('/signposting**', createProxyMiddleware({
|
||||||
|
target: `${environment.rest.baseUrl}`,
|
||||||
|
pathRewrite: path => path.replace(environment.ui.nameSpace, '/'),
|
||||||
|
changeOrigin: true
|
||||||
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the rateLimiter property is present
|
* Checks if the rateLimiter property is present
|
||||||
* When it is present, the rateLimiter will be enabled. When it is undefined, the rateLimiter will be disabled.
|
* When it is present, the rateLimiter will be enabled. When it is undefined, the rateLimiter will be disabled.
|
||||||
|
@@ -11,6 +11,9 @@ import { ActivatedRoute, Router } from '@angular/router';
|
|||||||
import { getForbiddenRoute } from '../../app-routing-paths';
|
import { getForbiddenRoute } from '../../app-routing-paths';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { SignpostingDataService } from '../../core/data/signposting-data.service';
|
||||||
|
import { ServerResponseService } from '../../core/services/server-response.service';
|
||||||
|
import { PLATFORM_ID } from '@angular/core';
|
||||||
|
|
||||||
describe('BitstreamDownloadPageComponent', () => {
|
describe('BitstreamDownloadPageComponent', () => {
|
||||||
let component: BitstreamDownloadPageComponent;
|
let component: BitstreamDownloadPageComponent;
|
||||||
@@ -24,6 +27,20 @@ describe('BitstreamDownloadPageComponent', () => {
|
|||||||
let router;
|
let router;
|
||||||
|
|
||||||
let bitstream: Bitstream;
|
let bitstream: Bitstream;
|
||||||
|
let serverResponseService: jasmine.SpyObj<ServerResponseService>;
|
||||||
|
let signpostingDataService: jasmine.SpyObj<SignpostingDataService>;
|
||||||
|
|
||||||
|
const mocklink = {
|
||||||
|
href: 'http://test.org',
|
||||||
|
rel: 'test',
|
||||||
|
type: 'test'
|
||||||
|
};
|
||||||
|
|
||||||
|
const mocklink2 = {
|
||||||
|
href: 'http://test2.org',
|
||||||
|
rel: 'test',
|
||||||
|
type: 'test'
|
||||||
|
};
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
authService = jasmine.createSpyObj('authService', {
|
authService = jasmine.createSpyObj('authService', {
|
||||||
@@ -44,8 +61,8 @@ describe('BitstreamDownloadPageComponent', () => {
|
|||||||
bitstream = Object.assign(new Bitstream(), {
|
bitstream = Object.assign(new Bitstream(), {
|
||||||
uuid: 'bitstreamUuid',
|
uuid: 'bitstreamUuid',
|
||||||
_links: {
|
_links: {
|
||||||
content: {href: 'bitstream-content-link'},
|
content: { href: 'bitstream-content-link' },
|
||||||
self: {href: 'bitstream-self-link'},
|
self: { href: 'bitstream-self-link' },
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -54,10 +71,21 @@ describe('BitstreamDownloadPageComponent', () => {
|
|||||||
bitstream: createSuccessfulRemoteDataObject(
|
bitstream: createSuccessfulRemoteDataObject(
|
||||||
bitstream
|
bitstream
|
||||||
)
|
)
|
||||||
|
}),
|
||||||
|
params: observableOf({
|
||||||
|
id: 'testid'
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
router = jasmine.createSpyObj('router', ['navigateByUrl']);
|
router = jasmine.createSpyObj('router', ['navigateByUrl']);
|
||||||
|
|
||||||
|
serverResponseService = jasmine.createSpyObj('ServerResponseService', {
|
||||||
|
setHeader: jasmine.createSpy('setHeader'),
|
||||||
|
});
|
||||||
|
|
||||||
|
signpostingDataService = jasmine.createSpyObj('SignpostingDataService', {
|
||||||
|
getLinks: observableOf([mocklink, mocklink2])
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function initTestbed() {
|
function initTestbed() {
|
||||||
@@ -65,12 +93,15 @@ describe('BitstreamDownloadPageComponent', () => {
|
|||||||
imports: [CommonModule, TranslateModule.forRoot()],
|
imports: [CommonModule, TranslateModule.forRoot()],
|
||||||
declarations: [BitstreamDownloadPageComponent],
|
declarations: [BitstreamDownloadPageComponent],
|
||||||
providers: [
|
providers: [
|
||||||
{provide: ActivatedRoute, useValue: activatedRoute},
|
{ provide: ActivatedRoute, useValue: activatedRoute },
|
||||||
{provide: Router, useValue: router},
|
{ provide: Router, useValue: router },
|
||||||
{provide: AuthorizationDataService, useValue: authorizationService},
|
{ provide: AuthorizationDataService, useValue: authorizationService },
|
||||||
{provide: AuthService, useValue: authService},
|
{ provide: AuthService, useValue: authService },
|
||||||
{provide: FileService, useValue: fileService},
|
{ provide: FileService, useValue: fileService },
|
||||||
{provide: HardRedirectService, useValue: hardRedirectService},
|
{ provide: HardRedirectService, useValue: hardRedirectService },
|
||||||
|
{ provide: ServerResponseService, useValue: serverResponseService },
|
||||||
|
{ provide: SignpostingDataService, useValue: signpostingDataService },
|
||||||
|
{ provide: PLATFORM_ID, useValue: 'server' }
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
@@ -107,6 +138,9 @@ describe('BitstreamDownloadPageComponent', () => {
|
|||||||
it('should redirect to the content link', () => {
|
it('should redirect to the content link', () => {
|
||||||
expect(hardRedirectService.redirect).toHaveBeenCalledWith('bitstream-content-link');
|
expect(hardRedirectService.redirect).toHaveBeenCalledWith('bitstream-content-link');
|
||||||
});
|
});
|
||||||
|
it('should add the signposting links', () => {
|
||||||
|
expect(serverResponseService.setHeader).toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
describe('when the user is authorized and logged in', () => {
|
describe('when the user is authorized and logged in', () => {
|
||||||
beforeEach(waitForAsync(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
@@ -134,7 +168,7 @@ describe('BitstreamDownloadPageComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
it('should navigate to the forbidden route', () => {
|
it('should navigate to the forbidden route', () => {
|
||||||
expect(router.navigateByUrl).toHaveBeenCalledWith(getForbiddenRoute(), {skipLocationChange: true});
|
expect(router.navigateByUrl).toHaveBeenCalledWith(getForbiddenRoute(), { skipLocationChange: true });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('when the user is not authorized and not logged in', () => {
|
describe('when the user is not authorized and not logged in', () => {
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, Inject, OnInit, PLATFORM_ID } from '@angular/core';
|
||||||
import { filter, map, switchMap, take } from 'rxjs/operators';
|
import { filter, map, switchMap, take } from 'rxjs/operators';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { hasValue, isNotEmpty } from '../../shared/empty.util';
|
import { hasValue, isNotEmpty } from '../../shared/empty.util';
|
||||||
import { getRemoteDataPayload} from '../../core/shared/operators';
|
import { getRemoteDataPayload } from '../../core/shared/operators';
|
||||||
import { Bitstream } from '../../core/shared/bitstream.model';
|
import { Bitstream } from '../../core/shared/bitstream.model';
|
||||||
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
|
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
|
||||||
import { FeatureID } from '../../core/data/feature-authorization/feature-id';
|
import { FeatureID } from '../../core/data/feature-authorization/feature-id';
|
||||||
@@ -13,8 +13,11 @@ import { HardRedirectService } from '../../core/services/hard-redirect.service';
|
|||||||
import { getForbiddenRoute } from '../../app-routing-paths';
|
import { getForbiddenRoute } from '../../app-routing-paths';
|
||||||
import { RemoteData } from '../../core/data/remote-data';
|
import { RemoteData } from '../../core/data/remote-data';
|
||||||
import { redirectOn4xx } from '../../core/shared/authorized.operators';
|
import { redirectOn4xx } from '../../core/shared/authorized.operators';
|
||||||
import { Location } from '@angular/common';
|
import { isPlatformServer, Location } from '@angular/common';
|
||||||
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
|
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
|
||||||
|
import { SignpostingDataService } from '../../core/data/signposting-data.service';
|
||||||
|
import { ServerResponseService } from '../../core/services/server-response.service';
|
||||||
|
import { SignpostingLink } from '../../core/data/signposting-links.model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-bitstream-download-page',
|
selector: 'ds-bitstream-download-page',
|
||||||
@@ -28,7 +31,6 @@ export class BitstreamDownloadPageComponent implements OnInit {
|
|||||||
bitstream$: Observable<Bitstream>;
|
bitstream$: Observable<Bitstream>;
|
||||||
bitstreamRD$: Observable<RemoteData<Bitstream>>;
|
bitstreamRD$: Observable<RemoteData<Bitstream>>;
|
||||||
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
@@ -38,8 +40,11 @@ export class BitstreamDownloadPageComponent implements OnInit {
|
|||||||
private hardRedirectService: HardRedirectService,
|
private hardRedirectService: HardRedirectService,
|
||||||
private location: Location,
|
private location: Location,
|
||||||
public dsoNameService: DSONameService,
|
public dsoNameService: DSONameService,
|
||||||
|
private signpostingDataService: SignpostingDataService,
|
||||||
|
private responseService: ServerResponseService,
|
||||||
|
@Inject(PLATFORM_ID) protected platformId: string
|
||||||
) {
|
) {
|
||||||
|
this.initPageLinks();
|
||||||
}
|
}
|
||||||
|
|
||||||
back(): void {
|
back(): void {
|
||||||
@@ -89,4 +94,26 @@ export class BitstreamDownloadPageComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create page links if any are retrieved by signposting endpoint
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private initPageLinks(): void {
|
||||||
|
if (isPlatformServer(this.platformId)) {
|
||||||
|
this.route.params.subscribe(params => {
|
||||||
|
this.signpostingDataService.getLinks(params.id).pipe(take(1)).subscribe((signpostingLinks: SignpostingLink[]) => {
|
||||||
|
let links = '';
|
||||||
|
|
||||||
|
signpostingLinks.forEach((link: SignpostingLink) => {
|
||||||
|
links = links + (isNotEmpty(links) ? ', ' : '') + `<${link.href}> ; rel="${link.rel}"` + (isNotEmpty(link.type) ? ` ; type="${link.type}" ` : ' ');
|
||||||
|
links = links + (isNotEmpty(links) ? ', ' : '') + `<${link.href}> ; rel="${link.rel}" ; type="${link.type}" `;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.responseService.setHeader('Link', links);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
97
src/app/core/data/signposting-data.service.spec.ts
Normal file
97
src/app/core/data/signposting-data.service.spec.ts
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
import { fakeAsync, TestBed, tick } from '@angular/core/testing';
|
||||||
|
import { SignpostingDataService } from './signposting-data.service';
|
||||||
|
import { DspaceRestService } from '../dspace-rest/dspace-rest.service';
|
||||||
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
|
import { of } from 'rxjs';
|
||||||
|
import { SignpostingLink } from './signposting-links.model';
|
||||||
|
|
||||||
|
describe('SignpostingDataService', () => {
|
||||||
|
let service: SignpostingDataService;
|
||||||
|
let restServiceSpy: jasmine.SpyObj<DspaceRestService>;
|
||||||
|
let halServiceSpy: jasmine.SpyObj<HALEndpointService>;
|
||||||
|
const mocklink = {
|
||||||
|
href: 'http://test.org',
|
||||||
|
rel: 'test',
|
||||||
|
type: 'test'
|
||||||
|
};
|
||||||
|
|
||||||
|
const mocklink2 = {
|
||||||
|
href: 'http://test2.org',
|
||||||
|
rel: 'test',
|
||||||
|
type: 'test'
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockResponse: any = {
|
||||||
|
statusCode: 200,
|
||||||
|
payload: [mocklink, mocklink2]
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockErrResponse: any = {
|
||||||
|
statusCode: 500
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
const restSpy = jasmine.createSpyObj('DspaceRestService', ['get', 'getWithHeaders']);
|
||||||
|
const halSpy = jasmine.createSpyObj('HALEndpointService', ['getRootHref']);
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [
|
||||||
|
SignpostingDataService,
|
||||||
|
{ provide: DspaceRestService, useValue: restSpy },
|
||||||
|
{ provide: HALEndpointService, useValue: halSpy }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
service = TestBed.inject(SignpostingDataService);
|
||||||
|
restServiceSpy = TestBed.inject(DspaceRestService) as jasmine.SpyObj<DspaceRestService>;
|
||||||
|
halServiceSpy = TestBed.inject(HALEndpointService) as jasmine.SpyObj<HALEndpointService>;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return signposting links', fakeAsync(() => {
|
||||||
|
const uuid = '123';
|
||||||
|
const baseUrl = 'http://localhost:8080';
|
||||||
|
|
||||||
|
halServiceSpy.getRootHref.and.returnValue(`${baseUrl}/api`);
|
||||||
|
|
||||||
|
restServiceSpy.get.and.returnValue(of(mockResponse));
|
||||||
|
|
||||||
|
let result: SignpostingLink[];
|
||||||
|
|
||||||
|
const expectedResult: SignpostingLink[] = [mocklink, mocklink2];
|
||||||
|
|
||||||
|
service.getLinks(uuid).subscribe((links) => {
|
||||||
|
result = links;
|
||||||
|
});
|
||||||
|
|
||||||
|
tick();
|
||||||
|
|
||||||
|
expect(result).toEqual(expectedResult);
|
||||||
|
expect(halServiceSpy.getRootHref).toHaveBeenCalled();
|
||||||
|
expect(restServiceSpy.get).toHaveBeenCalledWith(`${baseUrl}/signposting/links/${uuid}`);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should handle error and return an empty array', fakeAsync(() => {
|
||||||
|
const uuid = '123';
|
||||||
|
const baseUrl = 'http://localhost:8080';
|
||||||
|
|
||||||
|
halServiceSpy.getRootHref.and.returnValue(`${baseUrl}/api`);
|
||||||
|
|
||||||
|
restServiceSpy.get.and.returnValue(of(mockErrResponse));
|
||||||
|
|
||||||
|
let result: any;
|
||||||
|
|
||||||
|
service.getLinks(uuid).subscribe((data) => {
|
||||||
|
result = data;
|
||||||
|
});
|
||||||
|
|
||||||
|
tick();
|
||||||
|
|
||||||
|
expect(result).toEqual([]);
|
||||||
|
expect(halServiceSpy.getRootHref).toHaveBeenCalled();
|
||||||
|
expect(restServiceSpy.get).toHaveBeenCalledWith(`${baseUrl}/signposting/links/${uuid}`);
|
||||||
|
}));
|
||||||
|
});
|
38
src/app/core/data/signposting-data.service.ts
Normal file
38
src/app/core/data/signposting-data.service.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
import { catchError, map } from 'rxjs/operators';
|
||||||
|
import { Observable, of as observableOf } from 'rxjs';
|
||||||
|
|
||||||
|
import { DspaceRestService } from '../dspace-rest/dspace-rest.service';
|
||||||
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
|
import { RawRestResponse } from '../dspace-rest/raw-rest-response.model';
|
||||||
|
import { SignpostingLink } from './signposting-links.model';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service responsible for handling requests related to the Signposting endpoint
|
||||||
|
*/
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class SignpostingDataService {
|
||||||
|
|
||||||
|
constructor(private restService: DspaceRestService, protected halService: HALEndpointService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the list of signposting links related to the given resource's id
|
||||||
|
*
|
||||||
|
* @param uuid
|
||||||
|
*/
|
||||||
|
getLinks(uuid: string): Observable<SignpostingLink[]> {
|
||||||
|
const baseUrl = this.halService.getRootHref().replace('/api', '');
|
||||||
|
|
||||||
|
return this.restService.get(`${baseUrl}/signposting/links/${uuid}`).pipe(
|
||||||
|
catchError((err) => {
|
||||||
|
return observableOf([]);
|
||||||
|
}),
|
||||||
|
map((res: RawRestResponse) => res.statusCode === 200 ? res.payload as SignpostingLink[] : [])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
8
src/app/core/data/signposting-links.model.ts
Normal file
8
src/app/core/data/signposting-links.model.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
/**
|
||||||
|
* Represents the link object received by the signposting endpoint
|
||||||
|
*/
|
||||||
|
export interface SignpostingLink {
|
||||||
|
href?: string,
|
||||||
|
rel?: string,
|
||||||
|
type?: string
|
||||||
|
}
|
@@ -46,6 +46,7 @@ export class ServerHardRedirectService extends HardRedirectService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Redirecting from ${this.req.url} to ${url} with ${status}`);
|
console.log(`Redirecting from ${this.req.url} to ${url} with ${status}`);
|
||||||
|
|
||||||
this.res.redirect(status, url);
|
this.res.redirect(status, url);
|
||||||
this.res.end();
|
this.res.end();
|
||||||
// I haven't found a way to correctly stop Angular rendering.
|
// I haven't found a way to correctly stop Angular rendering.
|
||||||
|
@@ -1,7 +1,11 @@
|
|||||||
import { RESPONSE } from '@nguniversal/express-engine/tokens';
|
import { RESPONSE } from '@nguniversal/express-engine/tokens';
|
||||||
import { Inject, Injectable, Optional } from '@angular/core';
|
import { Inject, Injectable, Optional } from '@angular/core';
|
||||||
|
|
||||||
import { Response } from 'express';
|
import { Response } from 'express';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service responsible to provide method to manage the response object
|
||||||
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ServerResponseService {
|
export class ServerResponseService {
|
||||||
private response: Response;
|
private response: Response;
|
||||||
@@ -10,6 +14,12 @@ export class ServerResponseService {
|
|||||||
this.response = response;
|
this.response = response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a status code to response
|
||||||
|
*
|
||||||
|
* @param code
|
||||||
|
* @param message
|
||||||
|
*/
|
||||||
setStatus(code: number, message?: string): this {
|
setStatus(code: number, message?: string): this {
|
||||||
if (this.response) {
|
if (this.response) {
|
||||||
this.response.statusCode = code;
|
this.response.statusCode = code;
|
||||||
@@ -20,19 +30,51 @@ export class ServerResponseService {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Unauthorized status
|
||||||
|
*
|
||||||
|
* @param message
|
||||||
|
*/
|
||||||
setUnauthorized(message = 'Unauthorized'): this {
|
setUnauthorized(message = 'Unauthorized'): this {
|
||||||
return this.setStatus(401, message);
|
return this.setStatus(401, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Forbidden status
|
||||||
|
*
|
||||||
|
* @param message
|
||||||
|
*/
|
||||||
setForbidden(message = 'Forbidden'): this {
|
setForbidden(message = 'Forbidden'): this {
|
||||||
return this.setStatus(403, message);
|
return this.setStatus(403, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Not found status
|
||||||
|
*
|
||||||
|
* @param message
|
||||||
|
*/
|
||||||
setNotFound(message = 'Not found'): this {
|
setNotFound(message = 'Not found'): this {
|
||||||
return this.setStatus(404, message);
|
return this.setStatus(404, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Internal Server Error status
|
||||||
|
*
|
||||||
|
* @param message
|
||||||
|
*/
|
||||||
setInternalServerError(message = 'Internal Server Error'): this {
|
setInternalServerError(message = 'Internal Server Error'): this {
|
||||||
return this.setStatus(500, message);
|
return this.setStatus(500, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a response's header
|
||||||
|
*
|
||||||
|
* @param header
|
||||||
|
* @param content
|
||||||
|
*/
|
||||||
|
setHeader(header: string, content: string) {
|
||||||
|
if (this.response) {
|
||||||
|
this.response.setHeader(header, content);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,7 +2,7 @@ import { ComponentFixture, fakeAsync, TestBed, waitForAsync } from '@angular/cor
|
|||||||
import { ItemDataService } from '../../core/data/item-data.service';
|
import { ItemDataService } from '../../core/data/item-data.service';
|
||||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||||
import { TranslateLoaderMock } from '../../shared/mocks/translate-loader.mock';
|
import { TranslateLoaderMock } from '../../shared/mocks/translate-loader.mock';
|
||||||
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA, PLATFORM_ID } from '@angular/core';
|
||||||
import { TruncatePipe } from '../../shared/utils/truncate.pipe';
|
import { TruncatePipe } from '../../shared/utils/truncate.pipe';
|
||||||
import { FullItemPageComponent } from './full-item-page.component';
|
import { FullItemPageComponent } from './full-item-page.component';
|
||||||
import { MetadataService } from '../../core/metadata/metadata.service';
|
import { MetadataService } from '../../core/metadata/metadata.service';
|
||||||
@@ -20,6 +20,9 @@ import { createPaginatedList } from '../../shared/testing/utils.test';
|
|||||||
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
|
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
|
||||||
import { createRelationshipsObservable } from '../simple/item-types/shared/item.component.spec';
|
import { createRelationshipsObservable } from '../simple/item-types/shared/item.component.spec';
|
||||||
import { RemoteData } from '../../core/data/remote-data';
|
import { RemoteData } from '../../core/data/remote-data';
|
||||||
|
import { ServerResponseService } from '../../core/services/server-response.service';
|
||||||
|
import { SignpostingDataService } from '../../core/data/signposting-data.service';
|
||||||
|
import { LinkHeadService } from '../../core/services/link-head.service';
|
||||||
|
|
||||||
const mockItem: Item = Object.assign(new Item(), {
|
const mockItem: Item = Object.assign(new Item(), {
|
||||||
bundles: createSuccessfulRemoteDataObject$(createPaginatedList([])),
|
bundles: createSuccessfulRemoteDataObject$(createPaginatedList([])),
|
||||||
@@ -55,8 +58,21 @@ describe('FullItemPageComponent', () => {
|
|||||||
let routeStub: ActivatedRouteStub;
|
let routeStub: ActivatedRouteStub;
|
||||||
let routeData;
|
let routeData;
|
||||||
let authorizationDataService: AuthorizationDataService;
|
let authorizationDataService: AuthorizationDataService;
|
||||||
|
let serverResponseService: jasmine.SpyObj<ServerResponseService>;
|
||||||
|
let signpostingDataService: jasmine.SpyObj<SignpostingDataService>;
|
||||||
|
let linkHeadService: jasmine.SpyObj<LinkHeadService>;
|
||||||
|
|
||||||
|
const mocklink = {
|
||||||
|
href: 'http://test.org',
|
||||||
|
rel: 'test',
|
||||||
|
type: 'test'
|
||||||
|
};
|
||||||
|
|
||||||
|
const mocklink2 = {
|
||||||
|
href: 'http://test2.org',
|
||||||
|
rel: 'test',
|
||||||
|
type: 'test'
|
||||||
|
};
|
||||||
|
|
||||||
beforeEach(waitForAsync(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
authService = jasmine.createSpyObj('authService', {
|
authService = jasmine.createSpyObj('authService', {
|
||||||
@@ -76,6 +92,19 @@ describe('FullItemPageComponent', () => {
|
|||||||
isAuthorized: observableOf(false),
|
isAuthorized: observableOf(false),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
serverResponseService = jasmine.createSpyObj('ServerResponseService', {
|
||||||
|
setHeader: jasmine.createSpy('setHeader'),
|
||||||
|
});
|
||||||
|
|
||||||
|
signpostingDataService = jasmine.createSpyObj('SignpostingDataService', {
|
||||||
|
getLinks: observableOf([mocklink, mocklink2]),
|
||||||
|
});
|
||||||
|
|
||||||
|
linkHeadService = jasmine.createSpyObj('LinkHeadService', {
|
||||||
|
addTag: jasmine.createSpy('setHeader'),
|
||||||
|
removeTag: jasmine.createSpy('removeTag'),
|
||||||
|
});
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [TranslateModule.forRoot({
|
imports: [TranslateModule.forRoot({
|
||||||
loader: {
|
loader: {
|
||||||
@@ -90,8 +119,11 @@ describe('FullItemPageComponent', () => {
|
|||||||
{ provide: MetadataService, useValue: metadataServiceStub },
|
{ provide: MetadataService, useValue: metadataServiceStub },
|
||||||
{ provide: AuthService, useValue: authService },
|
{ provide: AuthService, useValue: authService },
|
||||||
{ provide: AuthorizationDataService, useValue: authorizationDataService },
|
{ provide: AuthorizationDataService, useValue: authorizationDataService },
|
||||||
|
{ provide: ServerResponseService, useValue: serverResponseService },
|
||||||
|
{ provide: SignpostingDataService, useValue: signpostingDataService },
|
||||||
|
{ provide: LinkHeadService, useValue: linkHeadService },
|
||||||
|
{ provide: PLATFORM_ID, useValue: 'server' }
|
||||||
],
|
],
|
||||||
|
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).overrideComponent(FullItemPageComponent, {
|
}).overrideComponent(FullItemPageComponent, {
|
||||||
set: { changeDetection: ChangeDetectionStrategy.Default }
|
set: { changeDetection: ChangeDetectionStrategy.Default }
|
||||||
@@ -143,6 +175,11 @@ describe('FullItemPageComponent', () => {
|
|||||||
const objectLoader = fixture.debugElement.query(By.css('.full-item-info'));
|
const objectLoader = fixture.debugElement.query(By.css('.full-item-info'));
|
||||||
expect(objectLoader.nativeElement).not.toBeNull();
|
expect(objectLoader.nativeElement).not.toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should add the signposting links', () => {
|
||||||
|
expect(serverResponseService.setHeader).toHaveBeenCalled();
|
||||||
|
expect(linkHeadService.addTag).toHaveBeenCalledTimes(2);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
describe('when the item is withdrawn and the user is not an admin', () => {
|
describe('when the item is withdrawn and the user is not an admin', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -167,6 +204,11 @@ describe('FullItemPageComponent', () => {
|
|||||||
const objectLoader = fixture.debugElement.query(By.css('.full-item-info'));
|
const objectLoader = fixture.debugElement.query(By.css('.full-item-info'));
|
||||||
expect(objectLoader).not.toBeNull();
|
expect(objectLoader).not.toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should add the signposting links', () => {
|
||||||
|
expect(serverResponseService.setHeader).toHaveBeenCalled();
|
||||||
|
expect(linkHeadService.addTag).toHaveBeenCalledTimes(2);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when the item is not withdrawn and the user is not an admin', () => {
|
describe('when the item is not withdrawn and the user is not an admin', () => {
|
||||||
@@ -179,5 +221,10 @@ describe('FullItemPageComponent', () => {
|
|||||||
const objectLoader = fixture.debugElement.query(By.css('.full-item-info'));
|
const objectLoader = fixture.debugElement.query(By.css('.full-item-info'));
|
||||||
expect(objectLoader).not.toBeNull();
|
expect(objectLoader).not.toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should add the signposting links', () => {
|
||||||
|
expect(serverResponseService.setHeader).toHaveBeenCalled();
|
||||||
|
expect(linkHeadService.addTag).toHaveBeenCalledTimes(2);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { filter, map } from 'rxjs/operators';
|
import { filter, map } from 'rxjs/operators';
|
||||||
import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, Inject, OnDestroy, OnInit, PLATFORM_ID } from '@angular/core';
|
||||||
import { ActivatedRoute, Data, Router } from '@angular/router';
|
import { ActivatedRoute, Data, Router } from '@angular/router';
|
||||||
|
|
||||||
import { BehaviorSubject, Observable } from 'rxjs';
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
@@ -16,7 +16,9 @@ import { hasValue } from '../../shared/empty.util';
|
|||||||
import { AuthService } from '../../core/auth/auth.service';
|
import { AuthService } from '../../core/auth/auth.service';
|
||||||
import { Location } from '@angular/common';
|
import { Location } from '@angular/common';
|
||||||
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
|
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
|
||||||
|
import { ServerResponseService } from '../../core/services/server-response.service';
|
||||||
|
import { SignpostingDataService } from '../../core/data/signposting-data.service';
|
||||||
|
import { LinkHeadService } from '../../core/services/link-head.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component renders a full item page.
|
* This component renders a full item page.
|
||||||
@@ -43,13 +45,19 @@ export class FullItemPageComponent extends ItemPageComponent implements OnInit,
|
|||||||
|
|
||||||
subs = [];
|
subs = [];
|
||||||
|
|
||||||
constructor(protected route: ActivatedRoute,
|
constructor(
|
||||||
router: Router,
|
protected route: ActivatedRoute,
|
||||||
items: ItemDataService,
|
protected router: Router,
|
||||||
authService: AuthService,
|
protected items: ItemDataService,
|
||||||
authorizationService: AuthorizationDataService,
|
protected authService: AuthService,
|
||||||
private _location: Location) {
|
protected authorizationService: AuthorizationDataService,
|
||||||
super(route, router, items, authService, authorizationService);
|
protected _location: Location,
|
||||||
|
protected responseService: ServerResponseService,
|
||||||
|
protected signpostingDataService: SignpostingDataService,
|
||||||
|
protected linkHeadService: LinkHeadService,
|
||||||
|
@Inject(PLATFORM_ID) protected platformId: string,
|
||||||
|
) {
|
||||||
|
super(route, router, items, authService, authorizationService, responseService, signpostingDataService, linkHeadService, platformId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*** AoT inheritance fix, will hopefully be resolved in the near future **/
|
/*** AoT inheritance fix, will hopefully be resolved in the near future **/
|
||||||
|
@@ -2,7 +2,7 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
|||||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||||
import { TranslateLoaderMock } from '../../shared/mocks/translate-loader.mock';
|
import { TranslateLoaderMock } from '../../shared/mocks/translate-loader.mock';
|
||||||
import { ItemDataService } from '../../core/data/item-data.service';
|
import { ItemDataService } from '../../core/data/item-data.service';
|
||||||
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA, PLATFORM_ID } from '@angular/core';
|
||||||
import { ItemPageComponent } from './item-page.component';
|
import { ItemPageComponent } from './item-page.component';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { ActivatedRouteStub } from '../../shared/testing/active-router.stub';
|
import { ActivatedRouteStub } from '../../shared/testing/active-router.stub';
|
||||||
@@ -22,6 +22,10 @@ import {
|
|||||||
import { AuthService } from '../../core/auth/auth.service';
|
import { AuthService } from '../../core/auth/auth.service';
|
||||||
import { createPaginatedList } from '../../shared/testing/utils.test';
|
import { createPaginatedList } from '../../shared/testing/utils.test';
|
||||||
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
|
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
|
||||||
|
import { ServerResponseService } from '../../core/services/server-response.service';
|
||||||
|
import { SignpostingDataService } from '../../core/data/signposting-data.service';
|
||||||
|
import { LinkDefinition, LinkHeadService } from '../../core/services/link-head.service';
|
||||||
|
import { SignpostingLink } from '../../core/data/signposting-links.model';
|
||||||
|
|
||||||
const mockItem: Item = Object.assign(new Item(), {
|
const mockItem: Item = Object.assign(new Item(), {
|
||||||
bundles: createSuccessfulRemoteDataObject$(createPaginatedList([])),
|
bundles: createSuccessfulRemoteDataObject$(createPaginatedList([])),
|
||||||
@@ -36,11 +40,28 @@ const mockWithdrawnItem: Item = Object.assign(new Item(), {
|
|||||||
isWithdrawn: true
|
isWithdrawn: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const mocklink = {
|
||||||
|
href: 'http://test.org',
|
||||||
|
rel: 'rel1',
|
||||||
|
type: 'type1'
|
||||||
|
};
|
||||||
|
|
||||||
|
const mocklink2 = {
|
||||||
|
href: 'http://test2.org',
|
||||||
|
rel: 'rel2',
|
||||||
|
type: undefined
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockSignpostingLinks: SignpostingLink[] = [mocklink, mocklink2];
|
||||||
|
|
||||||
describe('ItemPageComponent', () => {
|
describe('ItemPageComponent', () => {
|
||||||
let comp: ItemPageComponent;
|
let comp: ItemPageComponent;
|
||||||
let fixture: ComponentFixture<ItemPageComponent>;
|
let fixture: ComponentFixture<ItemPageComponent>;
|
||||||
let authService: AuthService;
|
let authService: AuthService;
|
||||||
let authorizationDataService: AuthorizationDataService;
|
let authorizationDataService: AuthorizationDataService;
|
||||||
|
let serverResponseService: jasmine.SpyObj<ServerResponseService>;
|
||||||
|
let signpostingDataService: jasmine.SpyObj<SignpostingDataService>;
|
||||||
|
let linkHeadService: jasmine.SpyObj<LinkHeadService>;
|
||||||
|
|
||||||
const mockMetadataService = {
|
const mockMetadataService = {
|
||||||
/* eslint-disable no-empty,@typescript-eslint/no-empty-function */
|
/* eslint-disable no-empty,@typescript-eslint/no-empty-function */
|
||||||
@@ -60,6 +81,18 @@ describe('ItemPageComponent', () => {
|
|||||||
authorizationDataService = jasmine.createSpyObj('authorizationDataService', {
|
authorizationDataService = jasmine.createSpyObj('authorizationDataService', {
|
||||||
isAuthorized: observableOf(false),
|
isAuthorized: observableOf(false),
|
||||||
});
|
});
|
||||||
|
serverResponseService = jasmine.createSpyObj('ServerResponseService', {
|
||||||
|
setHeader: jasmine.createSpy('setHeader'),
|
||||||
|
});
|
||||||
|
|
||||||
|
signpostingDataService = jasmine.createSpyObj('SignpostingDataService', {
|
||||||
|
getLinks: observableOf([mocklink, mocklink2]),
|
||||||
|
});
|
||||||
|
|
||||||
|
linkHeadService = jasmine.createSpyObj('LinkHeadService', {
|
||||||
|
addTag: jasmine.createSpy('setHeader'),
|
||||||
|
removeTag: jasmine.createSpy('removeTag'),
|
||||||
|
});
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [TranslateModule.forRoot({
|
imports: [TranslateModule.forRoot({
|
||||||
@@ -76,6 +109,10 @@ describe('ItemPageComponent', () => {
|
|||||||
{ provide: Router, useValue: {} },
|
{ provide: Router, useValue: {} },
|
||||||
{ provide: AuthService, useValue: authService },
|
{ provide: AuthService, useValue: authService },
|
||||||
{ provide: AuthorizationDataService, useValue: authorizationDataService },
|
{ provide: AuthorizationDataService, useValue: authorizationDataService },
|
||||||
|
{ provide: ServerResponseService, useValue: serverResponseService },
|
||||||
|
{ provide: SignpostingDataService, useValue: signpostingDataService },
|
||||||
|
{ provide: LinkHeadService, useValue: linkHeadService },
|
||||||
|
{ provide: PLATFORM_ID, useValue: 'server' },
|
||||||
],
|
],
|
||||||
|
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
@@ -126,6 +163,33 @@ describe('ItemPageComponent', () => {
|
|||||||
const objectLoader = fixture.debugElement.query(By.css('ds-listable-object-component-loader'));
|
const objectLoader = fixture.debugElement.query(By.css('ds-listable-object-component-loader'));
|
||||||
expect(objectLoader.nativeElement).toBeDefined();
|
expect(objectLoader.nativeElement).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should add the signposting links', () => {
|
||||||
|
expect(serverResponseService.setHeader).toHaveBeenCalled();
|
||||||
|
expect(linkHeadService.addTag).toHaveBeenCalledTimes(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should add link tags correctly', () => {
|
||||||
|
|
||||||
|
expect(comp.signpostingLinks).toEqual([mocklink, mocklink2]);
|
||||||
|
|
||||||
|
// Check if linkHeadService.addTag() was called with the correct arguments
|
||||||
|
expect(linkHeadService.addTag).toHaveBeenCalledTimes(mockSignpostingLinks.length);
|
||||||
|
let expected: LinkDefinition = mockSignpostingLinks[0] as LinkDefinition;
|
||||||
|
expect(linkHeadService.addTag).toHaveBeenCalledWith(expected);
|
||||||
|
expected = {
|
||||||
|
href: 'http://test2.org',
|
||||||
|
rel: 'rel2'
|
||||||
|
};
|
||||||
|
expect(linkHeadService.addTag).toHaveBeenCalledWith(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set Link header on the server', () => {
|
||||||
|
|
||||||
|
expect(serverResponseService.setHeader).toHaveBeenCalledWith('Link', '<http://test.org> ; rel="rel1" ; type="type1" , <http://test2.org> ; rel="rel2" ');
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
describe('when the item is withdrawn and the user is not an admin', () => {
|
describe('when the item is withdrawn and the user is not an admin', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -150,6 +214,11 @@ describe('ItemPageComponent', () => {
|
|||||||
const objectLoader = fixture.debugElement.query(By.css('ds-listable-object-component-loader'));
|
const objectLoader = fixture.debugElement.query(By.css('ds-listable-object-component-loader'));
|
||||||
expect(objectLoader.nativeElement).toBeDefined();
|
expect(objectLoader.nativeElement).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should add the signposting links', () => {
|
||||||
|
expect(serverResponseService.setHeader).toHaveBeenCalled();
|
||||||
|
expect(linkHeadService.addTag).toHaveBeenCalledTimes(2);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when the item is not withdrawn and the user is not an admin', () => {
|
describe('when the item is not withdrawn and the user is not an admin', () => {
|
||||||
@@ -162,6 +231,11 @@ describe('ItemPageComponent', () => {
|
|||||||
const objectLoader = fixture.debugElement.query(By.css('ds-listable-object-component-loader'));
|
const objectLoader = fixture.debugElement.query(By.css('ds-listable-object-component-loader'));
|
||||||
expect(objectLoader.nativeElement).toBeDefined();
|
expect(objectLoader.nativeElement).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should add the signposting links', () => {
|
||||||
|
expect(serverResponseService.setHeader).toHaveBeenCalled();
|
||||||
|
expect(linkHeadService.addTag).toHaveBeenCalledTimes(2);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@@ -1,8 +1,9 @@
|
|||||||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, Inject, OnDestroy, OnInit, PLATFORM_ID } from '@angular/core';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { isPlatformServer } from '@angular/common';
|
||||||
|
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { map } from 'rxjs/operators';
|
import { map, take } from 'rxjs/operators';
|
||||||
|
|
||||||
import { ItemDataService } from '../../core/data/item-data.service';
|
import { ItemDataService } from '../../core/data/item-data.service';
|
||||||
import { RemoteData } from '../../core/data/remote-data';
|
import { RemoteData } from '../../core/data/remote-data';
|
||||||
@@ -15,6 +16,11 @@ import { getItemPageRoute } from '../item-page-routing-paths';
|
|||||||
import { redirectOn4xx } from '../../core/shared/authorized.operators';
|
import { redirectOn4xx } from '../../core/shared/authorized.operators';
|
||||||
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
|
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
|
||||||
import { FeatureID } from '../../core/data/feature-authorization/feature-id';
|
import { FeatureID } from '../../core/data/feature-authorization/feature-id';
|
||||||
|
import { ServerResponseService } from '../../core/services/server-response.service';
|
||||||
|
import { SignpostingDataService } from '../../core/data/signposting-data.service';
|
||||||
|
import { SignpostingLink } from '../../core/data/signposting-links.model';
|
||||||
|
import { isNotEmpty } from '../../shared/empty.util';
|
||||||
|
import { LinkDefinition, LinkHeadService } from '../../core/services/link-head.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component renders a simple item page.
|
* This component renders a simple item page.
|
||||||
@@ -28,7 +34,7 @@ import { FeatureID } from '../../core/data/feature-authorization/feature-id';
|
|||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
animations: [fadeInOut]
|
animations: [fadeInOut]
|
||||||
})
|
})
|
||||||
export class ItemPageComponent implements OnInit {
|
export class ItemPageComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The item's id
|
* The item's id
|
||||||
@@ -57,13 +63,23 @@ export class ItemPageComponent implements OnInit {
|
|||||||
|
|
||||||
itemUrl: string;
|
itemUrl: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains a list of SignpostingLink related to the item
|
||||||
|
*/
|
||||||
|
signpostingLinks: SignpostingLink[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected route: ActivatedRoute,
|
protected route: ActivatedRoute,
|
||||||
private router: Router,
|
protected router: Router,
|
||||||
private items: ItemDataService,
|
protected items: ItemDataService,
|
||||||
private authService: AuthService,
|
protected authService: AuthService,
|
||||||
private authorizationService: AuthorizationDataService
|
protected authorizationService: AuthorizationDataService,
|
||||||
|
protected responseService: ServerResponseService,
|
||||||
|
protected signpostingDataService: SignpostingDataService,
|
||||||
|
protected linkHeadService: LinkHeadService,
|
||||||
|
@Inject(PLATFORM_ID) protected platformId: string
|
||||||
) {
|
) {
|
||||||
|
this.initPageLinks();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -82,4 +98,42 @@ export class ItemPageComponent implements OnInit {
|
|||||||
this.isAdmin$ = this.authorizationService.isAuthorized(FeatureID.AdministratorOf);
|
this.isAdmin$ = this.authorizationService.isAuthorized(FeatureID.AdministratorOf);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create page links if any are retrieved by signposting endpoint
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private initPageLinks(): void {
|
||||||
|
this.route.params.subscribe(params => {
|
||||||
|
this.signpostingDataService.getLinks(params.id).pipe(take(1)).subscribe((signpostingLinks: SignpostingLink[]) => {
|
||||||
|
let links = '';
|
||||||
|
this.signpostingLinks = signpostingLinks;
|
||||||
|
|
||||||
|
signpostingLinks.forEach((link: SignpostingLink) => {
|
||||||
|
links = links + (isNotEmpty(links) ? ', ' : '') + `<${link.href}> ; rel="${link.rel}"` + (isNotEmpty(link.type) ? ` ; type="${link.type}" ` : ' ');
|
||||||
|
let tag: LinkDefinition = {
|
||||||
|
href: link.href,
|
||||||
|
rel: link.rel
|
||||||
|
};
|
||||||
|
if (isNotEmpty(link.type)) {
|
||||||
|
tag = Object.assign(tag, {
|
||||||
|
type: link.type
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.linkHeadService.addTag(tag);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isPlatformServer(this.platformId)) {
|
||||||
|
this.responseService.setHeader('Link', links);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.signpostingLinks.forEach((link: SignpostingLink) => {
|
||||||
|
this.linkHeadService.removeTag(`href='${link.href}'`);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -38,7 +38,7 @@ export class ServerInitService extends InitService {
|
|||||||
protected metadata: MetadataService,
|
protected metadata: MetadataService,
|
||||||
protected breadcrumbsService: BreadcrumbsService,
|
protected breadcrumbsService: BreadcrumbsService,
|
||||||
protected themeService: ThemeService,
|
protected themeService: ThemeService,
|
||||||
protected menuService: MenuService,
|
protected menuService: MenuService
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
store,
|
store,
|
||||||
|
Reference in New Issue
Block a user