add DSONameService

This commit is contained in:
Art Lowel
2020-02-26 13:58:09 +01:00
parent f67387ed65
commit 5b326aea92
6 changed files with 193 additions and 12 deletions

View File

@@ -1,11 +1,12 @@
import { Breadcrumb } from '../../breadcrumbs/breadcrumb/breadcrumb.model'; import { Breadcrumb } from '../../breadcrumbs/breadcrumb/breadcrumb.model';
import { BreadcrumbsService } from './breadcrumbs.service'; import { BreadcrumbsService } from './breadcrumbs.service';
import { DSONameService } from './dso-name.service';
import { Observable, of as observableOf } from 'rxjs'; import { Observable, of as observableOf } from 'rxjs';
import { ChildHALResource } from '../shared/child-hal-resource.model'; import { ChildHALResource } from '../shared/child-hal-resource.model';
import { LinkService } from '../cache/builders/link.service'; import { LinkService } from '../cache/builders/link.service';
import { DSpaceObject } from '../shared/dspace-object.model'; import { DSpaceObject } from '../shared/dspace-object.model';
import { followLink } from '../../shared/utils/follow-link-config.model'; import { followLink } from '../../shared/utils/follow-link-config.model';
import { filter, find, map, switchMap } from 'rxjs/operators'; import { find, map, switchMap } from 'rxjs/operators';
import { getDSOPath } from '../../app-routing.module'; import { getDSOPath } from '../../app-routing.module';
import { RemoteData } from '../data/remote-data'; import { RemoteData } from '../data/remote-data';
import { hasValue } from '../../shared/empty.util'; import { hasValue } from '../../shared/empty.util';
@@ -13,12 +14,16 @@ import { Injectable } from '@angular/core';
@Injectable() @Injectable()
export class DSOBreadcrumbsService implements BreadcrumbsService<ChildHALResource & DSpaceObject> { export class DSOBreadcrumbsService implements BreadcrumbsService<ChildHALResource & DSpaceObject> {
constructor(private linkService: LinkService) { constructor(
private linkService: LinkService,
private dsoNameService: DSONameService
) {
} }
getBreadcrumbs(key: ChildHALResource & DSpaceObject, url: string): Observable<Breadcrumb[]> { getBreadcrumbs(key: ChildHALResource & DSpaceObject, url: string): Observable<Breadcrumb[]> {
const crumb = new Breadcrumb(key.name, url); const label = this.dsoNameService.getName(key);
const crumb = new Breadcrumb(label, url);
const propertyName = key.getParentLinkKey(); const propertyName = key.getParentLinkKey();
return this.linkService.resolveLink(key, followLink(propertyName))[propertyName].pipe( return this.linkService.resolveLink(key, followLink(propertyName))[propertyName].pipe(
find((childRD: RemoteData<ChildHALResource & DSpaceObject>) => childRD.hasSucceeded || childRD.statusCode === 204), find((childRD: RemoteData<ChildHALResource & DSpaceObject>) => childRD.hasSucceeded || childRD.statusCode === 204),

View File

@@ -0,0 +1,116 @@
import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
import { DSpaceObject } from '../shared/dspace-object.model';
import { GenericConstructor } from '../shared/generic-constructor';
import { Item } from '../shared/item.model';
import { MetadataValueFilter } from '../shared/metadata.models';
import { DSONameService } from './dso-name.service';
describe(`DSONameService`, () => {
let service: DSONameService;
let mockPersonName: string;
let mockPerson: DSpaceObject;
let mockOrgUnitName: string;
let mockOrgUnit: DSpaceObject;
let mockDSOName: string;
let mockDSO: DSpaceObject;
beforeEach(() => {
mockPersonName = 'Doe, John';
mockPerson = Object.assign(new DSpaceObject(), {
firstMetadataValue(keyOrKeys: string | string[], valueFilter?: MetadataValueFilter): string {
return mockPersonName
},
getRenderTypes(): Array<string | GenericConstructor<ListableObject>> {
return ['Person', Item, DSpaceObject];
}
});
mockOrgUnitName = 'Molecular Spectroscopy';
mockOrgUnit = Object.assign(new DSpaceObject(), {
firstMetadataValue(keyOrKeys: string | string[], valueFilter?: MetadataValueFilter): string {
return mockOrgUnitName
},
getRenderTypes(): Array<string | GenericConstructor<ListableObject>> {
return ['OrgUnit', Item, DSpaceObject];
}
});
mockDSOName = 'Lorem Ipsum';
mockDSO = Object.assign(new DSpaceObject(), {
firstMetadataValue(keyOrKeys: string | string[], valueFilter?: MetadataValueFilter): string {
return mockDSOName
},
getRenderTypes(): Array<string | GenericConstructor<ListableObject>> {
return [DSpaceObject];
}
});
service = new DSONameService();
});
describe(`getName`, () => {
it(`should use the Person factory for Person entities`, () => {
spyOn((service as any).factories, 'Person').and.returnValue('Bingo!');
const result = service.getName(mockPerson);
expect((service as any).factories.Person).toHaveBeenCalledWith(mockPerson);
expect(result).toBe('Bingo!');
});
it(`should use the OrgUnit factory for OrgUnit entities`, () => {
spyOn((service as any).factories, 'OrgUnit').and.returnValue('Bingo!');
const result = service.getName(mockOrgUnit);
expect((service as any).factories.OrgUnit).toHaveBeenCalledWith(mockOrgUnit);
expect(result).toBe('Bingo!');
});
it(`should use the Default factory for regular DSpaceObjects`, () => {
spyOn((service as any).factories, 'Default').and.returnValue('Bingo!');
const result = service.getName(mockDSO);
expect((service as any).factories.Default).toHaveBeenCalledWith(mockDSO);
expect(result).toBe('Bingo!');
});
});
describe(`factories.Person`, () => {
beforeEach(() => {
spyOn(mockPerson, 'firstMetadataValue').and.returnValues(...mockPersonName.split(', '));
});
it(`should return 'person.familyName, person.givenName'`, () => {
const result = (service as any).factories.Person(mockPerson);
expect(result).toBe(mockPersonName);
expect(mockPerson.firstMetadataValue).toHaveBeenCalledWith('person.familyName');
expect(mockPerson.firstMetadataValue).toHaveBeenCalledWith('person.givenName');
});
});
describe(`factories.OrgUnit`, () => {
beforeEach(() => {
spyOn(mockOrgUnit, 'firstMetadataValue').and.callThrough();
});
it(`should return 'organization.legalName'`, () => {
const result = (service as any).factories.OrgUnit(mockOrgUnit);
expect(result).toBe(mockOrgUnitName);
expect(mockOrgUnit.firstMetadataValue).toHaveBeenCalledWith('organization.legalName');
});
});
describe(`factories.Default`, () => {
beforeEach(() => {
spyOn(mockDSO, 'firstMetadataValue').and.callThrough();
});
it(`should return 'dc.title'`, () => {
const result = (service as any).factories.Default(mockDSO);
expect(result).toBe(mockDSOName);
expect(mockDSO.firstMetadataValue).toHaveBeenCalledWith('dc.title');
});
});
});

View File

@@ -0,0 +1,53 @@
import { Injectable } from '@angular/core';
import { hasValue } from '../../shared/empty.util';
import { DSpaceObject } from '../shared/dspace-object.model';
/**
* Returns a name for a {@link DSpaceObject} based
* on its render types.
*/
@Injectable({
providedIn: 'root'
})
export class DSONameService {
/**
* Functions to generate the specific names.
*
* If this list ever expands it will probably be worth it to
* refactor this using decorators for specific entity types,
* or perhaps by using a dedicated model for each entity type
*
* With only two exceptions those solutions seem overkill for now.
*/
private factories = {
Person: (dso: DSpaceObject): string => {
return `${dso.firstMetadataValue('person.familyName')}, ${dso.firstMetadataValue('person.givenName')}`;
},
OrgUnit: (dso: DSpaceObject): string => {
return dso.firstMetadataValue('organization.legalName');
},
Default: (dso: DSpaceObject): string => {
return dso.firstMetadataValue('dc.title');
}
};
/**
* Get the name for the given {@link DSpaceObject}
*
* @param dso The {@link DSpaceObject} you want a name for
*/
getName(dso: DSpaceObject): string {
const types = dso.getRenderTypes();
const match = types
.filter((type) => typeof type === 'string')
.find((type: string) => Object.keys(this.factories).includes(type)) as string;
if (hasValue(match)) {
return this.factories[match](dso);
} else {
return this.factories.Default(dso);
}
}
}

View File

@@ -55,7 +55,10 @@ export class LinkService {
parent: this.parentInjector parent: this.parentInjector
}).get(provider); }).get(provider);
const href = model._links[matchingLinkDef.linkName].href; const link = model._links[matchingLinkDef.linkName];
if (hasValue(link)) {
const href = link.href;
try { try {
if (matchingLinkDef.isList) { if (matchingLinkDef.isList) {
@@ -67,6 +70,7 @@ export class LinkService {
throw new Error(`Something went wrong when using @dataService(${matchingLinkDef.resourceType.value}) ${hasValue(service) ? '' : '(undefined) '}to resolve link ${linkToFollow.name} from ${href}`); throw new Error(`Something went wrong when using @dataService(${matchingLinkDef.resourceType.value}) ${hasValue(service) ? '' : '(undefined) '}to resolve link ${linkToFollow.name} from ${href}`);
} }
} }
}
return model; return model;
} }

View File

@@ -10,6 +10,7 @@ import { catchError, distinctUntilKeyChanged, filter, first, map, take } from 'r
import { GLOBAL_CONFIG, GlobalConfig } from '../../../config'; import { GLOBAL_CONFIG, GlobalConfig } from '../../../config';
import { hasValue, isNotEmpty } from '../../shared/empty.util'; import { hasValue, isNotEmpty } from '../../shared/empty.util';
import { DSONameService } from '../breadcrumbs/dso-name.service';
import { CacheableObject } from '../cache/object-cache.reducer'; import { CacheableObject } from '../cache/object-cache.reducer';
import { BitstreamDataService } from '../data/bitstream-data.service'; import { BitstreamDataService } from '../data/bitstream-data.service';
import { BitstreamFormatDataService } from '../data/bitstream-format-data.service'; import { BitstreamFormatDataService } from '../data/bitstream-format-data.service';
@@ -35,6 +36,7 @@ export class MetadataService {
private translate: TranslateService, private translate: TranslateService,
private meta: Meta, private meta: Meta,
private title: Title, private title: Title,
private dsoNameService: DSONameService,
private bitstreamDataService: BitstreamDataService, private bitstreamDataService: BitstreamDataService,
private bitstreamFormatDataService: BitstreamFormatDataService, private bitstreamFormatDataService: BitstreamFormatDataService,
@Inject(GLOBAL_CONFIG) private envConfig: GlobalConfig @Inject(GLOBAL_CONFIG) private envConfig: GlobalConfig
@@ -154,7 +156,7 @@ export class MetadataService {
* Add <meta name="title" ... > to the <head> * Add <meta name="title" ... > to the <head>
*/ */
private setTitleTag(): void { private setTitleTag(): void {
const value = this.getMetaTagValue('dc.title'); const value = this.dsoNameService.getName(this.currentObject.getValue());
this.addMetaTag('title', value); this.addMetaTag('title', value);
this.title.setTitle(value); this.title.setTitle(value);
} }

View File

@@ -69,6 +69,7 @@ export class DSpaceObject extends ListableObject implements CacheableObject {
/** /**
* The name for this DSpaceObject * The name for this DSpaceObject
* @deprecated use {@link DSONameService} instead
*/ */
get name(): string { get name(): string {
return (isUndefined(this._name)) ? this.firstMetadataValue('dc.title') : this._name; return (isUndefined(this._name)) ? this.firstMetadataValue('dc.title') : this._name;