switched to resource-types for annotations to get around circular dependency issues

This commit is contained in:
Art Lowel
2020-01-22 11:11:02 +01:00
committed by Art Lowel
parent 56c3d12497
commit 1d31cae970
30 changed files with 80 additions and 64 deletions

View File

@@ -42,7 +42,7 @@ export class AuthStatus implements CacheableObject {
/** /**
* The eperson of this auth status * The eperson of this auth status
*/ */
@link(EPerson) @link(EPerson.type)
eperson: Observable<RemoteData<EPerson>>; eperson: Observable<RemoteData<EPerson>>;
/** /**

View File

@@ -88,23 +88,23 @@ export function getRelationships(target: any) {
return relationshipMap.get(target); return relationshipMap.get(target);
} }
export function dataService<T extends CacheableObject>(domainModelConstructor: GenericConstructor<T>): any { export function dataService(resourceType: ResourceType): any {
return (target: any) => { return (target: any) => {
if (hasNoValue(domainModelConstructor)) { if (hasNoValue(resourceType)) {
throw new Error(`Invalid @dataService annotation on ${target}, domainModelConstructor needs to be defined`); throw new Error(`Invalid @dataService annotation on ${target}, resourceType needs to be defined`);
} }
const existingDataservice = dataServiceMap.get(domainModelConstructor); const existingDataservice = dataServiceMap.get(resourceType.value);
if (hasValue(existingDataservice)) { if (hasValue(existingDataservice)) {
throw new Error(`Multiple dataservices for ${domainModelConstructor}: ${existingDataservice} and ${target}`); throw new Error(`Multiple dataservices for ${resourceType.value}: ${existingDataservice} and ${target}`);
} }
dataServiceMap.set(domainModelConstructor, target); dataServiceMap.set(resourceType.value, target);
}; };
} }
export function getDataServiceFor<T extends CacheableObject>(domainModelConstructor: GenericConstructor<T>) { export function getDataServiceFor<T extends CacheableObject>(resourceType: ResourceType) {
return dataServiceMap.get(domainModelConstructor); return dataServiceMap.get(resourceType.value);
} }
export function resolvedLink<T extends DataService<any>, K extends keyof T>(provider: GenericConstructor<T>, methodName?: K, ...params: any[]): any { export function resolvedLink<T extends DataService<any>, K extends keyof T>(provider: GenericConstructor<T>, methodName?: K, ...params: any[]): any {
@@ -135,20 +135,18 @@ export function getResolvedLinks(target: any) {
} }
export class LinkDefinition<T extends HALResource> { export class LinkDefinition<T extends HALResource> {
targetConstructor: GenericConstructor<CacheableObject>; resourceType: ResourceType;
isList = false; isList = false;
linkName: keyof T['_links']; linkName: keyof T['_links'];
propertyName: keyof T; propertyName: keyof T;
} }
export const link = <T extends HALResource>( export const link = <T extends HALResource>(
targetConstructor: GenericConstructor<HALResource>, resourceType: ResourceType,
isList = false, isList = false,
linkName?: keyof T['_links'], linkName?: keyof T['_links'],
) => { ) => {
console.log('link call', targetConstructor, isList, linkName);
return (target: T, propertyName: string) => { return (target: T, propertyName: string) => {
console.log('link return', targetConstructor, isList, linkName, target, propertyName);
let targetMap = linkMap.get(target.constructor); let targetMap = linkMap.get(target.constructor);
if (hasNoValue(targetMap)) { if (hasNoValue(targetMap)) {
@@ -160,7 +158,7 @@ export const link = <T extends HALResource>(
} }
targetMap.set(propertyName, { targetMap.set(propertyName, {
targetConstructor, resourceType,
isList, isList,
linkName, linkName,
propertyName propertyName

View File

@@ -21,10 +21,10 @@ export class LinkService {
if (hasNoValue(matchingLinkDef)) { if (hasNoValue(matchingLinkDef)) {
throw new Error(`followLink('${linkToFollow.name}') was used for a ${model.constructor.name}, but there is no property on ${model.constructor.name} models with an @link() for ${linkToFollow.name}`); throw new Error(`followLink('${linkToFollow.name}') was used for a ${model.constructor.name}, but there is no property on ${model.constructor.name} models with an @link() for ${linkToFollow.name}`);
} else { } else {
const provider = getDataServiceFor(matchingLinkDef.targetConstructor); const provider = getDataServiceFor(matchingLinkDef.resourceType);
if (hasNoValue(provider)) { if (hasNoValue(provider)) {
throw new Error(`The @link() for ${linkToFollow.name} on ${model.constructor.name} models refers to a ${matchingLinkDef.targetConstructor.name}, but there is no service with an @dataService(${matchingLinkDef.targetConstructor.name}) annotation in order to retrieve it`); throw new Error(`The @link() for ${linkToFollow.name} on ${model.constructor.name} models uses the resource type ${matchingLinkDef.resourceType.value.toUpperCase()}, but there is no service with an @dataService(${matchingLinkDef.resourceType.value.toUpperCase()}) annotation in order to retrieve it`);
} }
const service = Injector.create({ const service = Injector.create({

View File

@@ -28,7 +28,7 @@ import { RequestService } from './request.service';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
@dataService(Bitstream) @dataService(Bitstream.type)
export class BitstreamDataService extends DataService<Bitstream> { export class BitstreamDataService extends DataService<Bitstream> {
protected linkPath = 'bitstreams'; protected linkPath = 'bitstreams';

View File

@@ -41,7 +41,7 @@ const selectedBitstreamFormatSelector = createSelector(bitstreamFormatsStateSele
* A service responsible for fetching/sending data from/to the REST API on the bitstreamformats endpoint * A service responsible for fetching/sending data from/to the REST API on the bitstreamformats endpoint
*/ */
@Injectable() @Injectable()
@dataService(BitstreamFormat) @dataService(BitstreamFormat.type)
export class BitstreamFormatDataService extends DataService<BitstreamFormat> { export class BitstreamFormatDataService extends DataService<BitstreamFormat> {
protected linkPath = 'bitstreamformats'; protected linkPath = 'bitstreamformats';

View File

@@ -27,7 +27,7 @@ import { RequestService } from './request.service';
@Injectable( @Injectable(
{providedIn: 'root'} {providedIn: 'root'}
) )
@dataService(Bundle) @dataService(Bundle.type)
export class BundleDataService extends DataService<Bundle> { export class BundleDataService extends DataService<Bundle> {
protected linkPath = 'bundles'; protected linkPath = 'bundles';
protected forceBypassCache = false; protected forceBypassCache = false;

View File

@@ -49,7 +49,7 @@ import { INotification } from '../../shared/notifications/models/notification.mo
import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model'; import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model';
@Injectable() @Injectable()
@dataService(Collection) @dataService(Collection.type)
export class CollectionDataService extends ComColDataService<Collection> { export class CollectionDataService extends ComColDataService<Collection> {
protected linkPath = 'collections'; protected linkPath = 'collections';
protected errorTitle = 'collection.source.update.notifications.error.title'; protected errorTitle = 'collection.source.update.notifications.error.title';

View File

@@ -21,7 +21,7 @@ import { NormalizedObjectBuildService } from '../cache/builders/normalized-objec
import { DSOChangeAnalyzer } from './dso-change-analyzer.service'; import { DSOChangeAnalyzer } from './dso-change-analyzer.service';
@Injectable() @Injectable()
@dataService(Community) @dataService(Community.type)
export class CommunityDataService extends ComColDataService<Community> { export class CommunityDataService extends ComColDataService<Community> {
protected linkPath = 'communities'; protected linkPath = 'communities';
protected topLinkPath = 'communities/search/top'; protected topLinkPath = 'communities/search/top';

View File

@@ -39,7 +39,7 @@ class DataServiceImpl extends DataService<DSpaceObject> {
} }
@Injectable() @Injectable()
@dataService(DSpaceObject) @dataService(DSpaceObject.type)
export class DSpaceObjectDataService { export class DSpaceObjectDataService {
protected linkPath = 'dso'; protected linkPath = 'dso';
private dataService: DataServiceImpl; private dataService: DataServiceImpl;

View File

@@ -41,7 +41,7 @@ import { PaginatedList } from './paginated-list';
import { ExternalSourceEntry } from '../shared/external-source-entry.model'; import { ExternalSourceEntry } from '../shared/external-source-entry.model';
@Injectable() @Injectable()
@dataService(Item) @dataService(Item.type)
export class ItemDataService extends DataService<Item> { export class ItemDataService extends DataService<Item> {
protected linkPath = 'items'; protected linkPath = 'items';

View File

@@ -1,6 +1,5 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { dataService } from '../cache/builders/build-decorators'; import { dataService } from '../cache/builders/build-decorators';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { CoreState } from '../core.reducers'; import { CoreState } from '../core.reducers';
@@ -8,7 +7,6 @@ import { CoreState } from '../core.reducers';
import { DataService } from './data.service'; import { DataService } from './data.service';
import { RequestService } from './request.service'; import { RequestService } from './request.service';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { FindListOptions } from './request.models';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service'; import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
@@ -40,7 +38,7 @@ class DataServiceImpl extends DataService<MetadataSchema> {
* A service responsible for fetching/sending data from/to the REST API on the metadataschemas endpoint * A service responsible for fetching/sending data from/to the REST API on the metadataschemas endpoint
*/ */
@Injectable() @Injectable()
@dataService(MetadataSchema) @dataService(MetadataSchema.type)
export class MetadataSchemaDataService { export class MetadataSchemaDataService {
private dataService: DataServiceImpl; private dataService: DataServiceImpl;

View File

@@ -28,7 +28,7 @@ import { FindListOptions, FindListRequest } from './request.models';
* The service handling all relationship type requests * The service handling all relationship type requests
*/ */
@Injectable() @Injectable()
@dataService(RelationshipType) @dataService(RelationshipType.type)
export class RelationshipTypeService extends DataService<RelationshipType> { export class RelationshipTypeService extends DataService<RelationshipType> {
protected linkPath = 'relationshiptypes'; protected linkPath = 'relationshiptypes';

View File

@@ -47,7 +47,7 @@ const relationshipStateSelector = (listID: string, itemID: string): MemoizedSele
* The service handling all relationship requests * The service handling all relationship requests
*/ */
@Injectable() @Injectable()
@dataService(Relationship) @dataService(Relationship.type)
export class RelationshipService extends DataService<Relationship> { export class RelationshipService extends DataService<Relationship> {
protected linkPath = 'relationships'; protected linkPath = 'relationships';

View File

@@ -22,7 +22,7 @@ import { getSucceededRemoteData } from '../shared/operators';
* Service responsible for handling requests related to the Site object * Service responsible for handling requests related to the Site object
*/ */
@Injectable() @Injectable()
@dataService(Site) @dataService(Site.type)
export class SiteDataService extends DataService<Site> { export class SiteDataService extends DataService<Site> {
protected linkPath = 'sites'; protected linkPath = 'sites';

View File

@@ -14,7 +14,7 @@ import { HALEndpointService } from '../shared/hal-endpoint.service';
import { EPerson } from './models/eperson.model'; import { EPerson } from './models/eperson.model';
@Injectable() @Injectable()
@dataService(EPerson) @dataService(EPerson.type)
export class EPersonDataService extends DataService<EPerson> { export class EPersonDataService extends DataService<EPerson> {
protected linkPath: 'eperson/epersons'; protected linkPath: 'eperson/epersons';

View File

@@ -41,7 +41,7 @@ export class MetadataField extends ListableObject implements HALResource {
/** /**
* The metadata schema object of this metadata field * The metadata schema object of this metadata field
*/ */
@link(MetadataSchema) @link(MetadataSchema.type)
// TODO the responseparsingservice assumes schemas are always embedded. This should be remotedata instead. // TODO the responseparsingservice assumes schemas are always embedded. This should be remotedata instead.
schema?: MetadataSchema; schema?: MetadataSchema;

View File

@@ -34,17 +34,17 @@ export class Bitstream extends DSpaceObject implements HALResource {
/** /**
* The Bitstream Format for this Bitstream * The Bitstream Format for this Bitstream
*/ */
@link(BitstreamFormat) @link(BitstreamFormat.type)
format?: Observable<RemoteData<BitstreamFormat>>; format?: Observable<RemoteData<BitstreamFormat>>;
_links: { _links: {
// @link(Bitstream) // @link(Bitstream.type)
self: HALLink; self: HALLink;
// @link(Bundle) // @link(Bundle.type)
bundle: HALLink; bundle: HALLink;
// @link(BitstreamFormat) // @link(BitstreamFormat.type)
format: HALLink; format: HALLink;
content: HALLink; content: HALLink;

View File

@@ -66,13 +66,13 @@ export class Collection extends DSpaceObject {
/** /**
* The Bitstream that represents the logo of this Collection * The Bitstream that represents the logo of this Collection
*/ */
@link(Bitstream) @link(Bitstream.type)
logo?: Observable<RemoteData<Bitstream>>; logo?: Observable<RemoteData<Bitstream>>;
/** /**
* The default access conditions of this Collection * The default access conditions of this Collection
*/ */
@link(ResourcePolicy, true) @link(ResourcePolicy.type, true)
defaultAccessConditions?: Observable<RemoteData<PaginatedList<ResourcePolicy>>>; defaultAccessConditions?: Observable<RemoteData<PaginatedList<ResourcePolicy>>>;
_links: { _links: {

View File

@@ -51,13 +51,13 @@ export class Community extends DSpaceObject {
/** /**
* The Bitstream that represents the logo of this Community * The Bitstream that represents the logo of this Community
*/ */
@link(Bitstream) @link(Bitstream.type)
logo?: Observable<RemoteData<Bitstream>>; logo?: Observable<RemoteData<Bitstream>>;
@link(Collection, true) @link(Collection.type, true)
collections?: Observable<RemoteData<PaginatedList<Collection>>>; collections?: Observable<RemoteData<PaginatedList<Collection>>>;
@link(Community, true) @link(Community.type, true)
subcommunities?: Observable<RemoteData<PaginatedList<Community>>>; subcommunities?: Observable<RemoteData<PaginatedList<Community>>>;
_links: { _links: {

View File

@@ -65,13 +65,13 @@ export class RelationshipType implements CacheableObject {
/** /**
* The type of Item found to the left of this RelationshipType * The type of Item found to the left of this RelationshipType
*/ */
@link(ItemType) @link(ItemType.type)
leftType?: Observable<RemoteData<ItemType>>; leftType?: Observable<RemoteData<ItemType>>;
/** /**
* The type of Item found to the right of this RelationshipType * The type of Item found to the right of this RelationshipType
*/ */
@link(ItemType) @link(ItemType.type)
rightType?: Observable<RemoteData<ItemType>>; rightType?: Observable<RemoteData<ItemType>>;
_links: { _links: {

View File

@@ -3,15 +3,16 @@ import { link } from '../../cache/builders/build-decorators';
import { CacheableObject } from '../../cache/object-cache.reducer'; import { CacheableObject } from '../../cache/object-cache.reducer';
import { RemoteData } from '../../data/remote-data'; import { RemoteData } from '../../data/remote-data';
import { HALLink } from '../hal-link.model'; import { HALLink } from '../hal-link.model';
import { ResourceType } from '../resource-type';
import { RelationshipType } from './relationship-type.model'; import { RelationshipType } from './relationship-type.model';
import { Item } from '../item.model'; import { Item } from '../item.model';
import { ITEM } from "../item-resource-type";
import { RELATIONSHIP } from "../relationship.resource-type";
/** /**
* Describes a Relationship between two Items * Describes a Relationship between two Items
*/ */
export class Relationship implements CacheableObject { export class Relationship implements CacheableObject {
static type = new ResourceType('relationship'); static type = RELATIONSHIP;
/** /**
* The link to the rest endpoint where this object can be found * The link to the rest endpoint where this object can be found
@@ -32,14 +33,13 @@ export class Relationship implements CacheableObject {
* The item to the left of this relationship * The item to the left of this relationship
*/ */
// TODO it's likely a circular dependency 😒 -> https://stackoverflow.com/questions/35240716/webpack-import-returns-undefined-depending-on-the-order-of-imports @link(ITEM)
@link(Item)
leftItem?: Observable<RemoteData<Item>>; leftItem?: Observable<RemoteData<Item>>;
/** /**
* The item to the right of this relationship * The item to the right of this relationship
*/ */
@link(Item) @link(ITEM)
rightItem?: Observable<RemoteData<Item>>; rightItem?: Observable<RemoteData<Item>>;
/** /**
@@ -65,7 +65,7 @@ export class Relationship implements CacheableObject {
/** /**
* The type of Relationship * The type of Relationship
*/ */
@link(RelationshipType) @link(RelationshipType.type)
relationshipType?: Observable<RemoteData<RelationshipType>>; relationshipType?: Observable<RemoteData<RelationshipType>>;
_links: { _links: {

View File

@@ -0,0 +1,9 @@
import { ResourceType } from "./resource-type";
/**
* The resource type for Item.
*
* Needs to be in a separate file to prevent circular
* dependencies in webpack.
*/
export const ITEM = new ResourceType('item');

View File

@@ -12,13 +12,14 @@ import { DSpaceObject } from './dspace-object.model';
import { GenericConstructor } from './generic-constructor'; import { GenericConstructor } from './generic-constructor';
import { HALLink } from './hal-link.model'; import { HALLink } from './hal-link.model';
import { Relationship } from './item-relationships/relationship.model'; import { Relationship } from './item-relationships/relationship.model';
import { ResourceType } from './resource-type'; import { ITEM } from "./item-resource-type";
import { RELATIONSHIP } from "./relationship.resource-type";
/** /**
* Class representing a DSpace Item * Class representing a DSpace Item
*/ */
export class Item extends DSpaceObject { export class Item extends DSpaceObject {
static type = new ResourceType('item'); static type = ITEM;
/** /**
* A string representing the unique handle of this Item * A string representing the unique handle of this Item
@@ -48,13 +49,13 @@ export class Item extends DSpaceObject {
/** /**
* The Collection that owns this Item * The Collection that owns this Item
*/ */
@link(Collection) @link(Collection.type)
owningCollection: Observable<RemoteData<Collection>>; owningCollection: Observable<RemoteData<Collection>>;
@link(Bundle, true) @link(Bundle.type, true)
bundles: Observable<RemoteData<PaginatedList<Bundle>>>; bundles: Observable<RemoteData<PaginatedList<Bundle>>>;
@link(Relationship, true) @link(RELATIONSHIP)
relationships: Observable<RemoteData<PaginatedList<Relationship>>>; relationships: Observable<RemoteData<PaginatedList<Relationship>>>;
_links: { _links: {

View File

@@ -0,0 +1,10 @@
import { ResourceType } from "./resource-type";
/**
* The resource type for Relationship.
*
* Needs to be in a separate file to prevent circular
* dependencies in webpack.
*/
export const RELATIONSHIP = new ResourceType('relationship');

View File

@@ -39,13 +39,13 @@ export abstract class SubmissionObject extends DSpaceObject implements Cacheable
/** /**
* The collection this submission applies to * The collection this submission applies to
*/ */
@link(Collection) @link(Collection.type)
collection?: Observable<RemoteData<Collection>> | Collection; collection?: Observable<RemoteData<Collection>> | Collection;
/** /**
* The submission item * The submission item
*/ */
@link(Item) @link(Item.type)
item?: Observable<RemoteData<Item>> | Item; item?: Observable<RemoteData<Item>> | Item;
/** /**
@@ -56,13 +56,13 @@ export abstract class SubmissionObject extends DSpaceObject implements Cacheable
/** /**
* The configuration object that define this submission * The configuration object that define this submission
*/ */
@link(SubmissionDefinitionsModel) @link(SubmissionDefinitionsModel.type)
submissionDefinition?: Observable<RemoteData<SubmissionDefinitionsModel>> | SubmissionDefinitionsModel; submissionDefinition?: Observable<RemoteData<SubmissionDefinitionsModel>> | SubmissionDefinitionsModel;
/** /**
* The workspaceitem submitter * The workspaceitem submitter
*/ */
@link(EPerson) @link(EPerson.type)
submitter?: Observable<RemoteData<EPerson>> | EPerson; submitter?: Observable<RemoteData<EPerson>> | EPerson;
/** /**

View File

@@ -19,7 +19,7 @@ import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service';
* A service that provides methods to make REST requests with workflowitems endpoint. * A service that provides methods to make REST requests with workflowitems endpoint.
*/ */
@Injectable() @Injectable()
@dataService(WorkflowItem) @dataService(WorkflowItem.type)
export class WorkflowItemDataService extends DataService<WorkflowItem> { export class WorkflowItemDataService extends DataService<WorkflowItem> {
protected linkPath = 'workflowitems'; protected linkPath = 'workflowitems';
protected responseMsToLive = 10 * 1000; protected responseMsToLive = 10 * 1000;

View File

@@ -19,7 +19,7 @@ import { WorkspaceItem } from './models/workspaceitem.model';
* A service that provides methods to make REST requests with workspaceitems endpoint. * A service that provides methods to make REST requests with workspaceitems endpoint.
*/ */
@Injectable() @Injectable()
@dataService(WorkspaceItem) @dataService(WorkspaceItem.type)
export class WorkspaceitemDataService extends DataService<WorkspaceItem> { export class WorkspaceitemDataService extends DataService<WorkspaceItem> {
protected linkPath = 'workspaceitems'; protected linkPath = 'workspaceitems';
protected responseMsToLive = 10 * 1000; protected responseMsToLive = 10 * 1000;

View File

@@ -21,7 +21,7 @@ import { ProcessTaskResponse } from './models/process-task-response';
* The service handling all REST requests for ClaimedTask * The service handling all REST requests for ClaimedTask
*/ */
@Injectable() @Injectable()
@dataService(ClaimedTask) @dataService(ClaimedTask.type)
export class ClaimedTaskDataService extends TasksService<ClaimedTask> { export class ClaimedTaskDataService extends TasksService<ClaimedTask> {
protected responseMsToLive = 10 * 1000; protected responseMsToLive = 10 * 1000;

View File

@@ -34,19 +34,19 @@ export class TaskObject extends DSpaceObject implements CacheableObject {
/** /**
* The group of this task * The group of this task
*/ */
@link(EPerson) @link(EPerson.type)
eperson?: Observable<RemoteData<EPerson>>; eperson?: Observable<RemoteData<EPerson>>;
/** /**
* The group of this task * The group of this task
*/ */
@link(Group) @link(Group.type)
group?: Observable<RemoteData<Group>>; group?: Observable<RemoteData<Group>>;
/** /**
* The workflowitem object whom this task is related * The workflowitem object whom this task is related
*/ */
@link(WorkflowItem) @link(WorkflowItem.type)
workflowitem?: Observable<RemoteData<WorkflowItem>> | WorkflowItem; workflowitem?: Observable<RemoteData<WorkflowItem>> | WorkflowItem;
_links: { _links: {

View File

@@ -21,7 +21,7 @@ import { ProcessTaskResponse } from './models/process-task-response';
* The service handling all REST requests for PoolTask * The service handling all REST requests for PoolTask
*/ */
@Injectable() @Injectable()
@dataService(PoolTask) @dataService(PoolTask.type)
export class PoolTaskDataService extends TasksService<PoolTask> { export class PoolTaskDataService extends TasksService<PoolTask> {
/** /**