[CST-12235] support for the LDN inbox for root & /home & /item/:uuid pages

This commit is contained in:
Alisa Ismailati
2023-10-24 15:14:55 +02:00
parent 6d794cd51f
commit 422a223c40
6 changed files with 125 additions and 17 deletions

View File

@@ -10,7 +10,10 @@ import { ConfigurationProperty } from '../../shared/configuration-property.model
})
export class NotifyInfoService {
private relationLink = 'http://www.w3.org/ns/ldp#inbox';
/**
* The relation link for the inbox
*/
private _inboxRelationLink = 'http://www.w3.org/ns/ldp#inbox';
constructor(
private configService: ConfigurationDataService,
@@ -27,7 +30,11 @@ export class NotifyInfoService {
);
}
getCoarLdnLocalInboxUrl(): Observable<string[]> {
/**
* Get the url of the local inbox from the REST configuration
* @returns the url of the local inbox
*/
getCoarLdnLocalInboxUrls(): Observable<string[]> {
return this.configService.findByPropertyName('ldn.notify.local-inbox-endpoint').pipe(
getFirstSucceededRemoteData(),
getRemoteDataPayload(),
@@ -49,7 +56,11 @@ export class NotifyInfoService {
return `${ssl ? 'https' : 'http'}://${host}:${port}${namespace}`;
}
getRelationLink(): string{
return this.relationLink;
/**
* Method to get the relation link for the inbox
* @returns the relation link for the inbox
*/
getInboxRelationLink(): string {
return this._inboxRelationLink;
}
}

View File

@@ -1,23 +1,28 @@
import { Component, Inject, OnInit, PLATFORM_ID } from '@angular/core';
import { Component, Inject, OnDestroy, OnInit, PLATFORM_ID } from '@angular/core';
import { map, switchMap } from 'rxjs/operators';
import { ActivatedRoute } from '@angular/router';
import { Observable, of } from 'rxjs';
import { Observable } from 'rxjs';
import { Site } from '../core/shared/site.model';
import { environment } from '../../environments/environment';
import { isPlatformServer } from '@angular/common';
import { ServerResponseService } from '../core/services/server-response.service';
import { NotifyInfoService } from '../core/coar-notify/notify-info/notify-info.service';
import { LinkDefinition, LinkHeadService } from '../core/services/link-head.service';
import { isNotEmpty } from '../shared/empty.util';
@Component({
selector: 'ds-home-page',
styleUrls: ['./home-page.component.scss'],
templateUrl: './home-page.component.html'
})
export class HomePageComponent implements OnInit {
export class HomePageComponent implements OnInit, OnDestroy {
site$: Observable<Site>;
recentSubmissionspageSize: number;
/**
* An array of LinkDefinition objects representing inbox links for the home page.
*/
inboxLinks: LinkDefinition[] = [];
constructor(
private route: ActivatedRoute,
@@ -27,14 +32,18 @@ export class HomePageComponent implements OnInit {
@Inject(PLATFORM_ID) private platformId: string
) {
this.recentSubmissionspageSize = environment.homePage.recentSubmissions.pageSize;
// Get COAR REST API URLs from REST configuration
// only if COAR configuration is enabled
this.notifyInfoService.isCoarConfigEnabled().pipe(
switchMap((coarLdnEnabled: boolean) => {
if (coarLdnEnabled) {
return this.notifyInfoService.getCoarLdnLocalInboxUrl();
return this.notifyInfoService.getCoarLdnLocalInboxUrls();
}
})
).subscribe((coarRestApiUrls: string[]) => {
this.initPageLinks(coarRestApiUrls);
if (coarRestApiUrls.length > 0) {
this.initPageLinks(coarRestApiUrls);
}
});
}
@@ -44,17 +53,37 @@ export class HomePageComponent implements OnInit {
);
}
/**
* Initializes page links for COAR REST API URLs.
* @param coarRestApiUrls An array of COAR REST API URLs.
*/
private initPageLinks(coarRestApiUrls: string[]): void {
const rel = this.notifyInfoService.getRelationLink();
const rel = this.notifyInfoService.getInboxRelationLink();
let links = '';
coarRestApiUrls.forEach((coarRestApiUrl: string) => {
// Add link to head
let tag: LinkDefinition = {
href: coarRestApiUrl,
rel: rel
};
this.inboxLinks.push(tag);
this.linkHeadService.addTag(tag);
if (isPlatformServer(this.platformId)) {
this.responseService.setHeader('Link', `<${coarRestApiUrl}>; rel="${rel}"`);
}
links = links + (isNotEmpty(links) ? ', ' : '') + `<${coarRestApiUrl}> ; rel="${rel}"`;
});
if (isPlatformServer(this.platformId)) {
// Add link to response header
this.responseService.setHeader('Link', links);
}
}
/**
* It removes the inbox links from the head of the html.
*/
ngOnDestroy(): void {
this.inboxLinks.forEach((link: LinkDefinition) => {
this.linkHeadService.removeTag(`href='${link.href}'`);
});
}
}

View File

@@ -23,6 +23,7 @@ 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';
import { NotifyInfoService } from '../../core/coar-notify/notify-info/notify-info.service';
const mockItem: Item = Object.assign(new Item(), {
bundles: createSuccessfulRemoteDataObject$(createPaginatedList([])),
@@ -122,6 +123,7 @@ describe('FullItemPageComponent', () => {
{ provide: ServerResponseService, useValue: serverResponseService },
{ provide: SignpostingDataService, useValue: signpostingDataService },
{ provide: LinkHeadService, useValue: linkHeadService },
{ provide: NotifyInfoService, useValue: {} },
{ provide: PLATFORM_ID, useValue: 'server' }
],
schemas: [NO_ERRORS_SCHEMA]

View File

@@ -19,6 +19,7 @@ import { AuthorizationDataService } from '../../core/data/feature-authorization/
import { ServerResponseService } from '../../core/services/server-response.service';
import { SignpostingDataService } from '../../core/data/signposting-data.service';
import { LinkHeadService } from '../../core/services/link-head.service';
import { NotifyInfoService } from '../../core/coar-notify/notify-info/notify-info.service';
/**
* This component renders a full item page.
@@ -55,9 +56,10 @@ export class FullItemPageComponent extends ItemPageComponent implements OnInit,
protected responseService: ServerResponseService,
protected signpostingDataService: SignpostingDataService,
protected linkHeadService: LinkHeadService,
protected notifyInfoService: NotifyInfoService,
@Inject(PLATFORM_ID) protected platformId: string,
) {
super(route, router, items, authService, authorizationService, responseService, signpostingDataService, linkHeadService, platformId);
super(route, router, items, authService, authorizationService, responseService, signpostingDataService, linkHeadService,notifyInfoService, platformId);
}
/*** AoT inheritance fix, will hopefully be resolved in the near future **/

View File

@@ -26,6 +26,7 @@ import { ServerResponseService } from '../../core/services/server-response.servi
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';
import { NotifyInfoService } from '../../core/coar-notify/notify-info/notify-info.service';
const mockItem: Item = Object.assign(new Item(), {
bundles: createSuccessfulRemoteDataObject$(createPaginatedList([])),
@@ -62,6 +63,7 @@ describe('ItemPageComponent', () => {
let serverResponseService: jasmine.SpyObj<ServerResponseService>;
let signpostingDataService: jasmine.SpyObj<SignpostingDataService>;
let linkHeadService: jasmine.SpyObj<LinkHeadService>;
let notifyInfoService: jasmine.SpyObj<NotifyInfoService>;
const mockMetadataService = {
/* eslint-disable no-empty,@typescript-eslint/no-empty-function */
@@ -94,6 +96,12 @@ describe('ItemPageComponent', () => {
removeTag: jasmine.createSpy('removeTag'),
});
notifyInfoService = jasmine.createSpyObj('NotifyInfoService', {
getInboxRelationLink: 'http://www.w3.org/ns/ldp#inbox',
isCoarConfigEnabled: observableOf(true),
getCoarLdnLocalInboxUrls: observableOf(['http://test.org', 'http://test2.org']),
});
TestBed.configureTestingModule({
imports: [TranslateModule.forRoot({
loader: {
@@ -112,6 +120,7 @@ describe('ItemPageComponent', () => {
{ provide: ServerResponseService, useValue: serverResponseService },
{ provide: SignpostingDataService, useValue: signpostingDataService },
{ provide: LinkHeadService, useValue: linkHeadService },
{ provide: NotifyInfoService, useValue: notifyInfoService},
{ provide: PLATFORM_ID, useValue: 'server' },
],

View File

@@ -2,8 +2,8 @@ import { ChangeDetectionStrategy, Component, Inject, OnDestroy, OnInit, PLATFORM
import { ActivatedRoute, Router } from '@angular/router';
import { isPlatformServer } from '@angular/common';
import { Observable } from 'rxjs';
import { map, take } from 'rxjs/operators';
import { Observable, combineLatest } from 'rxjs';
import { map, switchMap, take } from 'rxjs/operators';
import { ItemDataService } from '../../core/data/item-data.service';
import { RemoteData } from '../../core/data/remote-data';
@@ -21,6 +21,7 @@ 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';
import { NotifyInfoService } from 'src/app/core/coar-notify/notify-info/notify-info.service';
/**
* This component renders a simple item page.
@@ -68,6 +69,13 @@ export class ItemPageComponent implements OnInit, OnDestroy {
*/
signpostingLinks: SignpostingLink[] = [];
/**
* An array of LinkDefinition objects representing inbox links for the item page.
*/
inboxTags: LinkDefinition[] = [];
coarRestApiUrls: string[] = [];
constructor(
protected route: ActivatedRoute,
protected router: Router,
@@ -77,6 +85,7 @@ export class ItemPageComponent implements OnInit, OnDestroy {
protected responseService: ServerResponseService,
protected signpostingDataService: SignpostingDataService,
protected linkHeadService: LinkHeadService,
protected notifyInfoService: NotifyInfoService,
@Inject(PLATFORM_ID) protected platformId: string
) {
this.initPageLinks();
@@ -106,7 +115,8 @@ export class ItemPageComponent implements OnInit, OnDestroy {
*/
private initPageLinks(): void {
this.route.params.subscribe(params => {
this.signpostingDataService.getLinks(params.id).pipe(take(1)).subscribe((signpostingLinks: SignpostingLink[]) => {
combineLatest([this.signpostingDataService.getLinks(params.id).pipe(take(1)), this.getCoarLdnLocalInboxUrls()])
.subscribe(([signpostingLinks, coarRestApiUrls]) => {
let links = '';
this.signpostingLinks = signpostingLinks;
@@ -124,6 +134,11 @@ export class ItemPageComponent implements OnInit, OnDestroy {
this.linkHeadService.addTag(tag);
});
if (coarRestApiUrls.length > 0) {
let inboxLinks = this.initPageInboxLinks(coarRestApiUrls);
links = links + (isNotEmpty(links) ? ', ' : '') + inboxLinks;
}
if (isPlatformServer(this.platformId)) {
this.responseService.setHeader('Link', links);
}
@@ -131,9 +146,49 @@ export class ItemPageComponent implements OnInit, OnDestroy {
});
}
/**
* Sets the COAR LDN local inbox URL if COAR configuration is enabled.
* If the COAR LDN local inbox URL is retrieved successfully, initializes the page inbox links.
*/
private getCoarLdnLocalInboxUrls(): Observable<string[]> {
return this.notifyInfoService.isCoarConfigEnabled().pipe(
switchMap((coarLdnEnabled: boolean) => {
if (coarLdnEnabled) {
return this.notifyInfoService.getCoarLdnLocalInboxUrls();
}
})
);
}
/**
* Initializes the page inbox links.
* @param coarRestApiUrls - An array of COAR REST API URLs.
*/
private initPageInboxLinks(coarRestApiUrls: string[]): string {
const rel = this.notifyInfoService.getInboxRelationLink();
let links = '';
coarRestApiUrls.forEach((coarRestApiUrl: string) => {
// Add link to head
let tag: LinkDefinition = {
href: coarRestApiUrl,
rel: rel
};
this.inboxTags.push(tag);
this.linkHeadService.addTag(tag);
links = links + (isNotEmpty(links) ? ', ' : '') + `<${coarRestApiUrl}> ; rel="${rel}"`;
});
return links;
}
ngOnDestroy(): void {
this.signpostingLinks.forEach((link: SignpostingLink) => {
this.linkHeadService.removeTag(`href='${link.href}'`);
});
this.inboxTags.forEach((link: LinkDefinition) => {
this.linkHeadService.removeTag(`href='${link.href}'`);
});
}
}