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
*/
@link(EPerson)
@link(EPerson.type)
eperson: Observable<RemoteData<EPerson>>;
/**

View File

@@ -88,23 +88,23 @@ export function getRelationships(target: any) {
return relationshipMap.get(target);
}
export function dataService<T extends CacheableObject>(domainModelConstructor: GenericConstructor<T>): any {
export function dataService(resourceType: ResourceType): any {
return (target: any) => {
if (hasNoValue(domainModelConstructor)) {
throw new Error(`Invalid @dataService annotation on ${target}, domainModelConstructor needs to be defined`);
if (hasNoValue(resourceType)) {
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)) {
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>) {
return dataServiceMap.get(domainModelConstructor);
export function getDataServiceFor<T extends CacheableObject>(resourceType: ResourceType) {
return dataServiceMap.get(resourceType.value);
}
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> {
targetConstructor: GenericConstructor<CacheableObject>;
resourceType: ResourceType;
isList = false;
linkName: keyof T['_links'];
propertyName: keyof T;
}
export const link = <T extends HALResource>(
targetConstructor: GenericConstructor<HALResource>,
resourceType: ResourceType,
isList = false,
linkName?: keyof T['_links'],
) => {
console.log('link call', targetConstructor, isList, linkName);
return (target: T, propertyName: string) => {
console.log('link return', targetConstructor, isList, linkName, target, propertyName);
let targetMap = linkMap.get(target.constructor);
if (hasNoValue(targetMap)) {
@@ -160,7 +158,7 @@ export const link = <T extends HALResource>(
}
targetMap.set(propertyName, {
targetConstructor,
resourceType,
isList,
linkName,
propertyName

View File

@@ -21,10 +21,10 @@ export class LinkService {
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}`);
} else {
const provider = getDataServiceFor(matchingLinkDef.targetConstructor);
const provider = getDataServiceFor(matchingLinkDef.resourceType);
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({

View File

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

View File

@@ -27,7 +27,7 @@ import { RequestService } from './request.service';
@Injectable(
{providedIn: 'root'}
)
@dataService(Bundle)
@dataService(Bundle.type)
export class BundleDataService extends DataService<Bundle> {
protected linkPath = 'bundles';
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';
@Injectable()
@dataService(Collection)
@dataService(Collection.type)
export class CollectionDataService extends ComColDataService<Collection> {
protected linkPath = 'collections';
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';
@Injectable()
@dataService(Community)
@dataService(Community.type)
export class CommunityDataService extends ComColDataService<Community> {
protected linkPath = 'communities';
protected topLinkPath = 'communities/search/top';

View File

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

View File

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

View File

@@ -1,6 +1,5 @@
import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { dataService } from '../cache/builders/build-decorators';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { CoreState } from '../core.reducers';
@@ -8,7 +7,6 @@ import { CoreState } from '../core.reducers';
import { DataService } from './data.service';
import { RequestService } from './request.service';
import { HALEndpointService } from '../shared/hal-endpoint.service';
import { FindListOptions } from './request.models';
import { ObjectCacheService } from '../cache/object-cache.service';
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
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
*/
@Injectable()
@dataService(MetadataSchema)
@dataService(MetadataSchema.type)
export class MetadataSchemaDataService {
private dataService: DataServiceImpl;

View File

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

View File

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

View File

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

View File

@@ -14,7 +14,7 @@ import { HALEndpointService } from '../shared/hal-endpoint.service';
import { EPerson } from './models/eperson.model';
@Injectable()
@dataService(EPerson)
@dataService(EPerson.type)
export class EPersonDataService extends DataService<EPerson> {
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
*/
@link(MetadataSchema)
@link(MetadataSchema.type)
// TODO the responseparsingservice assumes schemas are always embedded. This should be remotedata instead.
schema?: MetadataSchema;

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,15 +3,16 @@ import { link } from '../../cache/builders/build-decorators';
import { CacheableObject } from '../../cache/object-cache.reducer';
import { RemoteData } from '../../data/remote-data';
import { HALLink } from '../hal-link.model';
import { ResourceType } from '../resource-type';
import { RelationshipType } from './relationship-type.model';
import { Item } from '../item.model';
import { ITEM } from "../item-resource-type";
import { RELATIONSHIP } from "../relationship.resource-type";
/**
* Describes a Relationship between two Items
*/
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
@@ -32,14 +33,13 @@ export class Relationship implements CacheableObject {
* 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>>;
/**
* The item to the right of this relationship
*/
@link(Item)
@link(ITEM)
rightItem?: Observable<RemoteData<Item>>;
/**
@@ -65,7 +65,7 @@ export class Relationship implements CacheableObject {
/**
* The type of Relationship
*/
@link(RelationshipType)
@link(RelationshipType.type)
relationshipType?: Observable<RemoteData<RelationshipType>>;
_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 { HALLink } from './hal-link.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
*/
export class Item extends DSpaceObject {
static type = new ResourceType('item');
static type = 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
*/
@link(Collection)
@link(Collection.type)
owningCollection: Observable<RemoteData<Collection>>;
@link(Bundle, true)
@link(Bundle.type, true)
bundles: Observable<RemoteData<PaginatedList<Bundle>>>;
@link(Relationship, true)
@link(RELATIONSHIP)
relationships: Observable<RemoteData<PaginatedList<Relationship>>>;
_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
*/
@link(Collection)
@link(Collection.type)
collection?: Observable<RemoteData<Collection>> | Collection;
/**
* The submission item
*/
@link(Item)
@link(Item.type)
item?: Observable<RemoteData<Item>> | Item;
/**
@@ -56,13 +56,13 @@ export abstract class SubmissionObject extends DSpaceObject implements Cacheable
/**
* The configuration object that define this submission
*/
@link(SubmissionDefinitionsModel)
@link(SubmissionDefinitionsModel.type)
submissionDefinition?: Observable<RemoteData<SubmissionDefinitionsModel>> | SubmissionDefinitionsModel;
/**
* The workspaceitem submitter
*/
@link(EPerson)
@link(EPerson.type)
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.
*/
@Injectable()
@dataService(WorkflowItem)
@dataService(WorkflowItem.type)
export class WorkflowItemDataService extends DataService<WorkflowItem> {
protected linkPath = 'workflowitems';
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.
*/
@Injectable()
@dataService(WorkspaceItem)
@dataService(WorkspaceItem.type)
export class WorkspaceitemDataService extends DataService<WorkspaceItem> {
protected linkPath = 'workspaceitems';
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
*/
@Injectable()
@dataService(ClaimedTask)
@dataService(ClaimedTask.type)
export class ClaimedTaskDataService extends TasksService<ClaimedTask> {
protected responseMsToLive = 10 * 1000;

View File

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

View File

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