diff --git a/package.json b/package.json index b4a40c0a19..035022478c 100644 --- a/package.json +++ b/package.json @@ -135,7 +135,7 @@ "ngx-pagination": "3.0.3", "nouislider": "^11.0.0", "pem": "1.13.2", - "reflect-metadata": "0.1.12", + "reflect-metadata": "^0.1.13", "rxjs": "6.4.0", "rxjs-spy": "^7.5.1", "sass-resources-loader": "^2.0.0", diff --git a/src/app/+collection-page/collection-item-mapper/collection-item-mapper.component.ts b/src/app/+collection-page/collection-item-mapper/collection-item-mapper.component.ts index 5c67a78401..44fb0ecfca 100644 --- a/src/app/+collection-page/collection-item-mapper/collection-item-mapper.component.ts +++ b/src/app/+collection-page/collection-item-mapper/collection-item-mapper.component.ts @@ -22,6 +22,7 @@ import { SEARCH_CONFIG_SERVICE } from '../../+my-dspace-page/my-dspace-page.comp import { SearchConfigurationService } from '../../core/shared/search/search-configuration.service'; import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model'; import { SearchService } from '../../core/shared/search/search.service'; +import { followLink } from '../../shared/utils/follow-link-config.model'; @Component({ selector: 'ds-collection-item-mapper', @@ -122,7 +123,7 @@ export class CollectionItemMapperComponent implements OnInit { if (shouldUpdate) { return this.collectionDataService.getMappedItems(collectionRD.payload.id, Object.assign(options, { sort: this.defaultSortOptions - })) + }),followLink('owningCollection')) } }) ); diff --git a/src/app/core/auth/auth-response-parsing.service.ts b/src/app/core/auth/auth-response-parsing.service.ts index 8137734c49..79572fb0d8 100644 --- a/src/app/core/auth/auth-response-parsing.service.ts +++ b/src/app/core/auth/auth-response-parsing.service.ts @@ -10,7 +10,6 @@ import { ObjectCacheService } from '../cache/object-cache.service'; import { ResponseParsingService } from '../data/parsing.service'; import { RestRequest } from '../data/request.models'; import { AuthStatus } from './models/auth-status.model'; -import { NormalizedAuthStatus } from './models/normalized-auth-status.model'; import { NormalizedObject } from '../cache/models/normalized-object.model'; @Injectable() @@ -28,7 +27,7 @@ export class AuthResponseParsingService extends BaseResponseParsingService imple const response = this.process>(data.payload, request); return new AuthStatusResponse(response, data.statusCode, data.statusText); } else { - return new AuthStatusResponse(data.payload as NormalizedAuthStatus, data.statusCode, data.statusText); + return new AuthStatusResponse(data.payload as AuthStatus, data.statusCode, data.statusText); } } } diff --git a/src/app/core/auth/models/auth-status.model.ts b/src/app/core/auth/models/auth-status.model.ts index 9ec8a1323b..86ae9f3a49 100644 --- a/src/app/core/auth/models/auth-status.model.ts +++ b/src/app/core/auth/models/auth-status.model.ts @@ -1,10 +1,14 @@ +import { autoserialize, deserialize, deserializeAs } from 'cerialize'; import { Observable } from 'rxjs'; -import { link } from '../../cache/builders/build-decorators'; +import { link, resourceType } from '../../cache/builders/build-decorators'; +import { IDToUUIDSerializer } from '../../cache/id-to-uuid-serializer'; import { CacheableObject } from '../../cache/object-cache.reducer'; import { RemoteData } from '../../data/remote-data'; import { EPerson } from '../../eperson/models/eperson.model'; import { EPERSON } from '../../eperson/models/eperson.resource-type'; import { HALLink } from '../../shared/hal-link.model'; +import { ResourceType } from '../../shared/resource-type'; +import { excludeFromEquals } from '../../utilities/equals.decorators'; import { AuthError } from './auth-error.model'; import { AUTH_STATUS } from './auth-status.resource-type'; import { AuthTokenInfo } from './auth-token-info.model'; @@ -12,36 +16,55 @@ import { AuthTokenInfo } from './auth-token-info.model'; /** * Object that represents the authenticated status of a user */ +@resourceType(AuthStatus.type) export class AuthStatus implements CacheableObject { static type = AUTH_STATUS; /** * The unique identifier of this auth status */ + @autoserialize id: string; /** - * The unique uuid of this auth status + * The type for this AuthStatus */ + @excludeFromEquals + @autoserialize + type: ResourceType; + + /** + * The UUID of this auth status + * This UUID is generated client-side and isn't used by the backend. + * It is based on the ID, so it will be the same for each refresh. + */ + @deserializeAs(new IDToUUIDSerializer('auth-status'), 'id') uuid: string; /** * True if REST API is up and running, should never return false */ + @autoserialize okay: boolean; /** * If the auth status represents an authenticated state */ + @autoserialize authenticated: boolean; /** - * Authentication error if there was one for this status + * The HALLinks for this AuthStatus */ - error?: AuthError; + @deserialize + _links: { + self: HALLink; + eperson: HALLink; + }; /** - * The eperson of this auth status + * The EPerson of this auth status + * Will be undefined unless the eperson HALLink has been resolved. */ @link(EPERSON) eperson?: Observable>; @@ -49,15 +72,12 @@ export class AuthStatus implements CacheableObject { /** * True if the token is valid, false if there was no token or the token wasn't valid */ + @autoserialize token?: AuthTokenInfo; /** - * The self link of this auth status' REST object + * Authentication error if there was one for this status */ - self: string; - - _links: { - self: HALLink; - eperson: HALLink - } + // TODO should be refactored to use the RemoteData error + error?: AuthError; } diff --git a/src/app/core/auth/models/normalized-auth-status.model.ts b/src/app/core/auth/models/normalized-auth-status.model.ts deleted file mode 100644 index 036b893de2..0000000000 --- a/src/app/core/auth/models/normalized-auth-status.model.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { AuthStatus } from './auth-status.model'; -import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize'; -import { mapsTo, relationship } from '../../cache/builders/build-decorators'; -import { NormalizedObject } from '../../cache/models/normalized-object.model'; -import { IDToUUIDSerializer } from '../../cache/id-to-uuid-serializer'; -import { EPerson } from '../../eperson/models/eperson.model'; - -@mapsTo(AuthStatus) -@inheritSerialization(NormalizedObject) -export class NormalizedAuthStatus extends NormalizedObject { - /** - * The unique identifier of this auth status - */ - @autoserialize - id: string; - - /** - * The unique generated uuid of this auth status - */ - @autoserializeAs(new IDToUUIDSerializer('auth-status'), 'id') - uuid: string; - - /** - * True if REST API is up and running, should never return false - */ - @autoserialize - okay: boolean; - - /** - * True if the token is valid, false if there was no token or the token wasn't valid - */ - @autoserialize - authenticated: boolean; - - /** - * The self link to the eperson of this auth status - */ - @relationship(EPerson, false, false) - @autoserialize - eperson: string; -} diff --git a/src/app/core/cache/builders/build-decorators.ts b/src/app/core/cache/builders/build-decorators.ts index 7dcaa946b2..f02beed8a7 100644 --- a/src/app/core/cache/builders/build-decorators.ts +++ b/src/app/core/cache/builders/build-decorators.ts @@ -30,6 +30,21 @@ export function mapsTo(value: GenericConstructor) { } } +/** + * Decorator function to map a normalized class to it's not-normalized counter part class + * It will also maps a type to the matching class + * @param value The not-normalized class to map to + */ +// export function resourceType(target: any, key: string) { +// typeMap.set(target.key.value, target.constructor); +// } + +export function resourceType(value: ResourceType) { + return function decorator(objectConstructor: GenericConstructor) { + typeMap.set(value.value, objectConstructor); + } +} + /** * Maps a type to the matching class * @param value The resourse type @@ -180,3 +195,18 @@ export const getLinkDefinition = (source: GenericConstruc return undefined; } }; + +export const inheritLinkAnnotations = (parent: any): any => { + return (child: any) => { + const parentMap: Map> = linkMap.get(parent) || new Map(); + const childMap: Map> = linkMap.get(child) || new Map(); + + parentMap.forEach((value, key) => { + if (!childMap.has(key)) { + childMap.set(key, value); + } + }); + + linkMap.set(child, childMap); + } +}; diff --git a/src/app/core/cache/builders/link.service.ts b/src/app/core/cache/builders/link.service.ts index 3ba57d8304..90440c0c83 100644 --- a/src/app/core/cache/builders/link.service.ts +++ b/src/app/core/cache/builders/link.service.ts @@ -1,5 +1,5 @@ import { Injectable, Injector } from '@angular/core'; -import { hasNoValue, isNotEmpty } from '../../../shared/empty.util'; +import { hasNoValue, hasValue, isNotEmpty } from '../../../shared/empty.util'; import { FollowLinkConfig } from '../../../shared/utils/follow-link-config.model'; import { GenericConstructor } from '../../shared/generic-constructor'; import { HALResource } from '../../shared/hal-resource.model'; @@ -40,10 +40,14 @@ export class LinkService { const href = model._links[matchingLinkDef.linkName].href; - if (matchingLinkDef.isList) { - model[linkToFollow.name] = service.findAllByHref(href, linkToFollow.findListOptions, ...linkToFollow.linksToFollow); - } else { - model[linkToFollow.name] = service.findByHref(href, ...linkToFollow.linksToFollow); + try { + if (matchingLinkDef.isList) { + model[linkToFollow.name] = service.findAllByHref(href, linkToFollow.findListOptions, ...linkToFollow.linksToFollow); + } else { + model[linkToFollow.name] = service.findByHref(href, ...linkToFollow.linksToFollow); + } + } catch (e) { + throw new Error(`Something went wrong when using @dataService(${matchingLinkDef.resourceType.value}) ${hasValue(service) ? '' : '(undefined) '}to resolve link ${linkToFollow.name} from ${href}`); } } } diff --git a/src/app/core/cache/builders/remote-data-build.service.ts b/src/app/core/cache/builders/remote-data-build.service.ts index 55538ab70a..582fac532f 100644 --- a/src/app/core/cache/builders/remote-data-build.service.ts +++ b/src/app/core/cache/builders/remote-data-build.service.ts @@ -42,7 +42,7 @@ export class RemoteDataBuildService { * Creates a single {@link RemoteData} object based on the response of a request to the REST server, with a list of * {@link FollowLinkConfig} that indicate which embedded info should be added to the object * @param href$ Observable href of object we want to retrieve - * @param linksToFollow List of {@link FollowLinkConfig} that indicate which embedded info should be added + * @param linksToFollow List of {@link FollowLinkConfig} that indicate which HALLinks should be automatically resolved */ buildSingle(href$: string | Observable, ...linksToFollow: Array>): Observable> { if (typeof href$ === 'string') { @@ -126,7 +126,7 @@ export class RemoteDataBuildService { * Creates a list of {@link RemoteData} objects based on the response of a request to the REST server, with a list of * {@link FollowLinkConfig} that indicate which embedded info should be added to the objects * @param href$ Observable href of objects we want to retrieve - * @param linksToFollow List of {@link FollowLinkConfig} that indicate which embedded info should be added + * @param linksToFollow List of {@link FollowLinkConfig} that indicate which HALLinks should be automatically resolved */ buildList(href$: string | Observable, ...linksToFollow: Array>): Observable>> { if (typeof href$ === 'string') { @@ -229,8 +229,13 @@ export class RemoteDataBuildService { } } }); + let domainModel; const domainModelConstructor = getMapsTo(normalized.constructor); - const domainModel = Object.assign(new domainModelConstructor(), normalized, halLinks); + if(hasValue(domainModelConstructor) && domainModelConstructor !== normalized.constructor) { + domainModel = Object.assign(new domainModelConstructor(), normalized, halLinks); + } else { + domainModel = normalized; + } this.linkService.resolveLinks(domainModel, ...linksToFollow); diff --git a/src/app/core/cache/models/items/normalized-item-type.model.ts b/src/app/core/cache/models/items/normalized-item-type.model.ts deleted file mode 100644 index fdb3b9e455..0000000000 --- a/src/app/core/cache/models/items/normalized-item-type.model.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize'; -import { ItemType } from '../../../shared/item-relationships/item-type.model'; -import { mapsTo } from '../../builders/build-decorators'; -import { NormalizedObject } from '../normalized-object.model'; -import { IDToUUIDSerializer } from '../../id-to-uuid-serializer'; - -/** - * Normalized model class for a DSpace ItemType - */ -@mapsTo(ItemType) -@inheritSerialization(NormalizedObject) -export class NormalizedItemType extends NormalizedObject { - /** - * The label that describes the ResourceType of the Item - */ - @autoserialize - label: string; - - /** - * The identifier of this ItemType - */ - @autoserialize - id: string; - - /** - * The universally unique identifier of this ItemType - */ - @autoserializeAs(new IDToUUIDSerializer(ItemType.type.value), 'id') - uuid: string; -} diff --git a/src/app/core/cache/models/items/normalized-relationship-type.model.ts b/src/app/core/cache/models/items/normalized-relationship-type.model.ts deleted file mode 100644 index 23c3333a9b..0000000000 --- a/src/app/core/cache/models/items/normalized-relationship-type.model.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize'; -import { RelationshipType } from '../../../shared/item-relationships/relationship-type.model'; -import { ResourceType } from '../../../shared/resource-type'; -import { mapsTo, relationship } from '../../builders/build-decorators'; -import { NormalizedDSpaceObject } from '../normalized-dspace-object.model'; -import { NormalizedObject } from '../normalized-object.model'; -import { IDToUUIDSerializer } from '../../id-to-uuid-serializer'; -import { ItemType } from '../../../shared/item-relationships/item-type.model'; - -/** - * Normalized model class for a DSpace RelationshipType - */ -@mapsTo(RelationshipType) -@inheritSerialization(NormalizedDSpaceObject) -export class NormalizedRelationshipType extends NormalizedObject { - /** - * The identifier of this RelationshipType - */ - @autoserialize - id: string; - - /** - * The label that describes the Relation to the left of this RelationshipType - */ - @autoserialize - leftwardType: string; - - /** - * The maximum amount of Relationships allowed to the left of this RelationshipType - */ - @autoserialize - leftMaxCardinality: number; - - /** - * The minimum amount of Relationships allowed to the left of this RelationshipType - */ - @autoserialize - leftMinCardinality: number; - - /** - * The label that describes the Relation to the right of this RelationshipType - */ - @autoserialize - rightwardType: string; - - /** - * The maximum amount of Relationships allowed to the right of this RelationshipType - */ - @autoserialize - rightMaxCardinality: number; - - /** - * The minimum amount of Relationships allowed to the right of this RelationshipType - */ - @autoserialize - rightMinCardinality: number; - - /** - * The type of Item found to the left of this RelationshipType - */ - @autoserialize - @relationship(ItemType, false) - leftType: string; - - /** - * The type of Item found to the right of this RelationshipType - */ - @autoserialize - @relationship(ItemType, false) - rightType: string; - - /** - * The universally unique identifier of this RelationshipType - */ - @autoserializeAs(new IDToUUIDSerializer(RelationshipType.type.value), 'id') - uuid: string; -} diff --git a/src/app/core/cache/models/items/normalized-relationship.model.ts b/src/app/core/cache/models/items/normalized-relationship.model.ts deleted file mode 100644 index 51985fb2a9..0000000000 --- a/src/app/core/cache/models/items/normalized-relationship.model.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { autoserialize, deserialize, deserializeAs, inheritSerialization } from 'cerialize'; -import { Relationship } from '../../../shared/item-relationships/relationship.model'; -import { mapsTo, relationship } from '../../builders/build-decorators'; -import { NormalizedObject } from '../normalized-object.model'; -import { IDToUUIDSerializer } from '../../id-to-uuid-serializer'; -import { RelationshipType } from '../../../shared/item-relationships/relationship-type.model'; -import { Item } from '../../../shared/item.model'; - -/** - * Normalized model class for a DSpace Relationship - */ -@mapsTo(Relationship) -@inheritSerialization(NormalizedObject) -export class NormalizedRelationship extends NormalizedObject { - - /** - * The identifier of this Relationship - */ - @deserialize - id: string; - - /** - * The item to the left of this relationship - */ - @deserialize - @relationship(Item, false, false) - leftItem: string; - - /** - * The item to the right of this relationship - */ - @deserialize - @relationship(Item, false, false) - rightItem: string; - - /** - * The place of the Item to the left side of this Relationship - */ - @autoserialize - leftPlace: number; - - /** - * The place of the Item to the right side of this Relationship - */ - @autoserialize - rightPlace: number; - - /** - * The name variant of the Item to the left side of this Relationship - */ - @autoserialize - leftwardValue: string; - - /** - * The name variant of the Item to the right side of this Relationship - */ - @autoserialize - rightwardValue: string; - - /** - * The type of Relationship - */ - @deserialize - @relationship(RelationshipType, false, false) - relationshipType: string; - - /** - * The universally unique identifier of this Relationship - */ - @deserializeAs(new IDToUUIDSerializer(Relationship.type.value), 'id') - uuid: string; -} diff --git a/src/app/core/cache/models/normalized-bitstream-format.model.ts b/src/app/core/cache/models/normalized-bitstream-format.model.ts deleted file mode 100644 index 2283ecb368..0000000000 --- a/src/app/core/cache/models/normalized-bitstream-format.model.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize'; -import { BitstreamFormat } from '../../shared/bitstream-format.model'; - -import { mapsTo } from '../builders/build-decorators'; -import { IDToUUIDSerializer } from '../id-to-uuid-serializer'; -import { NormalizedObject } from './normalized-object.model'; -import { BitstreamFormatSupportLevel } from '../../shared/bitstream-format-support-level'; - -/** - * Normalized model class for a Bitstream Format - */ -@mapsTo(BitstreamFormat) -@inheritSerialization(NormalizedObject) -export class NormalizedBitstreamFormat extends NormalizedObject { - /** - * Short description of this Bitstream Format - */ - @autoserialize - shortDescription: string; - - /** - * Description of this Bitstream Format - */ - @autoserialize - description: string; - - /** - * String representing the MIME type of this Bitstream Format - */ - @autoserialize - mimetype: string; - - /** - * The level of support the system offers for this Bitstream Format - */ - @autoserialize - supportLevel: BitstreamFormatSupportLevel; - - /** - * True if the Bitstream Format is used to store system information, rather than the content of items in the system - */ - @autoserialize - internal: boolean; - - /** - * String representing this Bitstream Format's file extension - */ - @autoserialize - extensions: string[]; - - /** - * Identifier for this Bitstream Format - * Note that this ID is unique for bitstream formats, - * but might not be unique across different object types - */ - @autoserialize - id: string; - - /** - * Universally unique identifier for this Bitstream Format - * Consist of a prefix and the id field to ensure the identifier is unique across all object types - */ - @autoserializeAs(new IDToUUIDSerializer('bitstream-format'), 'id') - uuid: string; -} diff --git a/src/app/core/cache/models/normalized-bitstream.model.ts b/src/app/core/cache/models/normalized-bitstream.model.ts deleted file mode 100644 index 028c1a24de..0000000000 --- a/src/app/core/cache/models/normalized-bitstream.model.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { autoserialize, inheritSerialization } from 'cerialize'; - -import { NormalizedDSpaceObject } from './normalized-dspace-object.model'; -import { Bitstream } from '../../shared/bitstream.model'; -import { mapsTo, relationship } from '../builders/build-decorators'; -import { Item } from '../../shared/item.model'; -import { BitstreamFormat } from '../../shared/bitstream-format.model'; - -/** - * Normalized model class for a DSpace Bitstream - */ -@mapsTo(Bitstream) -@inheritSerialization(NormalizedDSpaceObject) -export class NormalizedBitstream extends NormalizedDSpaceObject { - /** - * The size of this bitstream in bytes - */ - @autoserialize - sizeBytes: number; - - /** - * The relative path to this Bitstream's file - */ - @autoserialize - @relationship(Bitstream, false, false) - content: string; - - /** - * The format of this Bitstream - */ - @autoserialize - @relationship(BitstreamFormat, false, false) - format: string; - - /** - * The description of this Bitstream - */ - @autoserialize - description: string; - - /** - * An array of Bundles that are direct parents of this Bitstream - */ - @autoserialize - @relationship(Item, true, false) - parents: string[]; - - /** - * The Bundle that owns this Bitstream - */ - @autoserialize - @relationship(Item, false, false) - owner: string; - - /** - * The name of the Bundle this Bitstream is part of - */ - @autoserialize - bundleName: string; - -} diff --git a/src/app/core/cache/models/normalized-bundle.model.ts b/src/app/core/cache/models/normalized-bundle.model.ts deleted file mode 100644 index 731bbcad8e..0000000000 --- a/src/app/core/cache/models/normalized-bundle.model.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { autoserialize, inheritSerialization } from 'cerialize'; -import { Bitstream } from '../../shared/bitstream.model'; -import { Bundle } from '../../shared/bundle.model'; -import { mapsTo, relationship } from '../builders/build-decorators'; - -import { NormalizedDSpaceObject } from './normalized-dspace-object.model'; - -/** - * Normalized model class for a DSpace Bundle - */ -@mapsTo(Bundle) -@inheritSerialization(NormalizedDSpaceObject) -export class NormalizedBundle extends NormalizedDSpaceObject { - - /** - * The bundle's name - */ - @autoserialize - name: string; - - /** - * The primary bitstream of this Bundle - */ - @autoserialize - @relationship(Bitstream, false, false) - primaryBitstream: string; - - /** - * List of Bitstreams that are part of this Bundle - */ - @autoserialize - @relationship(Bitstream, true, false) - bitstreams: string[]; - -} diff --git a/src/app/core/cache/models/normalized-collection.model.ts b/src/app/core/cache/models/normalized-collection.model.ts deleted file mode 100644 index a19a4effba..0000000000 --- a/src/app/core/cache/models/normalized-collection.model.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { autoserialize, deserialize, inheritSerialization } from 'cerialize'; -import { Bitstream } from '../../shared/bitstream.model'; -import { Collection } from '../../shared/collection.model'; -import { Item } from '../../shared/item.model'; -import { ResourcePolicy } from '../../shared/resource-policy.model'; -import { mapsTo, relationship } from '../builders/build-decorators'; - -import { NormalizedDSpaceObject } from './normalized-dspace-object.model'; - -/** - * Normalized model class for a DSpace Collection - */ -@mapsTo(Collection) -@inheritSerialization(NormalizedDSpaceObject) -export class NormalizedCollection extends NormalizedDSpaceObject { - - /** - * A string representing the unique handle of this Collection - */ - @autoserialize - handle: string; - - /** - * The Bitstream that represents the license of this Collection - */ - @autoserialize - license: string; - - /** - * The Bitstream that represents the default Access Conditions of this Collection - */ - @autoserialize - @relationship(ResourcePolicy, false, false) - defaultAccessConditions: string; - - /** - * The Bitstream that represents the logo of this Collection - */ - @deserialize - @relationship(Bitstream, false, false) - logo: string; - - /** - * List of Items that are part of (not necessarily owned by) this Collection - */ - @deserialize - @relationship(Item, true, false) - items: string[]; - -} diff --git a/src/app/core/cache/models/normalized-community.model.ts b/src/app/core/cache/models/normalized-community.model.ts deleted file mode 100644 index 1f4e5d5803..0000000000 --- a/src/app/core/cache/models/normalized-community.model.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { autoserialize, deserialize, inheritSerialization } from 'cerialize'; - -import { NormalizedDSpaceObject } from './normalized-dspace-object.model'; -import { Community } from '../../shared/community.model'; -import { mapsTo, relationship } from '../builders/build-decorators'; -import { ResourceType } from '../../shared/resource-type'; -import { NormalizedBitstream } from './normalized-bitstream.model'; -import { NormalizedCollection } from './normalized-collection.model'; -import { Bitstream } from '../../shared/bitstream.model'; -import { Collection } from '../../shared/collection.model'; - -/** - * Normalized model class for a DSpace Community - */ -@mapsTo(Community) -@inheritSerialization(NormalizedDSpaceObject) -export class NormalizedCommunity extends NormalizedDSpaceObject { - /** - * A string representing the unique handle of this Community - */ - @autoserialize - handle: string; - - /** - * The Bitstream that represents the logo of this Community - */ - @deserialize - @relationship(Bitstream, false, false) - logo: string; - - /** - * An array of Communities that are direct parents of this Community - */ - @deserialize - @relationship(Community, true, false) - parents: string[]; - - /** - * The Community that owns this Community - */ - @deserialize - @relationship(Community, false, false) - owner: string; - - /** - * List of Collections that are owned by this Community - */ - @deserialize - @relationship(Collection, true, false) - collections: string[]; - - @deserialize - @relationship(Community, true, false) - subcommunities: string[]; - -} diff --git a/src/app/core/cache/models/normalized-dspace-object.model.ts b/src/app/core/cache/models/normalized-dspace-object.model.ts index 4aaccec8aa..2282947bfc 100644 --- a/src/app/core/cache/models/normalized-dspace-object.model.ts +++ b/src/app/core/cache/models/normalized-dspace-object.model.ts @@ -2,14 +2,13 @@ import { autoserializeAs, deserializeAs, autoserialize } from 'cerialize'; import { DSpaceObject } from '../../shared/dspace-object.model'; import { HALLink } from '../../shared/hal-link.model'; import { MetadataMap, MetadataMapSerializer } from '../../shared/metadata.models'; -import { mapsTo } from '../builders/build-decorators'; +import { ResourceType } from '../../shared/resource-type'; import { NormalizedObject } from './normalized-object.model'; import { TypedObject } from '../object-cache.reducer'; /** * An model class for a DSpaceObject. */ -@mapsTo(DSpaceObject) export class NormalizedDSpaceObject extends NormalizedObject implements TypedObject { /** @@ -37,10 +36,10 @@ export class NormalizedDSpaceObject extends NormalizedOb uuid: string; /** - * A string representing the kind of DSpaceObject, e.g. community, item, … + * The type of the object */ @autoserialize - type: string; + type: ResourceType; /** * All metadata of this DSpaceObject diff --git a/src/app/core/cache/models/normalized-external-source-entry.model.ts b/src/app/core/cache/models/normalized-external-source-entry.model.ts deleted file mode 100644 index de262949e7..0000000000 --- a/src/app/core/cache/models/normalized-external-source-entry.model.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize'; -import { NormalizedObject } from './normalized-object.model'; -import { ExternalSourceEntry } from '../../shared/external-source-entry.model'; -import { mapsTo } from '../builders/build-decorators'; -import { MetadataMap, MetadataMapSerializer } from '../../shared/metadata.models'; - -/** - * Normalized model class for an external source entry - */ -@mapsTo(ExternalSourceEntry) -@inheritSerialization(NormalizedObject) -export class NormalizedExternalSourceEntry extends NormalizedObject { - /** - * Unique identifier - */ - @autoserialize - id: string; - - /** - * The value to display - */ - @autoserialize - display: string; - - /** - * The value to store the entry with - */ - @autoserialize - value: string; - - /** - * The ID of the external source this entry originates from - */ - @autoserialize - externalSource: string; - - /** - * Metadata of the entry - */ - @autoserializeAs(MetadataMapSerializer) - metadata: MetadataMap; -} diff --git a/src/app/core/cache/models/normalized-external-source.model.ts b/src/app/core/cache/models/normalized-external-source.model.ts deleted file mode 100644 index fd9a42fb72..0000000000 --- a/src/app/core/cache/models/normalized-external-source.model.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { autoserialize, inheritSerialization } from 'cerialize'; -import { NormalizedObject } from './normalized-object.model'; -import { ExternalSource } from '../../shared/external-source.model'; -import { mapsTo } from '../builders/build-decorators'; - -/** - * Normalized model class for an external source - */ -@mapsTo(ExternalSource) -@inheritSerialization(NormalizedObject) -export class NormalizedExternalSource extends NormalizedObject { - /** - * Unique identifier - */ - @autoserialize - id: string; - - /** - * The name of this external source - */ - @autoserialize - name: string; - - /** - * Is the source hierarchical? - */ - @autoserialize - hierarchical: boolean; -} diff --git a/src/app/core/cache/models/normalized-item.model.ts b/src/app/core/cache/models/normalized-item.model.ts deleted file mode 100644 index 878b3b422f..0000000000 --- a/src/app/core/cache/models/normalized-item.model.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { inheritSerialization, deserialize, autoserialize, autoserializeAs } from 'cerialize'; - -import { NormalizedDSpaceObject } from './normalized-dspace-object.model'; -import { Item } from '../../shared/item.model'; -import { mapsTo, relationship } from '../builders/build-decorators'; -import { Collection } from '../../shared/collection.model'; -import { Relationship } from '../../shared/item-relationships/relationship.model'; -import { Bundle } from '../../shared/bundle.model'; - -/** - * Normalized model class for a DSpace Item - */ -@mapsTo(Item) -@inheritSerialization(NormalizedDSpaceObject) -export class NormalizedItem extends NormalizedDSpaceObject { - - /** - * A string representing the unique handle of this Item - */ - @autoserialize - handle: string; - - /** - * The Date of the last modification of this Item - */ - @deserialize - lastModified: Date; - - /** - * A boolean representing if this Item is currently archived or not - */ - @autoserializeAs(Boolean, 'inArchive') - isArchived: boolean; - - /** - * A boolean representing if this Item is currently discoverable or not - */ - @autoserializeAs(Boolean, 'discoverable') - isDiscoverable: boolean; - - /** - * A boolean representing if this Item is currently withdrawn or not - */ - @autoserializeAs(Boolean, 'withdrawn') - isWithdrawn: boolean; - - /** - * The Collection that owns this Item - */ - @deserialize - @relationship(Collection, false, false) - owningCollection: string; - - /** - * List of Bitstreams that are owned by this Item - */ - @deserialize - @relationship(Bundle, true, false) - bundles: string[]; - - @deserialize - @relationship(Relationship, true, false) - relationships: string[]; - -} diff --git a/src/app/core/cache/models/normalized-license.model.ts b/src/app/core/cache/models/normalized-license.model.ts deleted file mode 100644 index 02bd1808c8..0000000000 --- a/src/app/core/cache/models/normalized-license.model.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { autoserialize, inheritSerialization } from 'cerialize'; -import { mapsTo } from '../builders/build-decorators'; -import { NormalizedDSpaceObject } from './normalized-dspace-object.model'; -import { License } from '../../shared/license.model'; - -/** - * Normalized model class for a Collection License - */ -@mapsTo(License) -@inheritSerialization(NormalizedDSpaceObject) -export class NormalizedLicense extends NormalizedDSpaceObject { - - /** - * A boolean representing if this License is custom or not - */ - @autoserialize - custom: boolean; - - /** - * The text of the license - */ - @autoserialize - text: string; -} diff --git a/src/app/core/cache/models/normalized-object.model.ts b/src/app/core/cache/models/normalized-object.model.ts index 323433997a..21de0de1f0 100644 --- a/src/app/core/cache/models/normalized-object.model.ts +++ b/src/app/core/cache/models/normalized-object.model.ts @@ -22,5 +22,5 @@ export abstract class NormalizedObject implements Cacheab * A string representing the kind of object */ @deserialize - type: string; + type: ResourceType; } diff --git a/src/app/core/cache/models/normalized-resource-policy.model.ts b/src/app/core/cache/models/normalized-resource-policy.model.ts deleted file mode 100644 index cd25a0af05..0000000000 --- a/src/app/core/cache/models/normalized-resource-policy.model.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize'; -import { ResourcePolicy } from '../../shared/resource-policy.model'; - -import { mapsTo } from '../builders/build-decorators'; -import { NormalizedObject } from './normalized-object.model'; -import { IDToUUIDSerializer } from '../id-to-uuid-serializer'; -import { ActionType } from './action-type.model'; - -/** - * Normalized model class for a Resource Policy - */ -@mapsTo(ResourcePolicy) -@inheritSerialization(NormalizedObject) -export class NormalizedResourcePolicy extends NormalizedObject { - /** - * The action that is allowed by this Resource Policy - */ - @autoserialize - action: ActionType; - - /** - * The name for this Resource Policy - */ - @autoserialize - name: string; - - /** - * The uuid of the Group this Resource Policy applies to - */ - @autoserialize - groupUUID: string; - - /** - * Identifier for this Resource Policy - * Note that this ID is unique for resource policies, - * but might not be unique across different object types - */ - @autoserialize - id: string; - - /** - * The universally unique identifier for this Resource Policy - * Consist of a prefix and the id field to ensure the identifier is unique across all object types - */ - @autoserializeAs(new IDToUUIDSerializer('resource-policy'), 'id') - uuid: string; - -} diff --git a/src/app/core/cache/models/normalized-site.model.ts b/src/app/core/cache/models/normalized-site.model.ts deleted file mode 100644 index 68a7e0a480..0000000000 --- a/src/app/core/cache/models/normalized-site.model.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { inheritSerialization } from 'cerialize'; -import { NormalizedDSpaceObject } from './normalized-dspace-object.model'; -import { mapsTo } from '../builders/build-decorators'; -import { Site } from '../../shared/site.model'; - -/** - * Normalized model class for a Site object - */ -@mapsTo(Site) -@inheritSerialization(NormalizedDSpaceObject) -export class NormalizedSite extends NormalizedDSpaceObject { - -} diff --git a/src/app/core/cache/object-cache.reducer.ts b/src/app/core/cache/object-cache.reducer.ts index 0a41701df0..a39ceb4e16 100644 --- a/src/app/core/cache/object-cache.reducer.ts +++ b/src/app/core/cache/object-cache.reducer.ts @@ -1,5 +1,7 @@ +import { autoserialize, deserialize } from 'cerialize'; import { HALLink } from '../shared/hal-link.model'; import { HALResource } from '../shared/hal-resource.model'; +import { excludeFromEquals } from '../utilities/equals.decorators'; import { ObjectCacheAction, ObjectCacheActionTypes, @@ -36,6 +38,7 @@ export interface Patch { export abstract class TypedObject { static type: ResourceType; + type: ResourceType; } /* tslint:disable:max-classes-per-file */ @@ -47,7 +50,6 @@ export abstract class TypedObject { export class CacheableObject extends TypedObject implements HALResource { uuid?: string; handle?: string; - self: string; _links: { self: HALLink; @@ -135,9 +137,9 @@ export function objectCacheReducer(state = initialState, action: ObjectCacheActi * the new state, with the object added, or overwritten. */ function addToObjectCache(state: ObjectCacheState, action: AddToObjectCacheAction): ObjectCacheState { - const existing = state[action.payload.objectToCache.self]; + const existing = state[action.payload.objectToCache._links.self.href]; return Object.assign({}, state, { - [action.payload.objectToCache.self]: { + [action.payload.objectToCache._links.self.href]: { data: action.payload.objectToCache, timeAdded: action.payload.timeAdded, msToLive: action.payload.msToLive, diff --git a/src/app/core/cache/object-cache.service.spec.ts b/src/app/core/cache/object-cache.service.spec.ts index 1b7a7ad077..47b419e6a4 100644 --- a/src/app/core/cache/object-cache.service.spec.ts +++ b/src/app/core/cache/object-cache.service.spec.ts @@ -6,7 +6,6 @@ import { first } from 'rxjs/operators'; import { CoreState } from '../core.reducers'; import { RestRequestMethod } from '../data/rest-request-method'; import { Item } from '../shared/item.model'; -import { NormalizedItem } from './models/normalized-item.model'; import { AddPatchObjectCacheAction, AddToObjectCacheAction, @@ -95,7 +94,7 @@ describe('ObjectCacheService', () => { service.getObjectBySelfLink(selfLink).pipe(first()).subscribe((o) => { expect(o.self).toBe(selfLink); // this only works if testObj is an instance of TestClass - expect(o instanceof NormalizedItem).toBeTruthy(); + expect(o instanceof Item).toBeTruthy(); } ); }); diff --git a/src/app/core/cache/object-cache.service.ts b/src/app/core/cache/object-cache.service.ts index a086a9bf31..b9a164d7af 100644 --- a/src/app/core/cache/object-cache.service.ts +++ b/src/app/core/cache/object-cache.service.ts @@ -116,6 +116,9 @@ export class ObjectCacheService { ), map((entry: ObjectCacheEntry) => { const type: GenericConstructor> = getMapsToType((entry.data as any).type); + if (typeof type !== 'function') { + throw new Error(`${type} is not a valid constructor for ${JSON.stringify(entry.data)}`); + } return Object.assign(new type(), entry.data) as NormalizedObject }) ); @@ -259,7 +262,7 @@ export class ObjectCacheService { const timeOutdated = entry.timeAdded + entry.msToLive; const isOutDated = new Date().getTime() > timeOutdated; if (isOutDated) { - this.store.dispatch(new RemoveFromObjectCacheAction(entry.data.self)); + this.store.dispatch(new RemoveFromObjectCacheAction(entry.data._links.self.href)); } return !isOutDated; } diff --git a/src/app/core/cache/response.models.ts b/src/app/core/cache/response.models.ts index 5f4e15e138..3f46ecf647 100644 --- a/src/app/core/cache/response.models.ts +++ b/src/app/core/cache/response.models.ts @@ -1,4 +1,5 @@ import { SearchQueryResponse } from '../../shared/search/search-query-response.model'; +import { AuthStatus } from '../auth/models/auth-status.model'; import { RequestError } from '../data/request.models'; import { PageInfo } from '../shared/page-info.model'; import { ConfigObject } from '../config/models/config.model'; @@ -11,7 +12,6 @@ import { RegistryBitstreamformatsResponse } from '../registry/registry-bitstream import { PaginatedList } from '../data/paginated-list'; import { SubmissionObject } from '../submission/models/submission-object.model'; import { DSpaceObject } from '../shared/dspace-object.model'; -import { NormalizedAuthStatus } from '../auth/models/normalized-auth-status.model'; import { MetadataSchema } from '../metadata/metadata-schema.model'; import { MetadataField } from '../metadata/metadata-field.model'; import { ContentSource } from '../shared/content-source.model'; @@ -203,7 +203,7 @@ export class AuthStatusResponse extends RestResponse { public toCache = false; constructor( - public response: NormalizedAuthStatus, + public response: AuthStatus, public statusCode: number, public statusText: string, ) { diff --git a/src/app/core/cache/server-sync-buffer.effects.ts b/src/app/core/cache/server-sync-buffer.effects.ts index 3aa6ad312f..c70f3ad8e1 100644 --- a/src/app/core/cache/server-sync-buffer.effects.ts +++ b/src/app/core/cache/server-sync-buffer.effects.ts @@ -18,7 +18,7 @@ import { RequestService } from '../data/request.service'; import { PutRequest } from '../data/request.models'; import { ObjectCacheService } from './object-cache.service'; import { ApplyPatchObjectCacheAction } from './object-cache.actions'; -import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer'; +import { NormalizedObjectSerializer } from '../dspace-rest-v2/normalized-object.serializer'; import { GenericConstructor } from '../shared/generic-constructor'; import { hasValue, isNotEmpty, isNotUndefined } from '../../shared/empty.util'; import { Observable } from 'rxjs/internal/Observable'; @@ -100,7 +100,7 @@ export class ServerSyncBufferEffects { return patchObject.pipe( map((object) => { - const serializedObject = new DSpaceRESTv2Serializer(object.constructor as GenericConstructor<{}>).serialize(object); + const serializedObject = new NormalizedObjectSerializer(object.constructor as GenericConstructor<{}>).serialize(object); this.requestService.configure(new PutRequest(this.requestService.generateRequestId(), href, serializedObject)); diff --git a/src/app/core/config/models/config-submission-definition.model.ts b/src/app/core/config/models/config-submission-definition.model.ts index 57c980d4b6..54ca948e62 100644 --- a/src/app/core/config/models/config-submission-definition.model.ts +++ b/src/app/core/config/models/config-submission-definition.model.ts @@ -1,3 +1,5 @@ +import { autoserialize, deserialize } from 'cerialize'; +import { resourceType } from '../../cache/builders/build-decorators'; import { PaginatedList } from '../../data/paginated-list'; import { HALLink } from '../../shared/hal-link.model'; import { ResourceType } from '../../shared/resource-type'; @@ -7,22 +9,27 @@ import { ConfigObject } from './config.model'; /** * Class for the configuration describing the submission */ +@resourceType(SubmissionDefinitionModel.type) export class SubmissionDefinitionModel extends ConfigObject { static type = new ResourceType('submissiondefinition'); /** * A boolean representing if this submission definition is the default or not */ + @autoserialize isDefault: boolean; /** * A list of SubmissionSectionModel that are present in this submission definition */ + // TODO refactor using remotedata + @deserialize sections: PaginatedList; /** * The links to all related resources returned by the rest api. */ + @deserialize _links: { self: HALLink, collections: HALLink, diff --git a/src/app/core/config/models/config-submission-definitions.model.ts b/src/app/core/config/models/config-submission-definitions.model.ts index d9892f542f..4554fa172d 100644 --- a/src/app/core/config/models/config-submission-definitions.model.ts +++ b/src/app/core/config/models/config-submission-definitions.model.ts @@ -1,6 +1,8 @@ +import { resourceType } from '../../cache/builders/build-decorators'; import { SubmissionDefinitionModel } from './config-submission-definition.model'; import { ResourceType } from '../../shared/resource-type'; +@resourceType(SubmissionDefinitionsModel.type) export class SubmissionDefinitionsModel extends SubmissionDefinitionModel { static type = new ResourceType('submissiondefinitions'); diff --git a/src/app/core/config/models/config-submission-form.model.ts b/src/app/core/config/models/config-submission-form.model.ts index a65d285c95..3fac904d45 100644 --- a/src/app/core/config/models/config-submission-form.model.ts +++ b/src/app/core/config/models/config-submission-form.model.ts @@ -1,3 +1,5 @@ +import { autoserialize, inheritSerialization } from 'cerialize'; +import { resourceType } from '../../cache/builders/build-decorators'; import { ConfigObject } from './config.model'; import { FormFieldModel } from '../../../shared/form/builder/models/form-field.model'; import { ResourceType } from '../../shared/resource-type'; @@ -12,11 +14,14 @@ export interface FormRowModel { /** * A model class for a NormalizedObject. */ +@resourceType(SubmissionFormModel.type) +@inheritSerialization(ConfigObject) export class SubmissionFormModel extends ConfigObject { static type = new ResourceType('submissionform'); /** * An array of [FormRowModel] that are present in this form */ + @autoserialize rows: FormRowModel[]; } diff --git a/src/app/core/config/models/config-submission-forms.model.ts b/src/app/core/config/models/config-submission-forms.model.ts index 017d7d68cc..6f688061f4 100644 --- a/src/app/core/config/models/config-submission-forms.model.ts +++ b/src/app/core/config/models/config-submission-forms.model.ts @@ -1,9 +1,11 @@ +import { resourceType } from '../../cache/builders/build-decorators'; import { SubmissionFormModel } from './config-submission-form.model'; import { ResourceType } from '../../shared/resource-type'; /** * A model class for a NormalizedObject. */ +@resourceType(SubmissionFormsModel.type) export class SubmissionFormsModel extends SubmissionFormModel { static type = new ResourceType('submissionforms'); } diff --git a/src/app/core/config/models/config-submission-section.model.ts b/src/app/core/config/models/config-submission-section.model.ts index 4c560fa631..f1881734d9 100644 --- a/src/app/core/config/models/config-submission-section.model.ts +++ b/src/app/core/config/models/config-submission-section.model.ts @@ -1,3 +1,6 @@ +import { autoserialize, deserialize } from 'cerialize'; +import { resourceType } from '../../cache/builders/build-decorators'; +import { HALLink } from '../../shared/hal-link.model'; import { ConfigObject } from './config.model'; import { SectionsType } from '../../../submission/sections/sections-type'; import { ResourceType } from '../../shared/resource-type'; @@ -10,27 +13,40 @@ export interface SubmissionSectionVisibility { other: any } +@resourceType(SubmissionSectionModel.type) export class SubmissionSectionModel extends ConfigObject { static type = new ResourceType('submissionsection'); /** * The header for this section */ + @autoserialize header: string; /** * A boolean representing if this submission section is the mandatory or not */ + @autoserialize mandatory: boolean; /** * A string representing the kind of section object */ + @autoserialize sectionType: SectionsType; /** * The [SubmissionSectionVisibility] object for this section */ - visibility: SubmissionSectionVisibility + @autoserialize + visibility: SubmissionSectionVisibility; + + /** + * The HALLinks for this SubmissionSectionModel + */ + @deserialize + _links: { + self: HALLink; + } } diff --git a/src/app/core/config/models/config-submission-sections.model.ts b/src/app/core/config/models/config-submission-sections.model.ts index ae7b133391..870468213c 100644 --- a/src/app/core/config/models/config-submission-sections.model.ts +++ b/src/app/core/config/models/config-submission-sections.model.ts @@ -1,6 +1,8 @@ +import { resourceType } from '../../cache/builders/build-decorators'; import { SubmissionSectionModel } from './config-submission-section.model'; import { ResourceType } from '../../shared/resource-type'; +@resourceType(SubmissionSectionsModel.type) export class SubmissionSectionsModel extends SubmissionSectionModel { static type = new ResourceType('submissionsections'); } diff --git a/src/app/core/config/models/config-submission-uploads.model.ts b/src/app/core/config/models/config-submission-uploads.model.ts index 812a590041..9b59ab6cc7 100644 --- a/src/app/core/config/models/config-submission-uploads.model.ts +++ b/src/app/core/config/models/config-submission-uploads.model.ts @@ -1,8 +1,10 @@ +import { resourceType } from '../../cache/builders/build-decorators'; import { ConfigObject } from './config.model'; import { AccessConditionOption } from './config-access-condition-option.model'; import { SubmissionFormsModel } from './config-submission-forms.model'; import { ResourceType } from '../../shared/resource-type'; +@resourceType(SubmissionUploadsModel.type) export class SubmissionUploadsModel extends ConfigObject { static type = new ResourceType('submissionupload'); /** diff --git a/src/app/core/config/models/config.model.ts b/src/app/core/config/models/config.model.ts index eb21c629bd..98ba3e147f 100644 --- a/src/app/core/config/models/config.model.ts +++ b/src/app/core/config/models/config.model.ts @@ -1,5 +1,8 @@ +import { autoserialize, deserialize } from 'cerialize'; import { CacheableObject } from '../../cache/object-cache.reducer'; import { HALLink } from '../../shared/hal-link.model'; +import { ResourceType } from '../../shared/resource-type'; +import { excludeFromEquals } from '../../utilities/equals.decorators'; export abstract class ConfigObject implements CacheableObject { @@ -8,16 +11,19 @@ export abstract class ConfigObject implements CacheableObject { */ public name: string; + /** + * The type of this ConfigObject + */ + @excludeFromEquals + @autoserialize + type: ResourceType; + /** * The links to all related resources returned by the rest api. */ + @deserialize _links: { self: HALLink, [name: string]: HALLink }; - - /** - * The link to the rest endpoint where this config object can be found - */ - self: string; } diff --git a/src/app/core/config/models/normalized-config-submission-definition.model.ts b/src/app/core/config/models/normalized-config-submission-definition.model.ts index 597f6037c4..6cbda0d9be 100644 --- a/src/app/core/config/models/normalized-config-submission-definition.model.ts +++ b/src/app/core/config/models/normalized-config-submission-definition.model.ts @@ -1,5 +1,4 @@ import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize'; -import { mapsTo } from '../../cache/builders/build-decorators'; import { PaginatedList } from '../../data/paginated-list'; import { SubmissionDefinitionModel } from './config-submission-definition.model'; import { SubmissionSectionModel } from './config-submission-section.model'; @@ -8,7 +7,6 @@ import { NormalizedConfigObject } from './normalized-config.model'; /** * Normalized class for the configuration describing the submission */ -@mapsTo(SubmissionDefinitionModel) @inheritSerialization(NormalizedConfigObject) export class NormalizedSubmissionDefinitionModel extends NormalizedConfigObject { diff --git a/src/app/core/config/models/normalized-config-submission-definitions.model.ts b/src/app/core/config/models/normalized-config-submission-definitions.model.ts index 4c52d96458..c1dc6d607b 100644 --- a/src/app/core/config/models/normalized-config-submission-definitions.model.ts +++ b/src/app/core/config/models/normalized-config-submission-definitions.model.ts @@ -1,13 +1,11 @@ import { inheritSerialization } from 'cerialize'; import { NormalizedConfigObject } from './normalized-config.model'; import { SubmissionDefinitionsModel } from './config-submission-definitions.model'; -import { mapsTo } from '../../cache/builders/build-decorators'; import { NormalizedSubmissionDefinitionModel } from './normalized-config-submission-definition.model'; /** * Normalized class for the configuration describing the submission */ -@mapsTo(SubmissionDefinitionsModel) @inheritSerialization(NormalizedConfigObject) export class NormalizedSubmissionDefinitionsModel extends NormalizedSubmissionDefinitionModel { } diff --git a/src/app/core/config/models/normalized-config-submission-form.model.ts b/src/app/core/config/models/normalized-config-submission-form.model.ts index afdfef4818..d6352c5822 100644 --- a/src/app/core/config/models/normalized-config-submission-form.model.ts +++ b/src/app/core/config/models/normalized-config-submission-form.model.ts @@ -1,12 +1,10 @@ import { autoserialize, inheritSerialization } from 'cerialize'; import { NormalizedConfigObject } from './normalized-config.model'; -import { mapsTo } from '../../cache/builders/build-decorators'; import { FormRowModel, SubmissionFormModel } from './config-submission-form.model'; /** * Normalized class for the configuration describing the submission form */ -@mapsTo(SubmissionFormModel) @inheritSerialization(NormalizedConfigObject) export class NormalizedSubmissionFormModel extends NormalizedConfigObject { diff --git a/src/app/core/config/models/normalized-config-submission-forms.model.ts b/src/app/core/config/models/normalized-config-submission-forms.model.ts index c040a94587..d79571ec16 100644 --- a/src/app/core/config/models/normalized-config-submission-forms.model.ts +++ b/src/app/core/config/models/normalized-config-submission-forms.model.ts @@ -1,12 +1,9 @@ import { inheritSerialization } from 'cerialize'; -import { mapsTo } from '../../cache/builders/build-decorators'; -import { SubmissionFormsModel } from './config-submission-forms.model'; import { NormalizedSubmissionFormModel } from './normalized-config-submission-form.model'; /** * Normalized class for the configuration describing the submission form */ -@mapsTo(SubmissionFormsModel) @inheritSerialization(NormalizedSubmissionFormModel) export class NormalizedSubmissionFormsModel extends NormalizedSubmissionFormModel { } diff --git a/src/app/core/config/models/normalized-config-submission-section.model.ts b/src/app/core/config/models/normalized-config-submission-section.model.ts index 364a981060..05ccf454b7 100644 --- a/src/app/core/config/models/normalized-config-submission-section.model.ts +++ b/src/app/core/config/models/normalized-config-submission-section.model.ts @@ -5,12 +5,9 @@ import { SubmissionSectionModel, SubmissionSectionVisibility } from './config-submission-section.model'; -import { mapsTo } from '../../cache/builders/build-decorators'; - /** * Normalized class for the configuration describing the submission section */ -@mapsTo(SubmissionSectionModel) @inheritSerialization(NormalizedConfigObject) export class NormalizedSubmissionSectionModel extends NormalizedConfigObject { diff --git a/src/app/core/config/models/normalized-config-submission-sections.model.ts b/src/app/core/config/models/normalized-config-submission-sections.model.ts index f91ded2fbd..9d1a92aed2 100644 --- a/src/app/core/config/models/normalized-config-submission-sections.model.ts +++ b/src/app/core/config/models/normalized-config-submission-sections.model.ts @@ -1,12 +1,10 @@ import { inheritSerialization } from 'cerialize'; -import { mapsTo } from '../../cache/builders/build-decorators'; import { SubmissionSectionsModel } from './config-submission-sections.model'; import { NormalizedSubmissionSectionModel } from './normalized-config-submission-section.model'; /** * Normalized class for the configuration describing the submission section */ -@mapsTo(SubmissionSectionsModel) @inheritSerialization(NormalizedSubmissionSectionModel) export class NormalizedSubmissionSectionsModel extends NormalizedSubmissionSectionModel { } diff --git a/src/app/core/config/models/normalized-config-submission-uploads.model.ts b/src/app/core/config/models/normalized-config-submission-uploads.model.ts index 7a21c15912..5393c1e89a 100644 --- a/src/app/core/config/models/normalized-config-submission-uploads.model.ts +++ b/src/app/core/config/models/normalized-config-submission-uploads.model.ts @@ -3,12 +3,9 @@ import { AccessConditionOption } from './config-access-condition-option.model'; import { SubmissionFormsModel } from './config-submission-forms.model'; import { NormalizedConfigObject } from './normalized-config.model'; import { SubmissionUploadsModel } from './config-submission-uploads.model'; -import { mapsTo } from '../../cache/builders/build-decorators'; - /** * Normalized class for the configuration describing the submission upload section */ -@mapsTo(SubmissionUploadsModel) @inheritSerialization(NormalizedConfigObject) export class NormalizedSubmissionUploadsModel extends NormalizedConfigObject { diff --git a/src/app/core/config/models/normalized-config.model.ts b/src/app/core/config/models/normalized-config.model.ts index 469b93f2e9..f697fdf857 100644 --- a/src/app/core/config/models/normalized-config.model.ts +++ b/src/app/core/config/models/normalized-config.model.ts @@ -2,6 +2,8 @@ import { autoserialize, inheritSerialization } from 'cerialize'; import { NormalizedObject } from '../../cache/models/normalized-object.model'; import { CacheableObject } from '../../cache/object-cache.reducer'; import { HALLink } from '../../shared/hal-link.model'; +import { ResourceType } from '../../shared/resource-type'; +import { excludeFromEquals } from '../../utilities/equals.decorators'; /** * Normalized abstract class for a configuration object @@ -15,6 +17,13 @@ export abstract class NormalizedConfigObject implemen @autoserialize public name: string; + /** + * The object type + */ + @excludeFromEquals + @autoserialize + type: ResourceType; + /** * The links to all related resources returned by the rest api. */ diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index 3c0b2847b2..9afd2276b6 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -1,148 +1,148 @@ -import { ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core'; import { CommonModule } from '@angular/common'; - -import { StoreModule } from '@ngrx/store'; -import { EffectsModule } from '@ngrx/effects'; +import { HTTP_INTERCEPTORS, HttpClient } from '@angular/common/http'; +import { ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core'; import { DynamicFormLayoutService, DynamicFormService, DynamicFormValidationService } from '@ng-dynamic-forms/core'; +import { EffectsModule } from '@ngrx/effects'; -import { coreEffects } from './core.effects'; -import { coreReducers } from './core.reducers'; +import { StoreModule } from '@ngrx/store'; +import { MyDSpaceGuard } from '../+my-dspace-page/my-dspace.guard'; +import { ENV_CONFIG, GLOBAL_CONFIG, GlobalConfig } from '../../config'; import { isNotEmpty } from '../shared/empty.util'; -import { EPersonDataService } from './eperson/eperson-data.service'; - -import { ApiService } from './services/api.service'; -import { BrowseEntriesResponseParsingService } from './data/browse-entries-response-parsing.service'; -import { CollectionDataService } from './data/collection-data.service'; -import { CommunityDataService } from './data/community-data.service'; -import { DebugResponseParsingService } from './data/debug-response-parsing.service'; -import { DSOResponseParsingService } from './data/dso-response-parsing.service'; -import { SearchResponseParsingService } from './data/search-response-parsing.service'; -import { DSpaceRESTv2Service } from './dspace-rest-v2/dspace-rest-v2.service'; import { FormBuilderService } from '../shared/form/builder/form-builder.service'; -import { SectionFormOperationsService } from '../submission/sections/form/section-form-operations.service'; import { FormService } from '../shared/form/form.service'; import { HostWindowService } from '../shared/host-window.service'; -import { ItemDataService } from './data/item-data.service'; -import { MetadataService } from './metadata/metadata.service'; -import { ObjectCacheService } from './cache/object-cache.service'; -import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model'; -import { RemoteDataBuildService } from './cache/builders/remote-data-build.service'; -import { EndpointMapResponseParsingService } from './data/endpoint-map-response-parsing.service'; -import { ServerResponseService } from './services/server-response.service'; -import { NativeWindowFactory, NativeWindowService } from './services/window.service'; -import { BrowseService } from './browse/browse.service'; -import { BrowseResponseParsingService } from './data/browse-response-parsing.service'; -import { ConfigResponseParsingService } from './config/config-response-parsing.service'; -import { RouteService } from './services/route.service'; -import { SubmissionDefinitionsConfigService } from './config/submission-definitions-config.service'; -import { SubmissionFormsConfigService } from './config/submission-forms-config.service'; -import { SubmissionSectionsConfigService } from './config/submission-sections-config.service'; -import { Relationship } from './shared/item-relationships/relationship.model'; -import { Item } from './shared/item.model'; -import { SubmissionResponseParsingService } from './submission/submission-response-parsing.service'; -import { EpersonResponseParsingService } from './eperson/eperson-response-parsing.service'; -import { JsonPatchOperationsBuilder } from './json-patch/builder/json-patch-operations-builder'; -import { AuthorityService } from './integration/authority.service'; -import { IntegrationResponseParsingService } from './integration/integration-response-parsing.service'; -import { WorkspaceitemDataService } from './submission/workspaceitem-data.service'; -import { UUIDService } from './shared/uuid.service'; -import { AuthenticatedGuard } from './auth/authenticated.guard'; -import { AuthRequestService } from './auth/auth-request.service'; -import { AuthResponseParsingService } from './auth/auth-response-parsing.service'; -import { HTTP_INTERCEPTORS, HttpClient } from '@angular/common/http'; -import { AuthInterceptor } from './auth/auth.interceptor'; -import { HALEndpointService } from './shared/hal-endpoint.service'; -import { FacetValueResponseParsingService } from './data/facet-value-response-parsing.service'; -import { FacetValueMapResponseParsingService } from './data/facet-value-map-response-parsing.service'; -import { FacetConfigResponseParsingService } from './data/facet-config-response-parsing.service'; -import { ResourcePolicyService } from './data/resource-policy.service'; -import { RegistryService } from './registry/registry.service'; -import { RegistryMetadataschemasResponseParsingService } from './data/registry-metadataschemas-response-parsing.service'; -import { RegistryMetadatafieldsResponseParsingService } from './data/registry-metadatafields-response-parsing.service'; -import { RegistryBitstreamformatsResponseParsingService } from './data/registry-bitstreamformats-response-parsing.service'; -import { WorkflowItemDataService } from './submission/workflowitem-data.service'; -import { NotificationsService } from '../shared/notifications/notifications.service'; -import { UploaderService } from '../shared/uploader/uploader.service'; -import { FileService } from './shared/file.service'; -import { SubmissionRestService } from './submission/submission-rest.service'; -import { BrowseItemsResponseParsingService } from './data/browse-items-response-parsing-service'; -import { DSpaceObjectDataService } from './data/dspace-object-data.service'; -import { MetadataschemaParsingService } from './data/metadataschema-parsing.service'; -import { FilteredDiscoveryPageResponseParsingService } from './data/filtered-discovery-page-response-parsing.service'; -import { CSSVariableService } from '../shared/sass-helper/sass-helper.service'; import { MenuService } from '../shared/menu/menu.service'; -import { SubmissionJsonPatchOperationsService } from './submission/submission-json-patch-operations.service'; -import { NormalizedObjectBuildService } from './cache/builders/normalized-object-build.service'; -import { DSOChangeAnalyzer } from './data/dso-change-analyzer.service'; -import { ObjectUpdatesService } from './data/object-updates/object-updates.service'; -import { DefaultChangeAnalyzer } from './data/default-change-analyzer.service'; -import { SearchService } from './shared/search/search.service'; -import { RelationshipService } from './data/relationship.service'; -import { NormalizedCollection } from './cache/models/normalized-collection.model'; -import { NormalizedCommunity } from './cache/models/normalized-community.model'; -import { NormalizedDSpaceObject } from './cache/models/normalized-dspace-object.model'; -import { NormalizedBitstream } from './cache/models/normalized-bitstream.model'; -import { NormalizedBundle } from './cache/models/normalized-bundle.model'; -import { NormalizedBitstreamFormat } from './cache/models/normalized-bitstream-format.model'; -import { NormalizedItem } from './cache/models/normalized-item.model'; -import { NormalizedEPerson } from './eperson/models/normalized-eperson.model'; -import { NormalizedGroup } from './eperson/models/normalized-group.model'; -import { NormalizedResourcePolicy } from './cache/models/normalized-resource-policy.model'; -import { NormalizedMetadataSchema } from './metadata/normalized-metadata-schema.model'; -import { NormalizedMetadataField } from './metadata/normalized-metadata-field.model'; -import { NormalizedLicense } from './cache/models/normalized-license.model'; -import { NormalizedWorkflowItem } from './submission/models/normalized-workflowitem.model'; -import { NormalizedWorkspaceItem } from './submission/models/normalized-workspaceitem.model'; -import { NormalizedSubmissionDefinitionsModel } from './config/models/normalized-config-submission-definitions.model'; -import { NormalizedSubmissionFormsModel } from './config/models/normalized-config-submission-forms.model'; -import { NormalizedSubmissionSectionModel } from './config/models/normalized-config-submission-section.model'; -import { NormalizedAuthStatus } from './auth/models/normalized-auth-status.model'; -import { NormalizedAuthorityValue } from './integration/models/normalized-authority-value.model'; -import { RoleService } from './roles/role.service'; -import { MyDSpaceGuard } from '../+my-dspace-page/my-dspace.guard'; -import { MyDSpaceResponseParsingService } from './data/mydspace-response-parsing.service'; -import { ClaimedTaskDataService } from './tasks/claimed-task-data.service'; -import { PoolTaskDataService } from './tasks/pool-task-data.service'; -import { TaskResponseParsingService } from './tasks/task-response-parsing.service'; -import { BitstreamFormatDataService } from './data/bitstream-format-data.service'; -import { NormalizedClaimedTask } from './tasks/models/normalized-claimed-task-object.model'; -import { NormalizedTaskObject } from './tasks/models/normalized-task-object.model'; -import { NormalizedPoolTask } from './tasks/models/normalized-pool-task-object.model'; -import { NormalizedRelationship } from './cache/models/items/normalized-relationship.model'; -import { NormalizedRelationshipType } from './cache/models/items/normalized-relationship-type.model'; -import { NormalizedItemType } from './cache/models/items/normalized-item-type.model'; -import { MetadatafieldParsingService } from './data/metadatafield-parsing.service'; -import { NormalizedSubmissionUploadsModel } from './config/models/normalized-config-submission-uploads.model'; -import { NormalizedBrowseEntry } from './shared/normalized-browse-entry.model'; -import { BrowseDefinition } from './shared/browse-definition.model'; -import { ContentSourceResponseParsingService } from './data/content-source-response-parsing.service'; -import { MappedCollectionsReponseParsingService } from './data/mapped-collections-reponse-parsing.service'; -import { ObjectSelectService } from '../shared/object-select/object-select.service'; -import {EntityTypeService} from './data/entity-type.service'; -import { SiteDataService } from './data/site-data.service'; -import { NormalizedSite } from './cache/models/normalized-site.model'; +import { EndpointMockingRestService } from '../shared/mocks/dspace-rest-v2/endpoint-mocking-rest.service'; import { MOCK_RESPONSE_MAP, MockResponseMap, mockResponseMap } from '../shared/mocks/dspace-rest-v2/mocks/mock-response-map'; -import { EndpointMockingRestService } from '../shared/mocks/dspace-rest-v2/endpoint-mocking-rest.service'; -import { ENV_CONFIG, GLOBAL_CONFIG, GlobalConfig } from '../../config'; -import { SearchFilterService } from './shared/search/search-filter.service'; -import { SearchConfigurationService } from './shared/search/search-configuration.service'; +import { NotificationsService } from '../shared/notifications/notifications.service'; import { SelectableListService } from '../shared/object-list/selectable-list/selectable-list.service'; -import { RelationshipTypeService } from './data/relationship-type.service'; +import { ObjectSelectService } from '../shared/object-select/object-select.service'; +import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model'; +import { CSSVariableService } from '../shared/sass-helper/sass-helper.service'; import { SidebarService } from '../shared/sidebar/sidebar.service'; -import { NormalizedExternalSource } from './cache/models/normalized-external-source.model'; -import { NormalizedExternalSourceEntry } from './cache/models/normalized-external-source-entry.model'; +import { UploaderService } from '../shared/uploader/uploader.service'; +import { SectionFormOperationsService } from '../submission/sections/form/section-form-operations.service'; +import { AuthRequestService } from './auth/auth-request.service'; +import { AuthResponseParsingService } from './auth/auth-response-parsing.service'; +import { AuthInterceptor } from './auth/auth.interceptor'; +import { AuthenticatedGuard } from './auth/authenticated.guard'; +import { AuthStatus } from './auth/models/auth-status.model'; +import { BrowseService } from './browse/browse.service'; +import { NormalizedObjectBuildService } from './cache/builders/normalized-object-build.service'; +import { RemoteDataBuildService } from './cache/builders/remote-data-build.service'; +import { ObjectCacheService } from './cache/object-cache.service'; +import { ConfigResponseParsingService } from './config/config-response-parsing.service'; +import { SubmissionDefinitionsModel } from './config/models/config-submission-definitions.model'; +import { SubmissionFormsModel } from './config/models/config-submission-forms.model'; +import { SubmissionSectionModel } from './config/models/config-submission-section.model'; +import { SubmissionUploadsModel } from './config/models/config-submission-uploads.model'; +import { SubmissionDefinitionsConfigService } from './config/submission-definitions-config.service'; +import { SubmissionFormsConfigService } from './config/submission-forms-config.service'; +import { SubmissionSectionsConfigService } from './config/submission-sections-config.service'; + +import { coreEffects } from './core.effects'; +import { coreReducers } from './core.reducers'; +import { BitstreamFormatDataService } from './data/bitstream-format-data.service'; +import { BrowseEntriesResponseParsingService } from './data/browse-entries-response-parsing.service'; +import { BrowseItemsResponseParsingService } from './data/browse-items-response-parsing-service'; +import { BrowseResponseParsingService } from './data/browse-response-parsing.service'; +import { CollectionDataService } from './data/collection-data.service'; +import { CommunityDataService } from './data/community-data.service'; +import { ContentSourceResponseParsingService } from './data/content-source-response-parsing.service'; +import { DebugResponseParsingService } from './data/debug-response-parsing.service'; +import { DefaultChangeAnalyzer } from './data/default-change-analyzer.service'; +import { DSOChangeAnalyzer } from './data/dso-change-analyzer.service'; +import { DSOResponseParsingService } from './data/dso-response-parsing.service'; +import { DSpaceObjectDataService } from './data/dspace-object-data.service'; +import { EndpointMapResponseParsingService } from './data/endpoint-map-response-parsing.service'; +import { ItemTypeDataService } from './data/entity-type-data.service'; +import { EntityTypeService } from './data/entity-type.service'; import { ExternalSourceService } from './data/external-source.service'; +import { FacetConfigResponseParsingService } from './data/facet-config-response-parsing.service'; +import { FacetValueMapResponseParsingService } from './data/facet-value-map-response-parsing.service'; +import { FacetValueResponseParsingService } from './data/facet-value-response-parsing.service'; +import { FilteredDiscoveryPageResponseParsingService } from './data/filtered-discovery-page-response-parsing.service'; +import { ItemDataService } from './data/item-data.service'; +import { LicenseDataService } from './data/license-data.service'; import { LookupRelationService } from './data/lookup-relation.service'; +import { MappedCollectionsReponseParsingService } from './data/mapped-collections-reponse-parsing.service'; +import { MetadatafieldParsingService } from './data/metadatafield-parsing.service'; +import { MetadataschemaParsingService } from './data/metadataschema-parsing.service'; +import { MyDSpaceResponseParsingService } from './data/mydspace-response-parsing.service'; +import { ObjectUpdatesService } from './data/object-updates/object-updates.service'; +import { RegistryBitstreamformatsResponseParsingService } from './data/registry-bitstreamformats-response-parsing.service'; +import { RegistryMetadatafieldsResponseParsingService } from './data/registry-metadatafields-response-parsing.service'; +import { RegistryMetadataschemasResponseParsingService } from './data/registry-metadataschemas-response-parsing.service'; +import { RelationshipTypeService } from './data/relationship-type.service'; +import { RelationshipService } from './data/relationship.service'; +import { ResourcePolicyService } from './data/resource-policy.service'; +import { SearchResponseParsingService } from './data/search-response-parsing.service'; +import { SiteDataService } from './data/site-data.service'; +import { DSpaceRESTv2Service } from './dspace-rest-v2/dspace-rest-v2.service'; +import { EPersonDataService } from './eperson/eperson-data.service'; +import { EpersonResponseParsingService } from './eperson/eperson-response-parsing.service'; +import { EPerson } from './eperson/models/eperson.model'; +import { Group } from './eperson/models/group.model'; +import { AuthorityService } from './integration/authority.service'; +import { IntegrationResponseParsingService } from './integration/integration-response-parsing.service'; +import { AuthorityValue } from './integration/models/authority.value'; +import { JsonPatchOperationsBuilder } from './json-patch/builder/json-patch-operations-builder'; +import { MetadataField } from './metadata/metadata-field.model'; +import { MetadataSchema } from './metadata/metadata-schema.model'; +import { MetadataService } from './metadata/metadata.service'; +import { RegistryService } from './registry/registry.service'; +import { RoleService } from './roles/role.service'; + +import { ApiService } from './services/api.service'; +import { RouteService } from './services/route.service'; +import { ServerResponseService } from './services/server-response.service'; +import { NativeWindowFactory, NativeWindowService } from './services/window.service'; +import { BitstreamFormat } from './shared/bitstream-format.model'; +import { Bitstream } from './shared/bitstream.model'; +import { BrowseDefinition } from './shared/browse-definition.model'; +import { BrowseEntry } from './shared/browse-entry.model'; +import { Bundle } from './shared/bundle.model'; +import { Collection } from './shared/collection.model'; +import { Community } from './shared/community.model'; +import { DSpaceObject } from './shared/dspace-object.model'; +import { ExternalSourceEntry } from './shared/external-source-entry.model'; +import { ExternalSource } from './shared/external-source.model'; +import { FileService } from './shared/file.service'; +import { HALEndpointService } from './shared/hal-endpoint.service'; +import { ItemType } from './shared/item-relationships/item-type.model'; +import { RelationshipType } from './shared/item-relationships/relationship-type.model'; +import { Relationship } from './shared/item-relationships/relationship.model'; +import { Item } from './shared/item.model'; +import { License } from './shared/license.model'; +import { ResourcePolicy } from './shared/resource-policy.model'; +import { SearchConfigurationService } from './shared/search/search-configuration.service'; +import { SearchFilterService } from './shared/search/search-filter.service'; +import { SearchService } from './shared/search/search.service'; +import { Site } from './shared/site.model'; +import { UUIDService } from './shared/uuid.service'; +import { WorkflowItem } from './submission/models/workflowitem.model'; +import { WorkspaceItem } from './submission/models/workspaceitem.model'; +import { SubmissionJsonPatchOperationsService } from './submission/submission-json-patch-operations.service'; +import { SubmissionResponseParsingService } from './submission/submission-response-parsing.service'; +import { SubmissionRestService } from './submission/submission-rest.service'; +import { WorkflowItemDataService } from './submission/workflowitem-data.service'; +import { WorkspaceitemDataService } from './submission/workspaceitem-data.service'; +import { ClaimedTaskDataService } from './tasks/claimed-task-data.service'; +import { ClaimedTask } from './tasks/models/claimed-task-object.model'; +import { PoolTask } from './tasks/models/pool-task-object.model'; +import { TaskObject } from './tasks/models/task-object.model'; +import { PoolTaskDataService } from './tasks/pool-task-data.service'; +import { TaskResponseParsingService } from './tasks/task-response-parsing.service'; /** * When not in production, endpoint responses can be mocked for testing purposes @@ -257,6 +257,8 @@ const PROVIDERS = [ RelationshipTypeService, ExternalSourceService, LookupRelationService, + LicenseDataService, + ItemTypeDataService, // register AuthInterceptor as HttpInterceptor { provide: HTTP_INTERCEPTORS, @@ -271,42 +273,40 @@ const PROVIDERS = [ /** * Declaration needed to make sure all decorator functions are called in time */ -export const normalizedModels = +export const models = [ - Relationship, + DSpaceObject, + Bundle, + Bitstream, + BitstreamFormat, Item, - NormalizedDSpaceObject, - NormalizedBundle, - NormalizedBitstream, - NormalizedBitstreamFormat, - NormalizedItem, - NormalizedSite, - NormalizedCollection, - NormalizedCommunity, - NormalizedEPerson, - NormalizedGroup, - NormalizedResourcePolicy, - NormalizedMetadataSchema, - NormalizedMetadataField, - NormalizedLicense, - NormalizedWorkflowItem, - NormalizedWorkspaceItem, - NormalizedSubmissionDefinitionsModel, - NormalizedSubmissionFormsModel, - NormalizedSubmissionSectionModel, - NormalizedSubmissionUploadsModel, - NormalizedAuthStatus, - NormalizedAuthorityValue, - NormalizedBrowseEntry, + Site, + Collection, + Community, + EPerson, + Group, + ResourcePolicy, + MetadataSchema, + MetadataField, + License, + WorkflowItem, + WorkspaceItem, + SubmissionDefinitionsModel, + SubmissionFormsModel, + SubmissionSectionModel, + SubmissionUploadsModel, + AuthStatus, + AuthorityValue, + BrowseEntry, BrowseDefinition, - NormalizedClaimedTask, - NormalizedTaskObject, - NormalizedPoolTask, - NormalizedRelationship, - NormalizedRelationshipType, - NormalizedItemType, - NormalizedExternalSource, - NormalizedExternalSourceEntry, + ClaimedTask, + TaskObject, + PoolTask, + Relationship, + RelationshipType, + ItemType, + ExternalSource, + ExternalSourceEntry, ]; @NgModule({ diff --git a/src/app/core/data/base-response-parsing.service.ts b/src/app/core/data/base-response-parsing.service.ts index 61d966c761..ab81e2dd8d 100644 --- a/src/app/core/data/base-response-parsing.service.ts +++ b/src/app/core/data/base-response-parsing.service.ts @@ -1,13 +1,14 @@ import { hasNoValue, hasValue, isNotEmpty } from '../../shared/empty.util'; -import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer'; +import { DSpaceSerializer } from '../dspace-rest-v2/dspace.serializer'; +import { NormalizedObjectSerializer } from '../dspace-rest-v2/normalized-object.serializer'; import { CacheableObject } from '../cache/object-cache.reducer'; +import { Serializer } from '../serializer'; import { PageInfo } from '../shared/page-info.model'; import { ObjectCacheService } from '../cache/object-cache.service'; import { GlobalConfig } from '../../../config/global-config.interface'; import { GenericConstructor } from '../shared/generic-constructor'; import { PaginatedList } from './paginated-list'; import { isRestDataObject, isRestPaginatedList } from '../cache/builders/normalized-object-build.service'; -import { ResourceType } from '../shared/resource-type'; import { getMapsToType } from '../cache/builders/build-decorators'; import { RestRequest } from './request.models'; /* tslint:disable:max-classes-per-file */ @@ -17,6 +18,7 @@ export abstract class BaseResponseParsingService { protected abstract objectCache: ObjectCacheService; protected abstract toCache: boolean; protected shouldDirectlyAttachEmbeds = false; + protected serializerConstructor: GenericConstructor> = DSpaceSerializer; protected process(data: any, request: RestRequest): any { if (isNotEmpty(data)) { @@ -47,7 +49,7 @@ export abstract class BaseResponseParsingService { }); } - this.cache(object, request); + this.cache(object, request, data); return object; } const result = {}; @@ -91,30 +93,35 @@ export abstract class BaseResponseParsingService { const normObjConstructor = getMapsToType(type) as GenericConstructor; if (hasValue(normObjConstructor)) { - const serializer = new DSpaceRESTv2Serializer(normObjConstructor); + const serializer = new this.serializerConstructor(normObjConstructor); return serializer.deserialize(obj); } else { - // TODO: move check to Validator? - // throw new Error(`The server returned an object with an unknown a known type: ${type}`); return null; } } else { - // TODO: move check to Validator - // throw new Error(`The server returned an object without a type: ${JSON.stringify(obj)}`); return null; } } - protected cache(obj, request: RestRequest) { + protected cache(obj, request: RestRequest, data: any) { if (this.toCache) { - this.addToObjectCache(obj, request); + this.addToObjectCache(obj, request, data); } } - protected addToObjectCache(co: CacheableObject, request: RestRequest): void { - if (hasNoValue(co) || hasNoValue(co.self)) { - throw new Error('The server returned an invalid object'); + protected addToObjectCache(co: CacheableObject, request: RestRequest, data: any): void { + if (hasNoValue(co) || hasNoValue(co._links) || hasNoValue(co._links.self) || hasNoValue(co._links.self.href)) { + const type = hasValue(data) && hasValue(data.type) ? data.type : 'object'; + let dataJSON: string; + if (hasValue(data._embedded)) { + dataJSON = JSON.stringify(Object.assign({}, data, { + _embedded: '...' + })); + } else { + dataJSON = JSON.stringify(data); + } + throw new Error(`Can't cache incomplete ${type}: ${JSON.stringify(co)}, parsed from (partial) response: ${dataJSON}`); } this.objectCache.add(co, hasValue(request.responseMsToLive) ? request.responseMsToLive : this.EnvConfig.cache.msToLive.default, request.uuid); } @@ -122,7 +129,7 @@ export abstract class BaseResponseParsingService { processPageInfo(payload: any): PageInfo { if (hasValue(payload.page)) { const pageObj = Object.assign({}, payload.page, { _links: payload._links }); - const pageInfoObject = new DSpaceRESTv2Serializer(PageInfo).deserialize(pageObj); + const pageInfoObject = new NormalizedObjectSerializer(PageInfo).deserialize(pageObj); if (pageInfoObject.currentPage >= 0) { Object.assign(pageInfoObject, { currentPage: pageInfoObject.currentPage + 1 }); } @@ -141,7 +148,7 @@ export abstract class BaseResponseParsingService { } protected retrieveObjectOrUrl(obj: any): any { - return this.toCache ? obj.self : obj; + return this.toCache ? obj._links.self.href : obj; } protected isSuccessStatus(statusCode: number) { diff --git a/src/app/core/data/browse-entries-response-parsing.service.ts b/src/app/core/data/browse-entries-response-parsing.service.ts index a2f5f21312..e8d1fbb15c 100644 --- a/src/app/core/data/browse-entries-response-parsing.service.ts +++ b/src/app/core/data/browse-entries-response-parsing.service.ts @@ -5,7 +5,7 @@ import { isNotEmpty } from '../../shared/empty.util'; import { ObjectCacheService } from '../cache/object-cache.service'; import { ErrorResponse, GenericSuccessResponse, RestResponse } from '../cache/response.models'; import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model'; -import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer'; +import { NormalizedObjectSerializer } from '../dspace-rest-v2/normalized-object.serializer'; import { BaseResponseParsingService } from './base-response-parsing.service'; import { ResponseParsingService } from './parsing.service'; import { RestRequest } from './request.models'; @@ -26,7 +26,7 @@ export class BrowseEntriesResponseParsingService extends BaseResponseParsingServ if (isNotEmpty(data.payload)) { let browseEntries = []; if (isNotEmpty(data.payload._embedded) && Array.isArray(data.payload._embedded[Object.keys(data.payload._embedded)[0]])) { - const serializer = new DSpaceRESTv2Serializer(NormalizedBrowseEntry); + const serializer = new NormalizedObjectSerializer(NormalizedBrowseEntry); browseEntries = serializer.deserializeArray(data.payload._embedded[Object.keys(data.payload._embedded)[0]]); } return new GenericSuccessResponse(browseEntries, data.statusCode, data.statusText, this.processPageInfo(data.payload)); diff --git a/src/app/core/data/browse-items-response-parsing-service.ts b/src/app/core/data/browse-items-response-parsing-service.ts index 324b36199a..82438c7fcb 100644 --- a/src/app/core/data/browse-items-response-parsing-service.ts +++ b/src/app/core/data/browse-items-response-parsing-service.ts @@ -6,7 +6,7 @@ import { hasValue, isNotEmpty } from '../../shared/empty.util'; import { ObjectCacheService } from '../cache/object-cache.service'; import { ErrorResponse, GenericSuccessResponse, RestResponse } from '../cache/response.models'; import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model'; -import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer'; +import { NormalizedObjectSerializer } from '../dspace-rest-v2/normalized-object.serializer'; import { BaseResponseParsingService } from './base-response-parsing.service'; import { ResponseParsingService } from './parsing.service'; import { RestRequest } from './request.models'; @@ -35,7 +35,7 @@ export class BrowseItemsResponseParsingService extends BaseResponseParsingServic parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse { if (isNotEmpty(data.payload) && isNotEmpty(data.payload._embedded) && Array.isArray(data.payload._embedded[Object.keys(data.payload._embedded)[0]])) { - const serializer = new DSpaceRESTv2Serializer(NormalizedDSpaceObject); + const serializer = new NormalizedObjectSerializer(NormalizedDSpaceObject); const items = serializer.deserializeArray(data.payload._embedded[Object.keys(data.payload._embedded)[0]]); return new GenericSuccessResponse(items, data.statusCode, data.statusText, this.processPageInfo(data.payload)); } else if (hasValue(data.payload) && hasValue(data.payload.page)) { diff --git a/src/app/core/data/browse-response-parsing.service.ts b/src/app/core/data/browse-response-parsing.service.ts index 3c67b2b3eb..08d2f8f04b 100644 --- a/src/app/core/data/browse-response-parsing.service.ts +++ b/src/app/core/data/browse-response-parsing.service.ts @@ -4,7 +4,7 @@ import { RestRequest } from './request.models'; import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model'; import { GenericSuccessResponse, ErrorResponse, RestResponse } from '../cache/response.models'; import { isNotEmpty } from '../../shared/empty.util'; -import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer'; +import { NormalizedObjectSerializer } from '../dspace-rest-v2/normalized-object.serializer'; import { BrowseDefinition } from '../shared/browse-definition.model'; @Injectable() @@ -13,7 +13,7 @@ export class BrowseResponseParsingService implements ResponseParsingService { parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse { if (isNotEmpty(data.payload) && isNotEmpty(data.payload._embedded) && Array.isArray(data.payload._embedded[Object.keys(data.payload._embedded)[0]])) { - const serializer = new DSpaceRESTv2Serializer(BrowseDefinition); + const serializer = new NormalizedObjectSerializer(BrowseDefinition); const browseDefinitions = serializer.deserializeArray(data.payload._embedded[Object.keys(data.payload._embedded)[0]]); return new GenericSuccessResponse(browseDefinitions, data.statusCode, data.statusText); } else { diff --git a/src/app/core/data/change-analyzer.ts b/src/app/core/data/change-analyzer.ts index c45c9e55b7..395af4a68c 100644 --- a/src/app/core/data/change-analyzer.ts +++ b/src/app/core/data/change-analyzer.ts @@ -1,4 +1,3 @@ -import { NormalizedObject } from '../cache/models/normalized-object.model'; import { Operation } from 'fast-json-patch/lib/core'; import { CacheableObject } from '../cache/object-cache.reducer'; @@ -12,10 +11,10 @@ export interface ChangeAnalyzer { * Compare two objects and return their differences as a * JsonPatch Operation Array * - * @param {NormalizedObject} object1 + * @param {CacheableObject} object1 * The first object to compare - * @param {NormalizedObject} object2 + * @param {CacheableObject} object2 * The second object to compare */ - diff(object1: T | NormalizedObject, object2: T | NormalizedObject): Operation[]; + diff(object1: T, object2: T): Operation[]; } diff --git a/src/app/core/data/collection-data.service.ts b/src/app/core/data/collection-data.service.ts index 19c825a943..6db87e59ef 100644 --- a/src/app/core/data/collection-data.service.ts +++ b/src/app/core/data/collection-data.service.ts @@ -9,6 +9,7 @@ import { NotificationOptions } from '../../shared/notifications/models/notificat import { INotification } from '../../shared/notifications/models/notification.model'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model'; +import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { dataService } from '../cache/builders/build-decorators'; import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; @@ -16,7 +17,7 @@ import { SearchParam } from '../cache/models/search-param.model'; import { ObjectCacheService } from '../cache/object-cache.service'; import { ContentSourceSuccessResponse, RestResponse } from '../cache/response.models'; import { CoreState } from '../core.reducers'; -import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer'; +import { NormalizedObjectSerializer } from '../dspace-rest-v2/normalized-object.serializer'; import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service'; import { Collection } from '../shared/collection.model'; import { COLLECTION } from '../shared/collection.resource-type'; @@ -152,7 +153,7 @@ export class CollectionDataService extends ComColDataService { */ updateContentSource(collectionId: string, contentSource: ContentSource): Observable { const requestId = this.requestService.generateRequestId(); - const serializedContentSource = new DSpaceRESTv2Serializer(ContentSource).serialize(contentSource); + const serializedContentSource = new NormalizedObjectSerializer(ContentSource).serialize(contentSource); const request$ = this.getHarvesterEndpoint(collectionId).pipe( take(1), map((href: string) => { @@ -210,8 +211,9 @@ export class CollectionDataService extends ComColDataService { * Fetches a list of items that are mapped to a collection * @param collectionId The id of the collection * @param searchOptions Search options to sort or filter out items + * @param linksToFollow List of {@link FollowLinkConfig} that indicate which HALLinks should be automatically resolved */ - getMappedItems(collectionId: string, searchOptions?: PaginatedSearchOptions): Observable>> { + getMappedItems(collectionId: string, searchOptions?: PaginatedSearchOptions, ...linksToFollow: Array>): Observable>> { const requestUuid = this.requestService.generateRequestId(); const href$ = this.getMappedItemsEndpoint(collectionId).pipe( @@ -233,7 +235,7 @@ export class CollectionDataService extends ComColDataService { configureRequest(this.requestService) ).subscribe(); - return this.rdbService.buildList(href$); + return this.rdbService.buildList(href$, ...linksToFollow); } protected getFindByParentHref(parentUUID: string): Observable { diff --git a/src/app/core/data/comcol-data.service.ts b/src/app/core/data/comcol-data.service.ts index 7d8cc72683..d83518a3b0 100644 --- a/src/app/core/data/comcol-data.service.ts +++ b/src/app/core/data/comcol-data.service.ts @@ -6,9 +6,10 @@ import { } from 'rxjs/operators'; import { merge as observableMerge, Observable, throwError as observableThrowError, combineLatest as observableCombineLatest } from 'rxjs'; import { hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util'; -import { NormalizedCommunity } from '../cache/models/normalized-community.model'; import { ObjectCacheService } from '../cache/object-cache.service'; +import { Community } from '../shared/community.model'; import { HALLink } from '../shared/hal-link.model'; +import { HALResource } from '../shared/hal-resource.model'; import { CommunityDataService } from './community-data.service'; import { DataService } from './data.service'; @@ -71,7 +72,7 @@ export abstract class ComColDataService extends DataS const successResponses = responses.pipe( filter((response) => response.isSuccessful), mergeMap(() => this.objectCache.getObjectByUUID(options.scopeID)), - map((nc: NormalizedCommunity) => nc._links[linkPath]), + map((hr: HALResource) => hr._links[linkPath]), filter((halLink: HALLink) => isNotEmpty(halLink)), map((halLink: HALLink) => halLink.href) ); diff --git a/src/app/core/data/content-source-response-parsing.service.ts b/src/app/core/data/content-source-response-parsing.service.ts index 4e0490148b..0d76ea1ef7 100644 --- a/src/app/core/data/content-source-response-parsing.service.ts +++ b/src/app/core/data/content-source-response-parsing.service.ts @@ -3,7 +3,7 @@ import { ResponseParsingService } from './parsing.service'; import { RestRequest } from './request.models'; import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model'; import { ContentSourceSuccessResponse, RestResponse } from '../cache/response.models'; -import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer'; +import { NormalizedObjectSerializer } from '../dspace-rest-v2/normalized-object.serializer'; import { ContentSource } from '../shared/content-source.model'; import { MetadataConfig } from '../shared/metadata-config.model'; @@ -17,11 +17,11 @@ export class ContentSourceResponseParsingService implements ResponseParsingServi parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse { const payload = data.payload; - const deserialized = new DSpaceRESTv2Serializer(ContentSource).deserialize(payload); + const deserialized = new NormalizedObjectSerializer(ContentSource).deserialize(payload); let metadataConfigs = []; if (payload._embedded && payload._embedded.harvestermetadata && payload._embedded.harvestermetadata.configs) { - metadataConfigs = new DSpaceRESTv2Serializer(MetadataConfig).serializeArray(payload._embedded.harvestermetadata.configs); + metadataConfigs = new NormalizedObjectSerializer(MetadataConfig).serializeArray(payload._embedded.harvestermetadata.configs); } deserialized.metadataConfigs = metadataConfigs; diff --git a/src/app/core/data/data.service.ts b/src/app/core/data/data.service.ts index 9e9560705a..e8335a2bdc 100644 --- a/src/app/core/data/data.service.ts +++ b/src/app/core/data/data.service.ts @@ -27,7 +27,7 @@ import { CacheableObject } from '../cache/object-cache.reducer'; import { ObjectCacheService } from '../cache/object-cache.service'; import { ErrorResponse, RestResponse } from '../cache/response.models'; import { CoreState } from '../core.reducers'; -import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer'; +import { NormalizedObjectSerializer } from '../dspace-rest-v2/normalized-object.serializer'; import { DSpaceObject } from '../shared/dspace-object.model'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { @@ -155,7 +155,7 @@ export abstract class DataService { /** * Returns {@link RemoteData} of all object with a list of {@link FollowLinkConfig}, to indicate which embedded * info should be added to the objects - * @param linksToFollow List of {@link FollowLinkConfig} to indicate which embedded info should be retrieved and added + * @param linksToFollow List of {@link FollowLinkConfig} that indicate which HALLinks should be automatically resolved */ findAll(options: FindListOptions = {}, ...linksToFollow: Array>): Observable>> { return this.findList(this.getFindAllHref(options), options, ...linksToFollow); @@ -163,9 +163,9 @@ export abstract class DataService { /** * Returns an observable of {@link RemoteData} of an object, based on href observable, - * with a list of {@link FollowLinkConfig}, to add embedded info to the object + * with a list of {@link FollowLinkConfig}, to automatically resolve HALLinks of the object * @param href$ Observable of href of object we want to retrieve - * @param linksToFollow List of {@link FollowLinkConfig} to indicate which embedded info should be retrieved and added + * @param linksToFollow List of {@link FollowLinkConfig} that indicate which HALLinks should be automatically resolved */ protected findList(href$, options: FindListOptions, ...linksToFollow: Array>) { href$.pipe( @@ -192,9 +192,9 @@ export abstract class DataService { /** * Returns an observable of {@link RemoteData} of an object, based on its ID, with a list of {@link FollowLinkConfig}, - * to add embedded info to the object + * to automatically resolve HALLinks of the object * @param id ID of object we want to retrieve - * @param linksToFollow List of {@link FollowLinkConfig} to indicate which embedded info should be retrieved and added + * @param linksToFollow List of {@link FollowLinkConfig} that indicate which HALLinks should be automatically resolved */ findById(id: string, ...linksToFollow: Array>): Observable> { @@ -216,9 +216,9 @@ export abstract class DataService { /** * Returns an observable of {@link RemoteData} of an object, based on an href, with a list of {@link FollowLinkConfig}, - * to add embedded info to the object + * to automatically resolve HALLinks of the object * @param href Href of object we want to retrieve - * @param linksToFollow List of {@link FollowLinkConfig} to indicate which embedded info should be retrieved and added + * @param linksToFollow List of {@link FollowLinkConfig} that indicate which HALLinks should be automatically resolved */ findByHref(href: string, ...linksToFollow: Array>): Observable> { const requestHref = this.buildHrefFromFindOptions(href, {}, []); @@ -232,9 +232,9 @@ export abstract class DataService { /** * Returns a list of observables of {@link RemoteData} of objects, based on an href, with a list of {@link FollowLinkConfig}, - * to add embedded info to the object + * to automatically resolve HALLinks of the object * @param id ID of object we want to retrieve - * @param linksToFollow List of {@link FollowLinkConfig} to indicate which embedded info should be retrieved and added + * @param linksToFollow List of {@link FollowLinkConfig} that indicate which HALLinks should be automatically resolved */ findAllByHref(href: string, findListOptions: FindListOptions = {}, ...linksToFollow: Array>): Observable>> { const requestHref = this.buildHrefFromFindOptions(href, findListOptions, []); @@ -302,16 +302,16 @@ export abstract class DataService { * @param {DSpaceObject} object The given object */ update(object: T): Observable> { - const oldVersion$ = this.findByHref(object.self); + const oldVersion$ = this.findByHref(object._links.self.href); return oldVersion$.pipe( getSucceededRemoteData(), getRemoteDataPayload(), mergeMap((oldVersion: T) => { const operations = this.comparator.diff(oldVersion, object); if (isNotEmpty(operations)) { - this.objectCache.addPatch(object.self, operations); + this.objectCache.addPatch(object._links.self.href, operations); } - return this.findByHref(object.self); + return this.findByHref(object._links.self.href); } )); } @@ -334,7 +334,7 @@ export abstract class DataService { ); const normalizedObject: NormalizedObject = this.dataBuildService.normalize(dso); - const serializedDso = new DSpaceRESTv2Serializer(getMapsToType((dso as any).type)).serialize(normalizedObject); + const serializedDso = new NormalizedObjectSerializer(getMapsToType((dso as any).type)).serialize(normalizedObject); const request$ = endpoint$.pipe( take(1), diff --git a/src/app/core/data/default-change-analyzer.service.ts b/src/app/core/data/default-change-analyzer.service.ts index 862c0e5b85..8e8674749b 100644 --- a/src/app/core/data/default-change-analyzer.service.ts +++ b/src/app/core/data/default-change-analyzer.service.ts @@ -1,10 +1,11 @@ -import { Operation } from 'fast-json-patch/lib/core'; -import { compare } from 'fast-json-patch'; -import { ChangeAnalyzer } from './change-analyzer'; import { Injectable } from '@angular/core'; -import { CacheableObject } from '../cache/object-cache.reducer'; +import { compare } from 'fast-json-patch'; +import { Operation } from 'fast-json-patch/lib/core'; +import { getMapsToType } from '../cache/builders/build-decorators'; import { NormalizedObject } from '../cache/models/normalized-object.model'; -import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service'; +import { CacheableObject } from '../cache/object-cache.reducer'; +import { DSpaceSerializer } from '../dspace-rest-v2/dspace.serializer'; +import { ChangeAnalyzer } from './change-analyzer'; /** * A class to determine what differs between two @@ -12,7 +13,7 @@ import { NormalizedObjectBuildService } from '../cache/builders/normalized-objec */ @Injectable() export class DefaultChangeAnalyzer implements ChangeAnalyzer { - constructor(private normalizeService: NormalizedObjectBuildService) { + constructor() { } /** @@ -24,7 +25,9 @@ export class DefaultChangeAnalyzer implements ChangeA * @param {NormalizedObject} object2 * The second object to compare */ - diff(object1: T | NormalizedObject, object2: T | NormalizedObject): Operation[] { - return compare(this.normalizeService.normalize(object1), this.normalizeService.normalize(object2)); + diff(object1: T, object2: T): Operation[] { + const serializer1 = new DSpaceSerializer(getMapsToType(object1.type)); + const serializer2 = new DSpaceSerializer(getMapsToType(object2.type)); + return compare(serializer1.serialize(object1), serializer2.serialize(object2)); } } diff --git a/src/app/core/data/dso-change-analyzer.service.ts b/src/app/core/data/dso-change-analyzer.service.ts index dd3487d3d0..7dc5f8fd2d 100644 --- a/src/app/core/data/dso-change-analyzer.service.ts +++ b/src/app/core/data/dso-change-analyzer.service.ts @@ -16,12 +16,12 @@ export class DSOChangeAnalyzer implements ChangeAnalyzer * Compare the metadata of two DSpaceObjects and return the differences as * a JsonPatch Operation Array * - * @param {NormalizedDSpaceObject} object1 + * @param {DSpaceObject} object1 * The first object to compare - * @param {NormalizedDSpaceObject} object2 + * @param {DSpaceObject} object2 * The second object to compare */ - diff(object1: T | NormalizedDSpaceObject, object2: T | NormalizedDSpaceObject): Operation[] { + diff(object1: DSpaceObject, object2: DSpaceObject): Operation[] { return compare(object1.metadata, object2.metadata).map((operation: Operation) => Object.assign({}, operation, { path: '/metadata' + operation.path })); } } diff --git a/src/app/core/data/dso-response-parsing.service.ts b/src/app/core/data/dso-response-parsing.service.ts index d2c21825cc..1c9c7d0e55 100644 --- a/src/app/core/data/dso-response-parsing.service.ts +++ b/src/app/core/data/dso-response-parsing.service.ts @@ -42,7 +42,7 @@ export class DSOResponseParsingService extends BaseResponseParsingService implem } else if (!Array.isArray(processRequestDTO)) { objectList = [processRequestDTO]; } - const selfLinks = objectList.map((no) => no.self); + const selfLinks = objectList.map((no) => no._links.self.href); return new DSOSuccessResponse(selfLinks, data.statusCode, data.statusText, this.processPageInfo(data.payload)) } diff --git a/src/app/core/data/entity-type-data.service.ts b/src/app/core/data/entity-type-data.service.ts new file mode 100644 index 0000000000..caad775139 --- /dev/null +++ b/src/app/core/data/entity-type-data.service.ts @@ -0,0 +1,76 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Store } from '@ngrx/store'; +import { Observable } from 'rxjs'; +import { NotificationsService } from '../../shared/notifications/notifications.service'; +import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; +import { dataService } from '../cache/builders/build-decorators'; +import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service'; +import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; +import { ObjectCacheService } from '../cache/object-cache.service'; +import { CoreState } from '../core.reducers'; +import { HALEndpointService } from '../shared/hal-endpoint.service'; +import { ItemType } from '../shared/item-relationships/item-type.model'; +import { ITEM_TYPE } from '../shared/item-relationships/item-type.resource-type'; +import { DataService } from './data.service'; +import { DefaultChangeAnalyzer } from './default-change-analyzer.service'; +import { PaginatedList } from './paginated-list'; +import { RemoteData } from './remote-data'; +import { FindListOptions } from './request.models'; +import { RequestService } from './request.service'; + +class DataServiceImpl extends DataService { + protected linkPath = 'entitytypes'; + + constructor( + protected requestService: RequestService, + protected rdbService: RemoteDataBuildService, + protected dataBuildService: NormalizedObjectBuildService, + protected store: Store, + protected objectCache: ObjectCacheService, + protected halService: HALEndpointService, + protected notificationsService: NotificationsService, + protected http: HttpClient, + protected comparator: DefaultChangeAnalyzer) { + super(); + } +} + +@Injectable() +@dataService(ITEM_TYPE) +export class ItemTypeDataService { + private dataService: DataServiceImpl; + + constructor( + protected requestService: RequestService, + protected rdbService: RemoteDataBuildService, + protected dataBuildService: NormalizedObjectBuildService, + protected store: Store, + protected objectCache: ObjectCacheService, + protected halService: HALEndpointService, + protected notificationsService: NotificationsService, + protected http: HttpClient, + protected comparator: DefaultChangeAnalyzer) { + this.dataService = new DataServiceImpl(requestService, rdbService, dataBuildService, null, objectCache, halService, notificationsService, http, comparator); + } + + /** + * Returns an observable of {@link RemoteData} of an object, based on an href, with a list of {@link FollowLinkConfig}, + * to automatically resolve HALLinks of the object + * @param href Href of object we want to retrieve + * @param linksToFollow List of {@link FollowLinkConfig} that indicate which HALLinks should be automatically resolved + */ + findByHref(href: string, ...linksToFollow: Array>): Observable> { + return this.dataService.findByHref(href, ...linksToFollow); + } + + /** + * Returns a list of observables of {@link RemoteData} of objects, based on an href, with a list of {@link FollowLinkConfig}, + * to automatically resolve HALLinks of the object + * @param id ID of object we want to retrieve + * @param linksToFollow List of {@link FollowLinkConfig} that indicate which HALLinks should be automatically resolved + */ + findByAllHref(href: string, findListOptions: FindListOptions = {}, ...linksToFollow: Array>): Observable>> { + return this.dataService.findAllByHref(href, findListOptions, ...linksToFollow); + } +} diff --git a/src/app/core/data/facet-config-response-parsing.service.ts b/src/app/core/data/facet-config-response-parsing.service.ts index 19b37f8b5d..1cc5e86b3e 100644 --- a/src/app/core/data/facet-config-response-parsing.service.ts +++ b/src/app/core/data/facet-config-response-parsing.service.ts @@ -6,7 +6,7 @@ import { import { ResponseParsingService } from './parsing.service'; import { RestRequest } from './request.models'; import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model'; -import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer'; +import { NormalizedObjectSerializer } from '../dspace-rest-v2/normalized-object.serializer'; import { SearchFilterConfig } from '../../shared/search/search-filter-config.model'; import { BaseResponseParsingService } from './base-response-parsing.service'; import { ObjectCacheService } from '../cache/object-cache.service'; @@ -24,7 +24,7 @@ export class FacetConfigResponseParsingService extends BaseResponseParsingServic parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse { const config = data.payload._embedded.facets; - const serializer = new DSpaceRESTv2Serializer(SearchFilterConfig); + const serializer = new NormalizedObjectSerializer(SearchFilterConfig); const facetConfig = serializer.deserializeArray(config); return new FacetConfigSuccessResponse(facetConfig, data.statusCode, data.statusText); } diff --git a/src/app/core/data/facet-value-map-response-parsing.service.ts b/src/app/core/data/facet-value-map-response-parsing.service.ts index 64c8e87e7d..51e97d2092 100644 --- a/src/app/core/data/facet-value-map-response-parsing.service.ts +++ b/src/app/core/data/facet-value-map-response-parsing.service.ts @@ -8,7 +8,7 @@ import { import { ResponseParsingService } from './parsing.service'; import { RestRequest } from './request.models'; import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model'; -import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer'; +import { NormalizedObjectSerializer } from '../dspace-rest-v2/normalized-object.serializer'; import { FacetValue } from '../../shared/search/facet-value.model'; import { BaseResponseParsingService } from './base-response-parsing.service'; import { ObjectCacheService } from '../cache/object-cache.service'; @@ -30,7 +30,7 @@ export class FacetValueMapResponseParsingService extends BaseResponseParsingServ const payload = data.payload; const facetMap: FacetValueMap = new FacetValueMap(); - const serializer = new DSpaceRESTv2Serializer(FacetValue); + const serializer = new NormalizedObjectSerializer(FacetValue); payload._embedded.facets.map((facet) => { const values = facet._embedded.values.map((value) => {value.search = value._links.search.href; return value;}); const facetValues = serializer.deserializeArray(values); diff --git a/src/app/core/data/facet-value-response-parsing.service.ts b/src/app/core/data/facet-value-response-parsing.service.ts index 7fedc17545..ac83176acc 100644 --- a/src/app/core/data/facet-value-response-parsing.service.ts +++ b/src/app/core/data/facet-value-response-parsing.service.ts @@ -3,8 +3,8 @@ import { FacetValueSuccessResponse, RestResponse } from '../cache/response.model import { ResponseParsingService } from './parsing.service'; import { RestRequest } from './request.models'; import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model'; -import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer'; -import {FacetValue} from '../../shared/search/facet-value.model'; +import { NormalizedObjectSerializer } from '../dspace-rest-v2/normalized-object.serializer'; +import { FacetValue } from '../../shared/search/facet-value.model'; import { BaseResponseParsingService } from './base-response-parsing.service'; import { ObjectCacheService } from '../cache/object-cache.service'; import { GLOBAL_CONFIG } from '../../../config'; @@ -21,7 +21,7 @@ export class FacetValueResponseParsingService extends BaseResponseParsingService parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse { const payload = data.payload; - const serializer = new DSpaceRESTv2Serializer(FacetValue); + const serializer = new NormalizedObjectSerializer(FacetValue); // const values = payload._embedded.values.map((value) => {value.search = value._links.search.href; return value;}); const facetValues = serializer.deserializeArray(payload._embedded.values); diff --git a/src/app/core/data/item-data.service.ts b/src/app/core/data/item-data.service.ts index fef3292101..ce189b561d 100644 --- a/src/app/core/data/item-data.service.ts +++ b/src/app/core/data/item-data.service.ts @@ -271,7 +271,7 @@ export class ItemDataService extends DataService { href$.pipe( find((href: string) => hasValue(href)), map((href: string) => { - const request = new PostRequest(requestId, href, externalSourceEntry.self, options); + const request = new PostRequest(requestId, href, externalSourceEntry._links.self.href, options); this.requestService.configure(request); }) ).subscribe(); diff --git a/src/app/core/data/license-data.service.ts b/src/app/core/data/license-data.service.ts new file mode 100644 index 0000000000..799de864ff --- /dev/null +++ b/src/app/core/data/license-data.service.ts @@ -0,0 +1,64 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Store } from '@ngrx/store'; +import { Observable } from 'rxjs'; +import { NotificationsService } from '../../shared/notifications/notifications.service'; +import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; +import { dataService } from '../cache/builders/build-decorators'; +import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service'; +import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; +import { ObjectCacheService } from '../cache/object-cache.service'; +import { CoreState } from '../core.reducers'; +import { HALEndpointService } from '../shared/hal-endpoint.service'; +import { License } from '../shared/license.model'; +import { LICENSE } from '../shared/license.resource-type'; +import { DataService } from './data.service'; +import { DefaultChangeAnalyzer } from './default-change-analyzer.service'; +import { PaginatedList } from './paginated-list'; +import { RemoteData } from './remote-data'; +import { FindListOptions } from './request.models'; +import { RequestService } from './request.service'; + +class DataServiceImpl extends DataService { + protected linkPath = ''; + + constructor( + protected requestService: RequestService, + protected rdbService: RemoteDataBuildService, + protected dataBuildService: NormalizedObjectBuildService, + protected store: Store, + protected objectCache: ObjectCacheService, + protected halService: HALEndpointService, + protected notificationsService: NotificationsService, + protected http: HttpClient, + protected comparator: DefaultChangeAnalyzer) { + super(); + } +} + +@Injectable() +@dataService(LICENSE) +export class LicenseDataService { + private dataService: DataServiceImpl; + + constructor( + protected requestService: RequestService, + protected rdbService: RemoteDataBuildService, + protected dataBuildService: NormalizedObjectBuildService, + protected store: Store, + protected objectCache: ObjectCacheService, + protected halService: HALEndpointService, + protected notificationsService: NotificationsService, + protected http: HttpClient, + protected comparator: DefaultChangeAnalyzer) { + this.dataService = new DataServiceImpl(requestService, rdbService, dataBuildService, null, objectCache, halService, notificationsService, http, comparator); + } + + findByHref(href: string, ...linksToFollow: Array>): Observable> { + return this.dataService.findByHref(href, ...linksToFollow); + } + + findByAllHref(href: string, findListOptions: FindListOptions = {}, ...linksToFollow: Array>): Observable>> { + return this.dataService.findAllByHref(href, findListOptions, ...linksToFollow); + } +} diff --git a/src/app/core/data/metadatafield-parsing.service.ts b/src/app/core/data/metadatafield-parsing.service.ts index 092285e9c5..bc5ea1e199 100644 --- a/src/app/core/data/metadatafield-parsing.service.ts +++ b/src/app/core/data/metadatafield-parsing.service.ts @@ -1,4 +1,4 @@ -import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer'; +import { NormalizedObjectSerializer } from '../dspace-rest-v2/normalized-object.serializer'; import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model'; import { RestRequest } from './request.models'; import { ResponseParsingService } from './parsing.service'; @@ -15,7 +15,7 @@ export class MetadatafieldParsingService implements ResponseParsingService { parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse { const payload = data.payload; - const deserialized = new DSpaceRESTv2Serializer(MetadataField).deserialize(payload); + const deserialized = new NormalizedObjectSerializer(MetadataField).deserialize(payload); return new MetadatafieldSuccessResponse(deserialized, data.statusCode, data.statusText); } diff --git a/src/app/core/data/metadataschema-parsing.service.ts b/src/app/core/data/metadataschema-parsing.service.ts index 3e9fd257bb..6d9867eb52 100644 --- a/src/app/core/data/metadataschema-parsing.service.ts +++ b/src/app/core/data/metadataschema-parsing.service.ts @@ -1,4 +1,4 @@ -import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer'; +import { NormalizedObjectSerializer } from '../dspace-rest-v2/normalized-object.serializer'; import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model'; import { RestRequest } from './request.models'; import { ResponseParsingService } from './parsing.service'; @@ -12,7 +12,7 @@ export class MetadataschemaParsingService implements ResponseParsingService { parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse { const payload = data.payload; - const deserialized = new DSpaceRESTv2Serializer(MetadataSchema).deserialize(payload); + const deserialized = new NormalizedObjectSerializer(MetadataSchema).deserialize(payload); return new MetadataschemaSuccessResponse(deserialized, data.statusCode, data.statusText); } diff --git a/src/app/core/data/mydspace-response-parsing.service.ts b/src/app/core/data/mydspace-response-parsing.service.ts index bd5d5b1083..cbaf28d34b 100644 --- a/src/app/core/data/mydspace-response-parsing.service.ts +++ b/src/app/core/data/mydspace-response-parsing.service.ts @@ -4,7 +4,7 @@ import { DSOResponseParsingService } from './dso-response-parsing.service'; import { ResponseParsingService } from './parsing.service'; import { RestRequest } from './request.models'; import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model'; -import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer'; +import { NormalizedObjectSerializer } from '../dspace-rest-v2/normalized-object.serializer'; import { hasValue } from '../../shared/empty.util'; import { SearchQueryResponse } from '../../shared/search/search-query-response.model'; import { MetadataMap, MetadataValue } from '../shared/metadata.models'; @@ -57,7 +57,7 @@ export class MyDSpaceResponseParsingService implements ResponseParsingService { _embedded: this.filterEmbeddedObjects(object) })); payload.objects = objects; - const deserialized = new DSpaceRESTv2Serializer(SearchQueryResponse).deserialize(payload); + const deserialized = new NormalizedObjectSerializer(SearchQueryResponse).deserialize(payload); return new SearchSuccessResponse(deserialized, data.statusCode, data.statusText, this.dsoParser.processPageInfo(payload)); } diff --git a/src/app/core/data/registry-bitstreamformats-response-parsing.service.ts b/src/app/core/data/registry-bitstreamformats-response-parsing.service.ts index 899fee4d1e..5391a2a003 100644 --- a/src/app/core/data/registry-bitstreamformats-response-parsing.service.ts +++ b/src/app/core/data/registry-bitstreamformats-response-parsing.service.ts @@ -1,6 +1,6 @@ import { RegistryBitstreamformatsSuccessResponse, RestResponse } from '../cache/response.models'; import { RegistryBitstreamformatsResponse } from '../registry/registry-bitstreamformats-response.model'; -import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer'; +import { NormalizedObjectSerializer } from '../dspace-rest-v2/normalized-object.serializer'; import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model'; import { RestRequest } from './request.models'; import { DSOResponseParsingService } from './dso-response-parsing.service'; @@ -18,7 +18,7 @@ export class RegistryBitstreamformatsResponseParsingService implements ResponseP const bitstreamformats = payload._embedded.bitstreamformats; payload.bitstreamformats = bitstreamformats; - const deserialized = new DSpaceRESTv2Serializer(RegistryBitstreamformatsResponse).deserialize(payload); + const deserialized = new NormalizedObjectSerializer(RegistryBitstreamformatsResponse).deserialize(payload); return new RegistryBitstreamformatsSuccessResponse(deserialized, data.statusCode, data.statusText, this.dsoParser.processPageInfo(data.payload.page)); } diff --git a/src/app/core/data/registry-metadatafields-response-parsing.service.ts b/src/app/core/data/registry-metadatafields-response-parsing.service.ts index a4bed3240e..4b826c23f3 100644 --- a/src/app/core/data/registry-metadatafields-response-parsing.service.ts +++ b/src/app/core/data/registry-metadatafields-response-parsing.service.ts @@ -5,7 +5,7 @@ import { import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model'; import { RestRequest } from './request.models'; import { ResponseParsingService } from './parsing.service'; -import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer'; +import { NormalizedObjectSerializer } from '../dspace-rest-v2/normalized-object.serializer'; import { DSOResponseParsingService } from './dso-response-parsing.service'; import { Injectable } from '@angular/core'; import { RegistryMetadatafieldsResponse } from '../registry/registry-metadatafields-response.model'; @@ -30,7 +30,7 @@ export class RegistryMetadatafieldsResponseParsingService implements ResponsePar payload.metadatafields = metadatafields; - const deserialized = new DSpaceRESTv2Serializer(RegistryMetadatafieldsResponse).deserialize(payload); + const deserialized = new NormalizedObjectSerializer(RegistryMetadatafieldsResponse).deserialize(payload); return new RegistryMetadatafieldsSuccessResponse(deserialized, data.statusCode, data.statusText, this.dsoParser.processPageInfo(data.payload)); } diff --git a/src/app/core/data/registry-metadataschemas-response-parsing.service.ts b/src/app/core/data/registry-metadataschemas-response-parsing.service.ts index d19b334131..9f60c6ec94 100644 --- a/src/app/core/data/registry-metadataschemas-response-parsing.service.ts +++ b/src/app/core/data/registry-metadataschemas-response-parsing.service.ts @@ -3,7 +3,7 @@ import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response. import { RestRequest } from './request.models'; import { ResponseParsingService } from './parsing.service'; import { RegistryMetadataschemasResponse } from '../registry/registry-metadataschemas-response.model'; -import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer'; +import { NormalizedObjectSerializer } from '../dspace-rest-v2/normalized-object.serializer'; import { DSOResponseParsingService } from './dso-response-parsing.service'; import { Injectable } from '@angular/core'; import { hasValue } from '../../shared/empty.util'; @@ -22,7 +22,7 @@ export class RegistryMetadataschemasResponseParsingService implements ResponsePa } payload.metadataschemas = metadataschemas; - const deserialized = new DSpaceRESTv2Serializer(RegistryMetadataschemasResponse).deserialize(payload); + const deserialized = new NormalizedObjectSerializer(RegistryMetadataschemasResponse).deserialize(payload); return new RegistryMetadataschemasSuccessResponse(deserialized, data.statusCode, data.statusText, this.dsoParser.processPageInfo(data.payload)); } diff --git a/src/app/core/data/relationship-type.service.ts b/src/app/core/data/relationship-type.service.ts index f77f444877..017bf7bf91 100644 --- a/src/app/core/data/relationship-type.service.ts +++ b/src/app/core/data/relationship-type.service.ts @@ -64,7 +64,7 @@ export class RelationshipTypeService extends DataService { .pipe( map((endpointURL: string) => new FindListRequest(this.requestService.generateRequestId(), endpointURL, options)), configureRequest(this.requestService), - switchMap(() => this.rdbService.buildList(link$, followLink('leftType'), followLink('rightType'))) + switchMap(() => this.rdbService.buildList(link$, followLink('leftType'), followLink('rightType'))) ) as Observable>>; } diff --git a/src/app/core/data/relationship.service.ts b/src/app/core/data/relationship.service.ts index b85186a0cc..7fe7b23c9c 100644 --- a/src/app/core/data/relationship.service.ts +++ b/src/app/core/data/relationship.service.ts @@ -164,15 +164,15 @@ export class RelationshipService extends DataService { * @param item The item to remove from the cache */ private removeRelationshipItemsFromCache(item) { - this.objectCache.remove(item.self); + this.objectCache.remove(item._links.self.href); this.requestService.removeByHrefSubstring(item.uuid); combineLatest( - this.objectCache.hasBySelfLinkObservable(item.self), + this.objectCache.hasBySelfLinkObservable(item._links.self.href), this.requestService.hasByHrefObservable(item.uuid) ).pipe( filter(([existsInOC, existsInRC]) => !existsInOC && !existsInRC), take(1), - switchMap(() => this.itemService.findByHref(item.self).pipe(take(1))) + switchMap(() => this.itemService.findByHref(item._links.self.href).pipe(take(1))) ).subscribe(); } diff --git a/src/app/core/data/request.effects.ts b/src/app/core/data/request.effects.ts index 9ef85bfe8b..61b99e2b6d 100644 --- a/src/app/core/data/request.effects.ts +++ b/src/app/core/data/request.effects.ts @@ -16,7 +16,7 @@ import { import { RequestError, RestRequest } from './request.models'; import { RequestEntry } from './request.reducer'; import { RequestService } from './request.service'; -import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer'; +import { NormalizedObjectSerializer } from '../dspace-rest-v2/normalized-object.serializer'; import { catchError, filter, flatMap, map, take, tap } from 'rxjs/operators'; import { ErrorResponse, RestResponse } from '../cache/response.models'; import { StoreActionTypes } from '../../store.actions'; @@ -45,7 +45,7 @@ export class RequestEffects { flatMap((request: RestRequest) => { let body; if (isNotEmpty(request.body)) { - const serializer = new DSpaceRESTv2Serializer(getMapsToType(request.body.type)); + const serializer = new NormalizedObjectSerializer(getMapsToType(request.body.type)); body = serializer.serialize(request.body); } return this.restApi.request(request.method, request.href, body, request.options).pipe( diff --git a/src/app/core/data/resource-policy.service.ts b/src/app/core/data/resource-policy.service.ts index 49e69f45e0..acfc013f0d 100644 --- a/src/app/core/data/resource-policy.service.ts +++ b/src/app/core/data/resource-policy.service.ts @@ -3,6 +3,8 @@ import { HttpClient } from '@angular/common/http'; import { Store } from '@ngrx/store'; import { Observable } from 'rxjs'; +import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; +import { dataService } from '../cache/builders/build-decorators'; import { DataService } from '../data/data.service'; import { RequestService } from '../data/request.service'; @@ -16,6 +18,7 @@ import { CoreState } from '../core.reducers'; import { ObjectCacheService } from '../cache/object-cache.service'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service'; +import { RESOURCE_POLICY } from '../shared/resource-policy.resource-type'; import { ChangeAnalyzer } from './change-analyzer'; import { DefaultChangeAnalyzer } from '../data/default-change-analyzer.service'; import { PaginatedList } from './paginated-list'; @@ -43,6 +46,7 @@ class DataServiceImpl extends DataService { * A service responsible for fetching/sending data from/to the REST API on the resourcepolicies endpoint */ @Injectable() +@dataService(RESOURCE_POLICY) export class ResourcePolicyService { private dataService: DataServiceImpl; @@ -58,8 +62,12 @@ export class ResourcePolicyService { this.dataService = new DataServiceImpl(requestService, rdbService, dataBuildService, null, objectCache, halService, notificationsService, http, comparator); } - findByHref(href: string): Observable> { - return this.dataService.findByHref(href); + findByHref(href: string, ...linksToFollow: Array>): Observable> { + return this.dataService.findByHref(href, ...linksToFollow); + } + + findAllByHref(href: string, findListOptions: FindListOptions = {}, ...linksToFollow: Array>): Observable>> { + return this.dataService.findAllByHref(href, findListOptions, ...linksToFollow); } getDefaultAccessConditionsFor(collection: Collection, findListOptions?: FindListOptions): Observable>> { diff --git a/src/app/core/data/search-response-parsing.service.ts b/src/app/core/data/search-response-parsing.service.ts index c449fa872f..f7c20b1eac 100644 --- a/src/app/core/data/search-response-parsing.service.ts +++ b/src/app/core/data/search-response-parsing.service.ts @@ -4,7 +4,7 @@ import { DSOResponseParsingService } from './dso-response-parsing.service'; import { ResponseParsingService } from './parsing.service'; import { RestRequest } from './request.models'; import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model'; -import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer'; +import { NormalizedObjectSerializer } from '../dspace-rest-v2/normalized-object.serializer'; import { hasValue } from '../../shared/empty.util'; import { SearchQueryResponse } from '../../shared/search/search-query-response.model'; import { MetadataMap, MetadataValue } from '../shared/metadata.models'; @@ -59,13 +59,9 @@ export class SearchResponseParsingService implements ResponseParsingService { .map((object, index) => Object.assign({}, object, { indexableObject: dsoSelfLinks[index], hitHighlights: hitHighlights[index], - // we don't need embedded collections, bitstreamformats, etc for search results. - // And parsing them all takes up a lot of time. Throw them away to improve performance - // until objs until partial results are supported by the rest api - _embedded: undefined })); payload.objects = objects; - const deserialized = new DSpaceRESTv2Serializer(SearchQueryResponse).deserialize(payload); + const deserialized = new NormalizedObjectSerializer(SearchQueryResponse).deserialize(payload); return new SearchSuccessResponse(deserialized, data.statusCode, data.statusText, this.dsoParser.processPageInfo(payload)); } } diff --git a/src/app/core/dspace-rest-v2/dspace.serializer.ts b/src/app/core/dspace-rest-v2/dspace.serializer.ts new file mode 100644 index 0000000000..e16094a040 --- /dev/null +++ b/src/app/core/dspace-rest-v2/dspace.serializer.ts @@ -0,0 +1,66 @@ +import { Deserialize, Serialize } from 'cerialize'; + +import { Serializer } from '../serializer'; +import { GenericConstructor } from '../shared/generic-constructor'; + +/** + * This Serializer turns responses from v2 of DSpace's REST API + * to models and vice versa + */ +export class DSpaceSerializer implements Serializer { + + /** + * Create a new DSpaceSerializer instance + * + * @param modelType a class or interface to indicate + * the kind of model this serializer should work with + */ + constructor(private modelType: GenericConstructor) { + } + + /** + * Convert a model in to the format expected by the backend + * + * @param model The model to serialize + * @returns An object to send to the backend + */ + serialize(model: T): any { + return Serialize(model, this.modelType); + } + + /** + * Convert an array of models in to the format expected by the backend + * + * @param models The array of models to serialize + * @returns An object to send to the backend + */ + serializeArray(models: T[]): any { + return Serialize(models, this.modelType); + } + + /** + * Convert a response from the backend in to a model. + * + * @param response An object returned by the backend + * @returns a model of type T + */ + deserialize(response: any): T { + if (Array.isArray(response)) { + throw new Error('Expected a single model, use deserializeArray() instead'); + } + return Deserialize(response, this.modelType) as T; + } + + /** + * Convert a response from the backend in to an array of models + * + * @param response An object returned by the backend + * @returns an array of models of type T + */ + deserializeArray(response: any): T[] { + if (!Array.isArray(response)) { + throw new Error('Expected an Array, use deserialize() instead'); + } + return Deserialize(response, this.modelType) as T[]; + } +} diff --git a/src/app/core/dspace-rest-v2/dspace-rest-v2.serializer.spec.ts b/src/app/core/dspace-rest-v2/normalized-object.serializer.spec.ts similarity index 86% rename from src/app/core/dspace-rest-v2/dspace-rest-v2.serializer.spec.ts rename to src/app/core/dspace-rest-v2/normalized-object.serializer.spec.ts index 8431d6f8b3..ab9ac0795f 100644 --- a/src/app/core/dspace-rest-v2/dspace-rest-v2.serializer.spec.ts +++ b/src/app/core/dspace-rest-v2/normalized-object.serializer.spec.ts @@ -1,6 +1,6 @@ import { autoserialize, autoserializeAs } from 'cerialize'; -import { DSpaceRESTv2Serializer } from './dspace-rest-v2.serializer'; +import { NormalizedObjectSerializer } from './normalized-object.serializer'; class TestModel { @autoserialize @@ -53,12 +53,12 @@ const testResponses = [ const parentHrefRegex = /^\/testmodels\/(.+)$/g; -describe('DSpaceRESTv2Serializer', () => { +describe('NormalizedObjectSerializer', () => { describe('serialize', () => { it('should turn a model in to a valid document', () => { - const serializer = new DSpaceRESTv2Serializer(TestModel); + const serializer = new NormalizedObjectSerializer(TestModel); const doc = serializer.serialize(testModels[0]); expect(testModels[0].id).toBe(doc.id); expect(testModels[0].name).toBe(doc.name); @@ -69,7 +69,7 @@ describe('DSpaceRESTv2Serializer', () => { describe('serializeArray', () => { it('should turn an array of models in to a valid document', () => { - const serializer = new DSpaceRESTv2Serializer(TestModel); + const serializer = new NormalizedObjectSerializer(TestModel); const doc = serializer.serializeArray(testModels); expect(testModels[0].id).toBe(doc[0].id); @@ -83,7 +83,7 @@ describe('DSpaceRESTv2Serializer', () => { describe('deserialize', () => { it('should turn a valid document describing a single entity in to a valid model', () => { - const serializer = new DSpaceRESTv2Serializer(TestModel); + const serializer = new NormalizedObjectSerializer(TestModel); const model = serializer.deserialize(testResponses[0]); expect(model.id).toBe(testResponses[0].id); @@ -93,7 +93,7 @@ describe('DSpaceRESTv2Serializer', () => { // TODO: cant implement/test this yet - depends on how relationships // will be handled in the rest api // it('should retain relationship information', () => { - // const serializer = new DSpaceRESTv2Serializer(TestModel); + // const serializer = new NormalizedObjectSerializer(TestModel); // const doc = { // '_embedded': testResponses[0], // }; @@ -113,7 +113,7 @@ describe('DSpaceRESTv2Serializer', () => { // TODO enable once validation is enabled in the serializer // it('should throw an error when dealing with an invalid document', () => { - // const serializer = new DSpaceRESTv2Serializer(TestModel); + // const serializer = new NormalizedObjectSerializer(TestModel); // const doc = testResponses[0]; // // expect(() => { @@ -122,7 +122,7 @@ describe('DSpaceRESTv2Serializer', () => { // }); it('should throw an error when dealing with a document describing an array', () => { - const serializer = new DSpaceRESTv2Serializer(TestModel); + const serializer = new NormalizedObjectSerializer(TestModel); expect(() => { serializer.deserialize(testResponses); }).toThrow(); @@ -134,7 +134,7 @@ describe('DSpaceRESTv2Serializer', () => { // TODO: rewrite to incorporate normalisation. // it('should turn a valid document describing a collection of objects in to an array of valid models', () => { - // const serializer = new DSpaceRESTv2Serializer(TestModel); + // const serializer = new NormalizedObjectSerializer(TestModel); // const doc = { // '_embedded': testResponses // }; @@ -150,7 +150,7 @@ describe('DSpaceRESTv2Serializer', () => { // TODO: cant implement/test this yet - depends on how relationships // will be handled in the rest api // it('should retain relationship information', () => { - // const serializer = new DSpaceRESTv2Serializer(TestModel); + // const serializer = new NormalizedObjectSerializer(TestModel); // const doc = { // '_embedded': testResponses, // }; @@ -170,7 +170,7 @@ describe('DSpaceRESTv2Serializer', () => { // TODO enable once validation is enabled in the serializer // it('should throw an error when dealing with an invalid document', () => { - // const serializer = new DSpaceRESTv2Serializer(TestModel); + // const serializer = new NormalizedObjectSerializer(TestModel); // const doc = testResponses[0]; // // expect(() => { @@ -179,7 +179,7 @@ describe('DSpaceRESTv2Serializer', () => { // }); it('should throw an error when dealing with a document describing a single model', () => { - const serializer = new DSpaceRESTv2Serializer(TestModel); + const serializer = new NormalizedObjectSerializer(TestModel); const doc = { _embedded: testResponses[0] }; diff --git a/src/app/core/dspace-rest-v2/dspace-rest-v2.serializer.ts b/src/app/core/dspace-rest-v2/normalized-object.serializer.ts similarity index 86% rename from src/app/core/dspace-rest-v2/dspace-rest-v2.serializer.ts rename to src/app/core/dspace-rest-v2/normalized-object.serializer.ts index 0bb78ce973..d7c3332fa9 100644 --- a/src/app/core/dspace-rest-v2/dspace-rest-v2.serializer.ts +++ b/src/app/core/dspace-rest-v2/normalized-object.serializer.ts @@ -1,19 +1,19 @@ -import { Serialize, Deserialize } from 'cerialize'; +import { Deserialize, Serialize } from 'cerialize'; +import { deprecate } from 'util'; import { Serializer } from '../serializer'; -import { DSpaceRESTV2Response } from './dspace-rest-v2-response.model'; -import { DSpaceRESTv2Validator } from './dspace-rest-v2.validator'; import { GenericConstructor } from '../shared/generic-constructor'; -import { hasNoValue, hasValue } from '../../shared/empty.util'; /** * This Serializer turns responses from v2 of DSpace's REST API - * to models and vice versa + * to normalized models and vice versa + * + * @deprecated use DSpaceSerializer instead */ -export class DSpaceRESTv2Serializer implements Serializer { +export class NormalizedObjectSerializer implements Serializer { /** - * Create a new DSpaceRESTv2Serializer instance + * Create a new NormalizedObjectSerializer instance * * @param modelType a class or interface to indicate * the kind of model this serializer should work with diff --git a/src/app/core/eperson/models/eperson.model.ts b/src/app/core/eperson/models/eperson.model.ts index 52bbfaedf9..d7048eb6ba 100644 --- a/src/app/core/eperson/models/eperson.model.ts +++ b/src/app/core/eperson/models/eperson.model.ts @@ -1,4 +1,6 @@ +import { autoserialize, deserialize, inheritSerialization } from 'cerialize'; import { Observable } from 'rxjs'; +import { link, resourceType } from '../../cache/builders/build-decorators'; import { PaginatedList } from '../../data/paginated-list'; import { RemoteData } from '../../data/remote-data'; @@ -6,48 +8,53 @@ import { DSpaceObject } from '../../shared/dspace-object.model'; import { HALLink } from '../../shared/hal-link.model'; import { EPERSON } from './eperson.resource-type'; import { Group } from './group.model'; +import { GROUP } from './group.resource-type'; +@resourceType(EPerson.type) +@inheritSerialization(DSpaceObject) export class EPerson extends DSpaceObject { static type = EPERSON; /** * A string representing the unique handle of this Collection */ + @autoserialize public handle: string; - /** - * List of Groups that this EPerson belong to - */ - public groups: Observable>>; - /** * A string representing the netid of this EPerson */ + @autoserialize public netid: string; /** * A string representing the last active date for this EPerson */ + @autoserialize public lastActive: string; /** * A boolean representing if this EPerson can log in */ + @autoserialize public canLogIn: boolean; /** * The EPerson email address */ + @autoserialize public email: string; /** * A boolean representing if this EPerson require certificate */ + @autoserialize public requireCertificate: boolean; /** * A boolean representing if this EPerson registered itself */ + @autoserialize public selfRegistered: boolean; /** @@ -58,7 +65,15 @@ export class EPerson extends DSpaceObject { } _links: { - self: HALLink, - groups: HALLink, - } + self: HALLink; + groups: HALLink; + }; + + /** + * The list of Groups this EPerson is part of + * Will be undefined unless the groups HALLink has been resolved. + */ + @link(GROUP, true) + public groups?: Observable>>; + } diff --git a/src/app/core/eperson/models/group.model.ts b/src/app/core/eperson/models/group.model.ts index e6073c7dbb..28a121d410 100644 --- a/src/app/core/eperson/models/group.model.ts +++ b/src/app/core/eperson/models/group.model.ts @@ -1,30 +1,44 @@ +import { autoserialize, deserialize, inheritSerialization } from 'cerialize'; import { Observable } from 'rxjs'; +import { link, resourceType } from '../../cache/builders/build-decorators'; import { PaginatedList } from '../../data/paginated-list'; import { RemoteData } from '../../data/remote-data'; import { DSpaceObject } from '../../shared/dspace-object.model'; +import { HALLink } from '../../shared/hal-link.model'; import { GROUP } from './group.resource-type'; +@resourceType(Group.type) +@inheritSerialization(DSpaceObject) export class Group extends DSpaceObject { static type = GROUP; - /** - * List of Groups that this Group belong to - */ - public groups: Observable>>; - /** * A string representing the unique handle of this Group */ + @autoserialize public handle: string; /** - * A string representing the name of this Group + * A boolean denoting whether this Group is permanent */ - public name: string; + @autoserialize + public permanent: boolean; /** - * A string representing the name of this Group is permanent + * The HALLinks for this Group */ - public permanent: boolean; + @deserialize + _links: { + self: HALLink; + groups: HALLink; + }; + + /** + * The list of Groups this Group is part of + * Will be undefined unless the groups HALLink has been resolved. + */ + @link(GROUP, true) + public groups?: Observable>>; + } diff --git a/src/app/core/eperson/models/normalized-eperson.model.ts b/src/app/core/eperson/models/normalized-eperson.model.ts deleted file mode 100644 index 489bf259c6..0000000000 --- a/src/app/core/eperson/models/normalized-eperson.model.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { autoserialize, deserialize, inheritSerialization } from 'cerialize'; - -import { CacheableObject } from '../../cache/object-cache.reducer'; -import { NormalizedDSpaceObject } from '../../cache/models/normalized-dspace-object.model'; -import { EPerson } from './eperson.model'; -import { mapsTo, relationship } from '../../cache/builders/build-decorators'; -import { Group } from './group.model'; - -@mapsTo(EPerson) -@inheritSerialization(NormalizedDSpaceObject) -export class NormalizedEPerson extends NormalizedDSpaceObject implements CacheableObject { - - /** - * A string representing the unique handle of this EPerson - */ - @autoserialize - public handle: string; - - /** - * List of Groups that this EPerson belong to - */ - @deserialize - @relationship(Group, true) - groups: string[]; - - /** - * A string representing the netid of this EPerson - */ - @autoserialize - public netid: string; - - /** - * A string representing the last active date for this EPerson - */ - @autoserialize - public lastActive: string; - - /** - * A boolean representing if this EPerson can log in - */ - @autoserialize - public canLogIn: boolean; - - /** - * The EPerson email address - */ - @autoserialize - public email: string; - - /** - * A boolean representing if this EPerson require certificate - */ - @autoserialize - public requireCertificate: boolean; - - /** - * A boolean representing if this EPerson registered itself - */ - @autoserialize - public selfRegistered: boolean; -} diff --git a/src/app/core/eperson/models/normalized-group.model.ts b/src/app/core/eperson/models/normalized-group.model.ts deleted file mode 100644 index 72b4e7b1a4..0000000000 --- a/src/app/core/eperson/models/normalized-group.model.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { autoserialize, deserialize, inheritSerialization } from 'cerialize'; - -import { CacheableObject } from '../../cache/object-cache.reducer'; -import { NormalizedDSpaceObject } from '../../cache/models/normalized-dspace-object.model'; -import { mapsTo, relationship } from '../../cache/builders/build-decorators'; -import { Group } from './group.model'; - -@mapsTo(Group) -@inheritSerialization(NormalizedDSpaceObject) -export class NormalizedGroup extends NormalizedDSpaceObject implements CacheableObject { - - /** - * List of Groups that this Group belong to - */ - @deserialize - @relationship(Group, true) - groups: string[]; - - /** - * A string representing the unique handle of this Group - */ - @autoserialize - public handle: string; - - /** - * A string representing the name of this Group - */ - @autoserialize - public name: string; - - /** - * A string representing the name of this Group is permanent - */ - @autoserialize - public permanent: boolean; -} diff --git a/src/app/core/eperson/models/workflowitem.resource-type.ts b/src/app/core/eperson/models/workflowitem.resource-type.ts new file mode 100644 index 0000000000..001b6b3f33 --- /dev/null +++ b/src/app/core/eperson/models/workflowitem.resource-type.ts @@ -0,0 +1,3 @@ +import { ResourceType } from '../../shared/resource-type'; + +export const WORKFLOWITEM = new ResourceType('workflowitem'); diff --git a/src/app/core/index/index.effects.ts b/src/app/core/index/index.effects.ts index 61cf313ab1..c9f6eace8f 100644 --- a/src/app/core/index/index.effects.ts +++ b/src/app/core/index/index.effects.ts @@ -24,7 +24,7 @@ export class UUIDIndexEffects { return new AddToIndexAction( IndexName.OBJECT, action.payload.objectToCache.uuid, - action.payload.objectToCache.self + action.payload.objectToCache._links.self.href ); }) ); diff --git a/src/app/core/integration/models/authority.value.ts b/src/app/core/integration/models/authority.value.ts index 06f8e13432..59301cadca 100644 --- a/src/app/core/integration/models/authority.value.ts +++ b/src/app/core/integration/models/authority.value.ts @@ -1,6 +1,8 @@ +import { autoserialize, deserialize, inheritSerialization } from 'cerialize'; import { isNotEmpty } from '../../../shared/empty.util'; import { PLACEHOLDER_PARENT_METADATA } from '../../../shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.model'; import { OtherInformation } from '../../../shared/form/builder/models/form-field-metadata-value.model'; +import { resourceType } from '../../cache/builders/build-decorators'; import { HALLink } from '../../shared/hal-link.model'; import { MetadataValueInterface } from '../../shared/metadata.models'; import { AUTHORITY_VALUE } from './authority.resource-type'; @@ -9,34 +11,42 @@ import { IntegrationModel } from './integration.model'; /** * Class representing an authority object */ +@resourceType(AuthorityValue.type) +@inheritSerialization(IntegrationModel) export class AuthorityValue extends IntegrationModel implements MetadataValueInterface { static type = AUTHORITY_VALUE; /** * The identifier of this authority */ + @autoserialize id: string; /** * The display value of this authority */ + @autoserialize display: string; /** * The value of this authority */ + @autoserialize value: string; /** * An object containing additional information related to this authority */ + @autoserialize otherInformation: OtherInformation; /** * The language code of this authority value */ + @autoserialize language: string; + @deserialize _links: { self: HALLink, }; diff --git a/src/app/core/integration/models/integration.model.ts b/src/app/core/integration/models/integration.model.ts index c84a1b6bd9..d2f21a70c0 100644 --- a/src/app/core/integration/models/integration.model.ts +++ b/src/app/core/integration/models/integration.model.ts @@ -1,4 +1,4 @@ -import { autoserialize } from 'cerialize'; +import { autoserialize, deserialize } from 'cerialize'; import { CacheableObject } from '../../cache/object-cache.reducer'; import { HALLink } from '../../shared/hal-link.model'; @@ -13,7 +13,7 @@ export abstract class IntegrationModel implements CacheableObject { @autoserialize public type: any; - @autoserialize + @deserialize public _links: { self: HALLink, [name: string]: HALLink diff --git a/src/app/core/integration/models/normalized-authority-value.model.ts b/src/app/core/integration/models/normalized-authority-value.model.ts deleted file mode 100644 index 5ebb61281d..0000000000 --- a/src/app/core/integration/models/normalized-authority-value.model.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { autoserialize, inheritSerialization } from 'cerialize'; -import { IntegrationModel } from './integration.model'; -import { mapsTo } from '../../cache/builders/build-decorators'; -import { AuthorityValue } from './authority.value'; - -/** - * Normalized model class for an Authority Value - */ -@mapsTo(AuthorityValue) -@inheritSerialization(IntegrationModel) -export class NormalizedAuthorityValue extends IntegrationModel { - - @autoserialize - id: string; - - @autoserialize - display: string; - - @autoserialize - value: string; - - @autoserialize - otherInformation: any; - - @autoserialize - language: string; - -} diff --git a/src/app/core/metadata/metadata-field.model.ts b/src/app/core/metadata/metadata-field.model.ts index 2cdbe84aee..bb4700e3b8 100644 --- a/src/app/core/metadata/metadata-field.model.ts +++ b/src/app/core/metadata/metadata-field.model.ts @@ -1,9 +1,12 @@ +import { autoserialize } from 'cerialize'; import { isNotEmpty } from '../../shared/empty.util'; import { ListableObject } from '../../shared/object-collection/shared/listable-object.model'; -import { link } from '../cache/builders/build-decorators'; +import { link, resourceType } from '../cache/builders/build-decorators'; import { GenericConstructor } from '../shared/generic-constructor'; import { HALLink } from '../shared/hal-link.model'; import { HALResource } from '../shared/hal-resource.model'; +import { ResourceType } from '../shared/resource-type'; +import { excludeFromEquals } from '../utilities/equals.decorators'; import { METADATA_FIELD } from './metadata-field.resource-type'; import { MetadataSchema } from './metadata-schema.model'; import { METADATA_SCHEMA } from './metadata-schema.resource-type'; @@ -11,9 +14,17 @@ import { METADATA_SCHEMA } from './metadata-schema.resource-type'; /** * Class the represents a metadata field */ +@resourceType(MetadataField.type) export class MetadataField extends ListableObject implements HALResource { static type = METADATA_FIELD; + /** + * The object type + */ + @excludeFromEquals + @autoserialize + type: ResourceType; + /** * The identifier of this metadata field */ @@ -40,7 +51,8 @@ export class MetadataField extends ListableObject implements HALResource { scopeNote: string; /** - * The metadata schema object of this metadata field + * The MetadataSchema for this MetadataField + * Will be undefined unless the schema HALLink has been resolved. */ @link(METADATA_SCHEMA) // TODO the responseparsingservice assumes schemas are always embedded. This should be remotedata instead. diff --git a/src/app/core/metadata/metadata-schema.model.ts b/src/app/core/metadata/metadata-schema.model.ts index 16c9cfbb29..16fa820837 100644 --- a/src/app/core/metadata/metadata-schema.model.ts +++ b/src/app/core/metadata/metadata-schema.model.ts @@ -1,12 +1,17 @@ +import { autoserialize } from 'cerialize'; import { ListableObject } from '../../shared/object-collection/shared/listable-object.model'; +import { resourceType } from '../cache/builders/build-decorators'; import { GenericConstructor } from '../shared/generic-constructor'; import { HALLink } from '../shared/hal-link.model'; import { HALResource } from '../shared/hal-resource.model'; +import { ResourceType } from '../shared/resource-type'; +import { excludeFromEquals } from '../utilities/equals.decorators'; import { METADATA_SCHEMA } from './metadata-schema.resource-type'; /** * Class that represents a metadata schema */ +@resourceType(MetadataSchema.type) export class MetadataSchema extends ListableObject implements HALResource { static type = METADATA_SCHEMA; @@ -16,9 +21,11 @@ export class MetadataSchema extends ListableObject implements HALResource { id: number; /** - * The REST link to itself + * The object type */ - self: string; + @excludeFromEquals + @autoserialize + type: ResourceType; /** * A unique prefix that defines this schema diff --git a/src/app/core/metadata/normalized-metadata-field.model.ts b/src/app/core/metadata/normalized-metadata-field.model.ts index 3d8750778d..6c7d8601e9 100644 --- a/src/app/core/metadata/normalized-metadata-field.model.ts +++ b/src/app/core/metadata/normalized-metadata-field.model.ts @@ -1,5 +1,5 @@ import { autoserialize, deserialize, inheritSerialization } from 'cerialize'; -import { mapsTo, relationship } from '../cache/builders/build-decorators'; +import { relationship } from '../cache/builders/build-decorators'; import { MetadataField } from './metadata-field.model'; import { NormalizedObject } from '../cache/models/normalized-object.model'; import { MetadataSchema } from './metadata-schema.model'; @@ -7,7 +7,6 @@ import { MetadataSchema } from './metadata-schema.model'; /** * Class the represents a normalized metadata field */ -@mapsTo(MetadataField) @inheritSerialization(NormalizedObject) export class NormalizedMetadataField extends NormalizedObject { diff --git a/src/app/core/metadata/normalized-metadata-schema.model.ts b/src/app/core/metadata/normalized-metadata-schema.model.ts index 4b534725f4..dc4b012378 100644 --- a/src/app/core/metadata/normalized-metadata-schema.model.ts +++ b/src/app/core/metadata/normalized-metadata-schema.model.ts @@ -1,12 +1,10 @@ import { autoserialize, inheritSerialization } from 'cerialize'; import { NormalizedObject } from '../cache/models/normalized-object.model'; -import { mapsTo } from '../cache/builders/build-decorators'; import { MetadataSchema } from './metadata-schema.model'; /** * Normalized class for a DSpace MetadataSchema */ -@mapsTo(MetadataSchema) @inheritSerialization(NormalizedObject) export class NormalizedMetadataSchema extends NormalizedObject { /** diff --git a/src/app/core/registry/registry-metadatafields-response.model.ts b/src/app/core/registry/registry-metadatafields-response.model.ts index d2ae64c6cd..e9c41b7841 100644 --- a/src/app/core/registry/registry-metadatafields-response.model.ts +++ b/src/app/core/registry/registry-metadatafields-response.model.ts @@ -1,15 +1,26 @@ import { autoserialize, deserialize } from 'cerialize'; -import { relationship } from '../cache/builders/build-decorators'; +import { relationship, resourceType } from '../cache/builders/build-decorators'; import { MetadataField } from '../metadata/metadata-field.model'; import { METADATA_FIELD } from '../metadata/metadata-field.resource-type'; import { HALLink } from '../shared/hal-link.model'; import { PageInfo } from '../shared/page-info.model'; +import { ResourceType } from '../shared/resource-type'; +import { excludeFromEquals } from '../utilities/equals.decorators'; /** * Class that represents a response with a registry's metadata fields */ +@resourceType(RegistryMetadatafieldsResponse.type) export class RegistryMetadatafieldsResponse { static type = METADATA_FIELD; + + /** + * The object type + */ + @excludeFromEquals + @autoserialize + type: ResourceType; + /** * List of metadata fields in the response */ diff --git a/src/app/core/registry/registry.service.ts b/src/app/core/registry/registry.service.ts index 3c6de36492..05495af7eb 100644 --- a/src/app/core/registry/registry.service.ts +++ b/src/app/core/registry/registry.service.ts @@ -48,7 +48,7 @@ import { MetadataRegistrySelectSchemaAction } from '../../+admin/admin-registries/metadata-registry/metadata-registry.actions'; import { distinctUntilChanged, flatMap, map, take, tap } from 'rxjs/operators'; -import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer'; +import { NormalizedObjectSerializer } from '../dspace-rest-v2/normalized-object.serializer'; import { NormalizedMetadataSchema } from '../metadata/normalized-metadata-schema.model'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { NotificationOptions } from '../../shared/notifications/models/notification-options.model'; @@ -400,7 +400,7 @@ export class RegistryService { distinctUntilChanged() ); - const serializedSchema = new DSpaceRESTv2Serializer(getMapsToType(MetadataSchema.type)).serialize(schema); + const serializedSchema = new NormalizedObjectSerializer(getMapsToType(MetadataSchema.type)).serialize(schema); const request$ = endpoint$.pipe( take(1), diff --git a/src/app/core/shared/bitstream-format.model.ts b/src/app/core/shared/bitstream-format.model.ts index 65f28a6427..0ba284fcc5 100644 --- a/src/app/core/shared/bitstream-format.model.ts +++ b/src/app/core/shared/bitstream-format.model.ts @@ -1,53 +1,69 @@ +import { autoserialize, deserialize, deserializeAs } from 'cerialize'; +import { resourceType } from '../cache/builders/build-decorators'; +import { IDToUUIDSerializer } from '../cache/id-to-uuid-serializer'; import { CacheableObject } from '../cache/object-cache.reducer'; +import { excludeFromEquals } from '../utilities/equals.decorators'; import { BitstreamFormatSupportLevel } from './bitstream-format-support-level'; import { BITSTREAM_FORMAT } from './bitstream-format.resource-type'; import { HALLink } from './hal-link.model'; +import { ResourceType } from './resource-type'; /** * Model class for a Bitstream Format */ +@resourceType(BitstreamFormat.type) export class BitstreamFormat implements CacheableObject { static type = BITSTREAM_FORMAT; - bitstreamformat + /** + * The object type + */ + @excludeFromEquals + @autoserialize + type: ResourceType; + /** * Short description of this Bitstream Format */ + @autoserialize shortDescription: string; /** * Description of this Bitstream Format */ + @autoserialize description: string; /** * String representing the MIME type of this Bitstream Format */ + @autoserialize mimetype: string; /** * The level of support the system offers for this Bitstream Format */ + @autoserialize supportLevel: BitstreamFormatSupportLevel; /** * True if the Bitstream Format is used to store system information, rather than the content of items in the system */ + @autoserialize internal: boolean; /** * String representing this Bitstream Format's file extension */ + @autoserialize extensions: string[]; - /** - * The link to the rest endpoint where this Bitstream Format can be found - */ - self: string; - /** * Universally unique identifier for this Bitstream Format + * This UUID is generated client-side and isn't used by the backend. + * It is based on the ID, so it will be the same for each refresh. */ + @deserializeAs(new IDToUUIDSerializer('bitstream-format'), 'id') uuid: string; /** @@ -55,8 +71,13 @@ export class BitstreamFormat implements CacheableObject { * Note that this ID is unique for bitstream formats, * but might not be unique across different object types */ + @autoserialize id: string; + /** + * The HALLinks for this BitstreamFormat + */ + @deserialize _links: { self: HALLink; } diff --git a/src/app/core/shared/bitstream.model.ts b/src/app/core/shared/bitstream.model.ts index e013d97f5e..da8c39cd8b 100644 --- a/src/app/core/shared/bitstream.model.ts +++ b/src/app/core/shared/bitstream.model.ts @@ -1,5 +1,6 @@ +import { autoserialize, deserialize, inheritSerialization } from 'cerialize'; import { Observable } from 'rxjs'; -import { link } from '../cache/builders/build-decorators'; +import { link, resourceType } from '../cache/builders/build-decorators'; import { RemoteData } from '../data/remote-data'; import { BitstreamFormat } from './bitstream-format.model'; import { BITSTREAM_FORMAT } from './bitstream-format.resource-type'; @@ -8,39 +9,52 @@ import { DSpaceObject } from './dspace-object.model'; import { HALLink } from './hal-link.model'; import { HALResource } from './hal-resource.model'; +@resourceType(Bitstream.type) +@inheritSerialization(DSpaceObject) export class Bitstream extends DSpaceObject implements HALResource { static type = BITSTREAM; /** * The size of this bitstream in bytes */ + @autoserialize sizeBytes: number; /** * The description of this Bitstream */ + @autoserialize description: string; /** * The name of the Bundle this Bitstream is part of */ + @autoserialize bundleName: string; /** - * The Thumbnail for this Bitstream + * The HALLinks for this Bitstream */ - thumbnail?: Observable>; - - /** - * The Bitstream Format for this Bitstream - */ - @link(BITSTREAM_FORMAT) - format?: Observable>; - + @deserialize _links: { self: HALLink; bundle: HALLink; format: HALLink; content: HALLink; - } + }; + + /** + * The thumbnail for this Bitstream + * Needs to be resolved first, but isn't available as a HALLink yet + * Use BitstreamDataService.getThumbnailFor(…) for now. + */ + thumbnail?: Observable>; + + /** + * The BitstreamFormat of this Bitstream + * Will be undefined unless the format HALLink has been resolved. + */ + @link(BITSTREAM_FORMAT) + format?: Observable>; + } diff --git a/src/app/core/shared/browse-definition.model.ts b/src/app/core/shared/browse-definition.model.ts index 96c48b4ba5..73badd4729 100644 --- a/src/app/core/shared/browse-definition.model.ts +++ b/src/app/core/shared/browse-definition.model.ts @@ -1,12 +1,23 @@ -import { autoserialize, autoserializeAs } from 'cerialize'; +import { autoserialize, autoserializeAs, deserialize } from 'cerialize'; +import { resourceType } from '../cache/builders/build-decorators'; import { TypedObject } from '../cache/object-cache.reducer'; +import { excludeFromEquals } from '../utilities/equals.decorators'; import { BROWSE_DEFINITION } from './browse-definition.resource-type'; import { HALLink } from './hal-link.model'; +import { ResourceType } from './resource-type'; import { SortOption } from './sort-option.model'; +@resourceType(BrowseDefinition.type) export class BrowseDefinition implements TypedObject { static type = BROWSE_DEFINITION; + /** + * The object type + */ + @excludeFromEquals + @autoserialize + type: ResourceType; + @autoserialize id: string; @@ -26,7 +37,7 @@ export class BrowseDefinition implements TypedObject { return this._links.self.href; } - @autoserialize + @deserialize _links: { self: HALLink; entries: HALLink; diff --git a/src/app/core/shared/browse-entry.model.ts b/src/app/core/shared/browse-entry.model.ts index 6f4861a9ad..06b4f88a86 100644 --- a/src/app/core/shared/browse-entry.model.ts +++ b/src/app/core/shared/browse-entry.model.ts @@ -1,17 +1,28 @@ +import { autoserialize } from 'cerialize'; import { ListableObject } from '../../shared/object-collection/shared/listable-object.model'; +import { resourceType } from '../cache/builders/build-decorators'; import { TypedObject } from '../cache/object-cache.reducer'; import { excludeFromEquals } from '../utilities/equals.decorators'; import { BROWSE_ENTRY } from './browse-entry.resource-type'; import { GenericConstructor } from './generic-constructor'; import { HALLink } from './hal-link.model'; +import { ResourceType } from './resource-type'; /** * Class object representing a browse entry * This class is not normalized because browse entries do not have self links */ +@resourceType(BrowseEntry.type) export class BrowseEntry extends ListableObject implements TypedObject { static type = BROWSE_ENTRY; + /** + * The object type + */ + @excludeFromEquals + @autoserialize + type: ResourceType; + /** * The authority string of this browse entry */ diff --git a/src/app/core/shared/bundle.model.ts b/src/app/core/shared/bundle.model.ts index 141d83dbed..a5d1818a0d 100644 --- a/src/app/core/shared/bundle.model.ts +++ b/src/app/core/shared/bundle.model.ts @@ -1,15 +1,18 @@ +import { deserialize, inheritSerialization } from 'cerialize'; +import { resourceType } from '../cache/builders/build-decorators'; import { BUNDLE } from './bundle.resource-type'; import { DSpaceObject } from './dspace-object.model'; import { HALLink } from './hal-link.model'; +@resourceType(Bundle.type) +@inheritSerialization(DSpaceObject) export class Bundle extends DSpaceObject { static type = BUNDLE; /** - * The bundle's name + * The HALLinks for this Bundle */ - name: string; - + @deserialize _links: { self: HALLink; primaryBitstream: HALLink; diff --git a/src/app/core/shared/collection.model.ts b/src/app/core/shared/collection.model.ts index 0c64c56ad0..d7508c27cb 100644 --- a/src/app/core/shared/collection.model.ts +++ b/src/app/core/shared/collection.model.ts @@ -1,5 +1,6 @@ +import { autoserialize, deserialize, inheritSerialization } from 'cerialize'; import { Observable } from 'rxjs'; -import { link } from '../cache/builders/build-decorators'; +import { link, resourceType } from '../cache/builders/build-decorators'; import { PaginatedList } from '../data/paginated-list'; import { RemoteData } from '../data/remote-data'; import { Bitstream } from './bitstream.model'; @@ -12,14 +13,52 @@ import { LICENSE } from './license.resource-type'; import { ResourcePolicy } from './resource-policy.model'; import { RESOURCE_POLICY } from './resource-policy.resource-type'; +@resourceType(Collection.type) +@inheritSerialization(DSpaceObject) export class Collection extends DSpaceObject { static type = COLLECTION; /** * A string representing the unique handle of this Collection */ + @autoserialize handle: string; + /** + * The HALLinks for this Collection + */ + @deserialize + _links: { + license: HALLink; + harvester: HALLink; + mappedItems: HALLink; + itemtemplate: HALLink; + defaultAccessConditions: HALLink; + logo: HALLink; + self: HALLink; + }; + + /** + * The license for this Collection + * Will be undefined unless the license HALLink has been resolved. + */ + @link(LICENSE) + license?: Observable>; + + /** + * The logo for this Collection + * Will be undefined unless the logo HALLink has been resolved. + */ + @link(BITSTREAM) + logo?: Observable>; + + /** + * The default access conditions for this Collection + * Will be undefined unless the defaultAccessConditions HALLink has been resolved. + */ + @link(RESOURCE_POLICY, true) + defaultAccessConditions?: Observable>>; + /** * The introductory text of this Collection * Corresponds to the metadata field dc.description @@ -59,32 +98,4 @@ export class Collection extends DSpaceObject { get sidebarText(): string { return this.firstMetadataValue('dc.description.tableofcontents'); } - - /** - * The deposit license of this Collection - */ - @link(LICENSE) - license?: Observable>; - - /** - * The Bitstream that represents the logo of this Collection - */ - @link(BITSTREAM) - logo?: Observable>; - - /** - * The default access conditions of this Collection - */ - @link(RESOURCE_POLICY, true) - defaultAccessConditions?: Observable>>; - - _links: { - license: HALLink; - harvester: HALLink; - mappedItems: HALLink; - itemtemplate: HALLink; - defaultAccessConditions: HALLink; - logo: HALLink; - self: HALLink; - } } diff --git a/src/app/core/shared/community.model.ts b/src/app/core/shared/community.model.ts index 58b4f0adb4..b1b91a8207 100644 --- a/src/app/core/shared/community.model.ts +++ b/src/app/core/shared/community.model.ts @@ -1,5 +1,6 @@ +import { autoserialize, deserialize, inheritSerialization } from 'cerialize'; import { Observable } from 'rxjs'; -import { link } from '../cache/builders/build-decorators'; +import { link, resourceType } from '../cache/builders/build-decorators'; import { PaginatedList } from '../data/paginated-list'; import { RemoteData } from '../data/remote-data'; import { Bitstream } from './bitstream.model'; @@ -10,14 +11,49 @@ import { COMMUNITY } from './community.resource-type'; import { DSpaceObject } from './dspace-object.model'; import { HALLink } from './hal-link.model'; +@resourceType(Community.type) +@inheritSerialization(DSpaceObject) export class Community extends DSpaceObject { static type = COMMUNITY; /** * A string representing the unique handle of this Community */ + @autoserialize handle: string; + /** + * The HALLinks for this Community + */ + @deserialize + _links: { + collections: HALLink; + logo: HALLink; + subcommunities: HALLink; + self: HALLink; + }; + + /** + * The logo for this Community + * Will be undefined unless the logo HALLink has been resolved. + */ + @link(BITSTREAM) + logo?: Observable>; + + /** + * The list of Collections that are direct children of this Community + * Will be undefined unless the collections HALLink has been resolved. + */ + @link(COLLECTION, true) + collections?: Observable>>; + + /** + * The list of Communities that are direct children of this Community + * Will be undefined unless the subcommunities HALLink has been resolved. + */ + @link(COMMUNITY, true) + subcommunities?: Observable>>; + /** * The introductory text of this Community * Corresponds to the metadata field dc.description @@ -49,23 +85,4 @@ export class Community extends DSpaceObject { get sidebarText(): string { return this.firstMetadataValue('dc.description.tableofcontents'); } - - /** - * The Bitstream that represents the logo of this Community - */ - @link(BITSTREAM) - logo?: Observable>; - - @link(COLLECTION, true) - collections?: Observable>>; - - @link(COMMUNITY, true) - subcommunities?: Observable>>; - - _links: { - collections: HALLink; - logo: HALLink; - subcommunities: HALLink; - self: HALLink; - } } diff --git a/src/app/core/shared/dspace-object.model.ts b/src/app/core/shared/dspace-object.model.ts index b8706f503c..ff9981079a 100644 --- a/src/app/core/shared/dspace-object.model.ts +++ b/src/app/core/shared/dspace-object.model.ts @@ -1,17 +1,26 @@ +import { autoserialize, autoserializeAs, deserialize, deserializeAs } from 'cerialize'; import { hasNoValue, isUndefined } from '../../shared/empty.util'; import { ListableObject } from '../../shared/object-collection/shared/listable-object.model'; +import { resourceType } from '../cache/builders/build-decorators'; import { CacheableObject } from '../cache/object-cache.reducer'; import { excludeFromEquals } from '../utilities/equals.decorators'; import { DSPACE_OBJECT } from './dspace-object.resource-type'; import { GenericConstructor } from './generic-constructor'; import { HALLink } from './hal-link.model'; -import { MetadataMap, MetadataValue, MetadataValueFilter, MetadatumViewModel } from './metadata.models'; +import { + MetadataMap, + MetadataMapSerializer, + MetadataValue, + MetadataValueFilter, + MetadatumViewModel +} from './metadata.models'; import { Metadata } from './metadata.utils'; import { ResourceType } from './resource-type'; /** * An abstract model class for a DSpaceObject. */ +@resourceType(DSpaceObject.type) export class DSpaceObject extends ListableObject implements CacheableObject { /** * A string representing the kind of DSpaceObject, e.g. community, item, … @@ -19,28 +28,36 @@ export class DSpaceObject extends ListableObject implements CacheableObject { static type = DSPACE_OBJECT; @excludeFromEquals + @deserializeAs('name') private _name: string; - @excludeFromEquals - self: string; - /** * The human-readable identifier of this DSpaceObject */ @excludeFromEquals + @autoserializeAs(String, 'uuid') id: string; /** * The universally unique identifier of this DSpaceObject */ + @autoserializeAs(String) uuid: string; /** * A string representing the kind of DSpaceObject, e.g. community, item, … */ @excludeFromEquals + @autoserialize type: ResourceType; + /** + * A shorthand for this DSpaceObject's self link + */ + get self(): string { + return this._links.self.href; + } + /** * The name for this DSpaceObject */ @@ -59,10 +76,12 @@ export class DSpaceObject extends ListableObject implements CacheableObject { * All metadata of this DSpaceObject */ @excludeFromEquals + @autoserializeAs(MetadataMapSerializer) metadata: MetadataMap; + @deserialize _links: { - self: HALLink, + self: HALLink; }; /** diff --git a/src/app/core/shared/external-source-entry.model.ts b/src/app/core/shared/external-source-entry.model.ts index 65b4d34fbd..1e37904573 100644 --- a/src/app/core/shared/external-source-entry.model.ts +++ b/src/app/core/shared/external-source-entry.model.ts @@ -1,45 +1,58 @@ +import { autoserialize, autoserializeAs, deserialize } from 'cerialize'; import { ListableObject } from '../../shared/object-collection/shared/listable-object.model'; -import { RESOURCE_TYPE } from './external-source-entry.resource-type'; +import { resourceType } from '../cache/builders/build-decorators'; +import { excludeFromEquals } from '../utilities/equals.decorators'; +import { EXTERNAL_SOURCE_ENTRY } from './external-source-entry.resource-type'; import { GenericConstructor } from './generic-constructor'; import { HALLink } from './hal-link.model'; -import { MetadataMap } from './metadata.models'; +import { MetadataMap, MetadataMapSerializer } from './metadata.models'; +import { ResourceType } from './resource-type'; /** * Model class for a single entry from an external source */ +@resourceType(ExternalSourceEntry.type) export class ExternalSourceEntry extends ListableObject { - static type = RESOURCE_TYPE; + static type = EXTERNAL_SOURCE_ENTRY; /** * Unique identifier */ + @autoserialize id: string; + /** + * The object type + */ + @excludeFromEquals + @autoserialize + type: ResourceType; + /** * The value to display */ + @autoserialize display: string; /** * The value to store the entry with */ + @autoserialize value: string; /** * The ID of the external source this entry originates from */ + @autoserialize externalSource: string; /** * Metadata of the entry */ + @autoserializeAs(MetadataMapSerializer) metadata: MetadataMap; - /** - * The link to the rest endpoint where this External Source Entry can be found - */ - self: string; - + @deserialize _links: { self: HALLink; }; diff --git a/src/app/core/shared/external-source-entry.resource-type.ts b/src/app/core/shared/external-source-entry.resource-type.ts index 4a3e92f227..0fc25a5e3f 100644 --- a/src/app/core/shared/external-source-entry.resource-type.ts +++ b/src/app/core/shared/external-source-entry.resource-type.ts @@ -6,4 +6,4 @@ import { ResourceType } from './resource-type'; * Needs to be in a separate file to prevent circular * dependencies in webpack. */ -export const RESOURCE_TYPE = new ResourceType('externalSourceEntry'); +export const EXTERNAL_SOURCE_ENTRY = new ResourceType('externalSourceEntry'); diff --git a/src/app/core/shared/external-source.model.ts b/src/app/core/shared/external-source.model.ts index 2b375941b4..afb6e48818 100644 --- a/src/app/core/shared/external-source.model.ts +++ b/src/app/core/shared/external-source.model.ts @@ -1,33 +1,44 @@ +import { autoserialize, deserialize, inheritSerialization } from 'cerialize'; +import { resourceType } from '../cache/builders/build-decorators'; import { CacheableObject } from '../cache/object-cache.reducer'; +import { excludeFromEquals } from '../utilities/equals.decorators'; import { EXTERNAL_SOURCE } from './external-source.resource-type'; import { HALLink } from './hal-link.model'; +import { ResourceType } from './resource-type'; /** * Model class for an external source */ +@resourceType(ExternalSource.type) export class ExternalSource extends CacheableObject { static type = EXTERNAL_SOURCE; + /** + * The object type + */ + @excludeFromEquals + @autoserialize + type: ResourceType; + /** * Unique identifier */ + @autoserialize id: string; /** * The name of this external source */ + @autoserialize name: string; /** * Is the source hierarchical? */ + @autoserialize hierarchical: boolean; - /** - * The link to the rest endpoint where this External Source can be found - */ - self: string; - + @deserialize _links: { self: HALLink; entries: HALLink; diff --git a/src/app/core/shared/hal-resource.model.ts b/src/app/core/shared/hal-resource.model.ts index 40454b859c..d42484febb 100644 --- a/src/app/core/shared/hal-resource.model.ts +++ b/src/app/core/shared/hal-resource.model.ts @@ -1,11 +1,6 @@ import { HALLink } from './hal-link.model'; export class HALResource { - - get self(): string { - return this._links.self.href; - } - _links: { self: HALLink [k: string]: HALLink; diff --git a/src/app/core/shared/item-relationships/item-type.model.ts b/src/app/core/shared/item-relationships/item-type.model.ts index 8fa71d52af..e379544c49 100644 --- a/src/app/core/shared/item-relationships/item-type.model.ts +++ b/src/app/core/shared/item-relationships/item-type.model.ts @@ -1,30 +1,44 @@ +import { autoserialize, deserialize, deserializeAs } from 'cerialize'; +import { resourceType } from '../../cache/builders/build-decorators'; +import { IDToUUIDSerializer } from '../../cache/id-to-uuid-serializer'; import { CacheableObject } from '../../cache/object-cache.reducer'; +import { excludeFromEquals } from '../../utilities/equals.decorators'; import { HALLink } from '../hal-link.model'; +import { ResourceType } from '../resource-type'; import { ITEM_TYPE } from './item-type.resource-type'; /** * Describes a type of Item */ +@resourceType(ItemType.type) export class ItemType implements CacheableObject { static type = ITEM_TYPE; + /** + * The object type + */ + @excludeFromEquals + @autoserialize + type: ResourceType; + /** * The identifier of this ItemType */ + @autoserialize id: string; + @autoserialize label: string; - /** - * The link to the rest endpoint where this object can be found - */ - self: string; - /** * The universally unique identifier of this ItemType + * This UUID is generated client-side and isn't used by the backend. + * It is based on the ID, so it will be the same for each refresh. */ + @deserializeAs(new IDToUUIDSerializer(ItemType.type.value), 'id') uuid: string; + @deserialize _links: { self: HALLink, }; diff --git a/src/app/core/shared/item-relationships/relationship-type.model.ts b/src/app/core/shared/item-relationships/relationship-type.model.ts index 86c0983f14..087b984c41 100644 --- a/src/app/core/shared/item-relationships/relationship-type.model.ts +++ b/src/app/core/shared/item-relationships/relationship-type.model.ts @@ -1,8 +1,12 @@ +import { autoserialize, deserialize, deserializeAs } from 'cerialize'; import { Observable } from 'rxjs'; -import { link } from '../../cache/builders/build-decorators'; +import { link, resourceType } from '../../cache/builders/build-decorators'; +import { IDToUUIDSerializer } from '../../cache/id-to-uuid-serializer'; import { CacheableObject } from '../../cache/object-cache.reducer'; import { RemoteData } from '../../data/remote-data'; +import { excludeFromEquals } from '../../utilities/equals.decorators'; import { HALLink } from '../hal-link.model'; +import { ResourceType } from '../resource-type'; import { ItemType } from './item-type.model'; import { ITEM_TYPE } from './item-type.resource-type'; import { RELATIONSHIP_TYPE } from './relationship-type.resource-type'; @@ -10,74 +14,94 @@ import { RELATIONSHIP_TYPE } from './relationship-type.resource-type'; /** * Describes a type of Relationship between multiple possible Items */ +@resourceType(RelationshipType.type) export class RelationshipType implements CacheableObject { static type = RELATIONSHIP_TYPE; /** - * The link to the rest endpoint where this object can be found + * The object type */ - self: string; + @excludeFromEquals + @autoserialize + type: ResourceType; /** * The label that describes this RelationshipType */ + @autoserialize label: string; /** * The identifier of this RelationshipType */ + @autoserialize id: string; /** * The universally unique identifier of this RelationshipType + * This UUID is generated client-side and isn't used by the backend. + * It is based on the ID, so it will be the same for each refresh. */ + @deserializeAs(new IDToUUIDSerializer(RelationshipType.type.value), 'id') uuid: string; /** * The label that describes the Relation to the left of this RelationshipType */ + @autoserialize leftwardType: string; /** * The maximum amount of Relationships allowed to the left of this RelationshipType */ + @autoserialize leftMaxCardinality: number; /** * The minimum amount of Relationships allowed to the left of this RelationshipType */ + @autoserialize leftMinCardinality: number; /** * The label that describes the Relation to the right of this RelationshipType */ + @autoserialize rightwardType: string; /** * The maximum amount of Relationships allowed to the right of this RelationshipType */ + @autoserialize rightMaxCardinality: number; /** * The minimum amount of Relationships allowed to the right of this RelationshipType */ + @autoserialize rightMinCardinality: number; /** - * The type of Item found to the left of this RelationshipType + * The HALLinks for this RelationshipType + */ + @deserialize + _links: { + self: HALLink; + leftType: HALLink; + rightType: HALLink; + }; + + /** + * The type of Item found on the left side of this RelationshipType + * Will be undefined unless the leftType HALLink has been resolved. */ @link(ITEM_TYPE) leftType?: Observable>; /** - * The type of Item found to the right of this RelationshipType + * The type of Item found on the right side of this RelationshipType + * Will be undefined unless the rightType HALLink has been resolved. */ @link(ITEM_TYPE) rightType?: Observable>; - - _links: { - self: HALLink, - leftType: HALLink, - rightType: HALLink, - } } diff --git a/src/app/core/shared/item-relationships/relationship.model.ts b/src/app/core/shared/item-relationships/relationship.model.ts index 7ebb4737e5..5d9251c0a4 100644 --- a/src/app/core/shared/item-relationships/relationship.model.ts +++ b/src/app/core/shared/item-relationships/relationship.model.ts @@ -1,10 +1,14 @@ +import { autoserialize, deserialize, serialize, deserializeAs } from 'cerialize'; import { Observable } from 'rxjs'; -import { link } from '../../cache/builders/build-decorators'; +import { link, resourceType } from '../../cache/builders/build-decorators'; +import { IDToUUIDSerializer } from '../../cache/id-to-uuid-serializer'; import { CacheableObject } from '../../cache/object-cache.reducer'; import { RemoteData } from '../../data/remote-data'; +import { excludeFromEquals } from '../../utilities/equals.decorators'; import { HALLink } from '../hal-link.model'; import { Item } from '../item.model'; import { ITEM } from '../item.resource-type'; +import { ResourceType } from '../resource-type'; import { RelationshipType } from './relationship-type.model'; import { RELATIONSHIP_TYPE } from './relationship-type.resource-type'; import { RELATIONSHIP } from './relationship.resource-type'; @@ -12,68 +16,85 @@ import { RELATIONSHIP } from './relationship.resource-type'; /** * Describes a Relationship between two Items */ +@resourceType(Relationship.type) export class Relationship implements CacheableObject { static type = RELATIONSHIP; /** - * The link to the rest endpoint where this object can be found + * The object type */ - self: string; + @excludeFromEquals + @autoserialize + type: ResourceType; /** * The universally unique identifier of this Relationship + * This UUID is generated client-side and isn't used by the backend. + * It is based on the ID, so it will be the same for each refresh. */ + @deserializeAs(new IDToUUIDSerializer(Relationship.type.value), 'id') uuid: string; /** * The identifier of this Relationship */ + @autoserialize id: string; /** * The place of the Item to the left side of this Relationship */ + @autoserialize leftPlace: number; /** * The place of the Item to the right side of this Relationship */ + @autoserialize rightPlace: number; /** * The name variant of the Item to the left side of this Relationship */ + @autoserialize leftwardValue: string; /** * The name variant of the Item to the right side of this Relationship */ + @autoserialize rightwardValue: string; /** - * The item to the left of this relationship + * The HALLinks for this Relationship */ + @deserialize + _links: { + self: HALLink; + leftItem: HALLink; + rightItem: HALLink; + relationshipType: HALLink; + }; + /** + * The item on the left side of this relationship + * Will be undefined unless the leftItem HALLink has been resolved. + */ @link(ITEM) leftItem?: Observable>; /** - * The item to the right of this relationship + * The item on the right side of this relationship + * Will be undefined unless the rightItem HALLink has been resolved. */ @link(ITEM) rightItem?: Observable>; /** - * The type of Relationship + * The RelationshipType for this Relationship + * Will be undefined unless the relationshipType HALLink has been resolved. */ @link(RELATIONSHIP_TYPE) relationshipType?: Observable>; - _links: { - self: HALLink, - leftItem: HALLink, - rightItem: HALLink, - relationshipType: HALLink, - } - } diff --git a/src/app/core/shared/item.model.ts b/src/app/core/shared/item.model.ts index 9a3fe975e6..5221176ad5 100644 --- a/src/app/core/shared/item.model.ts +++ b/src/app/core/shared/item.model.ts @@ -1,8 +1,9 @@ +import { autoserialize, autoserializeAs, deserialize, inheritSerialization } from 'cerialize'; import { Observable } from 'rxjs/internal/Observable'; import { isEmpty } from '../../shared/empty.util'; import { DEFAULT_ENTITY_TYPE } from '../../shared/metadata-representation/metadata-representation.decorator'; import { ListableObject } from '../../shared/object-collection/shared/listable-object.model'; -import { link } from '../cache/builders/build-decorators'; +import { link, resourceType } from '../cache/builders/build-decorators'; import { PaginatedList } from '../data/paginated-list'; import { RemoteData } from '../data/remote-data'; import { Bundle } from './bundle.model'; @@ -20,46 +21,45 @@ import { ITEM } from './item.resource-type'; /** * Class representing a DSpace Item */ +@resourceType(Item.type) +@inheritSerialization(DSpaceObject) export class Item extends DSpaceObject { static type = ITEM; /** * A string representing the unique handle of this Item */ + @autoserialize handle: string; /** * The Date of the last modification of this Item */ + @deserialize lastModified: Date; /** * A boolean representing if this Item is currently archived or not */ + @autoserializeAs(Boolean, 'inArchive') isArchived: boolean; /** * A boolean representing if this Item is currently discoverable or not */ + @autoserializeAs(Boolean, 'discoverable') isDiscoverable: boolean; /** * A boolean representing if this Item is currently withdrawn or not */ + @autoserializeAs(Boolean, 'withdrawn') isWithdrawn: boolean; /** - * The Collection that owns this Item + * The HALLinks for this Item */ - @link(COLLECTION) - owningCollection: Observable>; - - @link(BUNDLE, true) - bundles: Observable>>; - - @link(RELATIONSHIP) - relationships?: Observable>>; - + @deserialize _links: { mappedCollections: HALLink; relationships: HALLink; @@ -69,6 +69,27 @@ export class Item extends DSpaceObject { self: HALLink; }; + /** + * The owning Collection for this Item + * Will be undefined unless the owningCollection HALLink has been resolved. + */ + @link(COLLECTION) + owningCollection?: Observable>; + + /** + * The list of Bundles inside this Item + * Will be undefined unless the bundles HALLink has been resolved. + */ + @link(BUNDLE, true) + bundles?: Observable>>; + + /** + * The list of Relationships this Item has with others + * Will be undefined unless the relationships HALLink has been resolved. + */ + @link(RELATIONSHIP) + relationships?: Observable>>; + /** * Method that returns as which type of object this object should be rendered */ diff --git a/src/app/core/shared/license.model.ts b/src/app/core/shared/license.model.ts index d498076471..45a5f9f314 100644 --- a/src/app/core/shared/license.model.ts +++ b/src/app/core/shared/license.model.ts @@ -1,16 +1,22 @@ +import { autoserialize, inheritSerialization } from 'cerialize'; +import { resourceType } from '../cache/builders/build-decorators'; import { DSpaceObject } from './dspace-object.model'; import { LICENSE } from './license.resource-type'; +@resourceType(License.type) +@inheritSerialization(DSpaceObject) export class License extends DSpaceObject { static type = LICENSE; /** * Is the license custom? */ + @autoserialize custom: boolean; /** * The text of the license */ + @autoserialize text: string; } diff --git a/src/app/core/shared/normalized-browse-entry.model.ts b/src/app/core/shared/normalized-browse-entry.model.ts index 949758cb67..bad0a0260e 100644 --- a/src/app/core/shared/normalized-browse-entry.model.ts +++ b/src/app/core/shared/normalized-browse-entry.model.ts @@ -1,13 +1,10 @@ import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize'; import { BrowseEntry } from './browse-entry.model'; import { NormalizedObject } from '../cache/models/normalized-object.model'; -import { mapsTo } from '../cache/builders/build-decorators'; - /** * Class object representing a browse entry * This class is not normalized because browse entries do not have self links */ -@mapsTo(BrowseEntry) @inheritSerialization(NormalizedObject) export class NormalizedBrowseEntry extends NormalizedObject { /** diff --git a/src/app/core/shared/resource-policy.model.ts b/src/app/core/shared/resource-policy.model.ts index e0f919035a..4ee8ad0e43 100644 --- a/src/app/core/shared/resource-policy.model.ts +++ b/src/app/core/shared/resource-policy.model.ts @@ -1,39 +1,54 @@ +import { autoserialize, deserialize, deserializeAs } from 'cerialize'; +import { resourceType } from '../cache/builders/build-decorators'; +import { IDToUUIDSerializer } from '../cache/id-to-uuid-serializer'; import { ActionType } from '../cache/models/action-type.model'; import { CacheableObject } from '../cache/object-cache.reducer'; +import { excludeFromEquals } from '../utilities/equals.decorators'; import { HALLink } from './hal-link.model'; import { RESOURCE_POLICY } from './resource-policy.resource-type'; +import { ResourceType } from './resource-type'; /** * Model class for a Resource Policy */ +@resourceType(ResourcePolicy.type) export class ResourcePolicy implements CacheableObject { static type = RESOURCE_POLICY; + /** + * The object type + */ + @excludeFromEquals + @autoserialize + type: ResourceType; + /** * The action that is allowed by this Resource Policy */ + @autoserialize action: ActionType; /** * The name for this Resource Policy */ + @autoserialize name: string; /** * The uuid of the Group this Resource Policy applies to */ + @autoserialize groupUUID: string; - /** - * The link to the rest endpoint where this Resource Policy can be found - */ - self: string; - /** * The universally unique identifier for this Resource Policy + * This UUID is generated client-side and isn't used by the backend. + * It is based on the ID, so it will be the same for each refresh. */ + @deserializeAs(new IDToUUIDSerializer('resource-policy'), 'id') uuid: string; + @deserialize _links: { self: HALLink, } diff --git a/src/app/core/shared/search/search.service.ts b/src/app/core/shared/search/search.service.ts index 01f59c8ac2..125317298c 100644 --- a/src/app/core/shared/search/search.service.ts +++ b/src/app/core/shared/search/search.service.ts @@ -15,7 +15,6 @@ import { GenericConstructor } from '../generic-constructor'; import { HALEndpointService } from '../hal-endpoint.service'; import { URLCombiner } from '../../url-combiner/url-combiner'; import { hasValue, isEmpty, isNotEmpty, isNotUndefined } from '../../../shared/empty.util'; -import { NormalizedSearchResult } from '../../../shared/search/normalized-search-result.model'; import { SearchOptions } from '../../../shared/search/search-options.model'; import { SearchResult } from '../../../shared/search/search-result.model'; import { FacetValue } from '../../../shared/search/facet-value.model'; @@ -170,8 +169,8 @@ export class SearchService implements OnDestroy { const dsoObs: Observable> = sqrObs.pipe( map((sqr: SearchQueryResponse) => { return sqr.objects - .filter((nsr: NormalizedSearchResult) => isNotUndefined(nsr.indexableObject)) - .map((nsr: NormalizedSearchResult) => new GetRequest(this.requestService.generateRequestId(), nsr.indexableObject)) + .filter((sr: SearchResult) => isNotUndefined(sr._links.indexableObject)) + .map((sr: SearchResult) => new GetRequest(this.requestService.generateRequestId(), sr._links.indexableObject.href)) }), // Send a request for each item to ensure fresh cache tap((reqs: RestRequest[]) => reqs.forEach((req: RestRequest) => this.requestService.configure(req))), @@ -182,7 +181,7 @@ export class SearchService implements OnDestroy { // Create search results again with the correct dso objects linked to each result const tDomainListObs = observableCombineLatest(sqrObs, dsoObs).pipe( map(([sqr, dsos]) => { - return sqr.objects.map((object: NormalizedSearchResult, index: number) => { + return sqr.objects.map((object: SearchResult, index: number) => { let co = DSpaceObject; if (dsos.payload[index]) { const constructor: GenericConstructor = dsos.payload[index].constructor as GenericConstructor; diff --git a/src/app/core/shared/site.model.ts b/src/app/core/shared/site.model.ts index 3c7c832709..204c33f17a 100644 --- a/src/app/core/shared/site.model.ts +++ b/src/app/core/shared/site.model.ts @@ -1,9 +1,13 @@ +import { inheritSerialization } from 'cerialize'; +import { resourceType } from '../cache/builders/build-decorators'; import { DSpaceObject } from './dspace-object.model'; import { SITE } from './site.resource-type'; /** * Model class for the Site object */ +@resourceType(Site.type) +@inheritSerialization(DSpaceObject) export class Site extends DSpaceObject { ​ static type = SITE; diff --git a/src/app/core/submission/models/normalized-workflowitem.model.ts b/src/app/core/submission/models/normalized-workflowitem.model.ts index e96024b4ae..54ee199f34 100644 --- a/src/app/core/submission/models/normalized-workflowitem.model.ts +++ b/src/app/core/submission/models/normalized-workflowitem.model.ts @@ -1,6 +1,6 @@ import { autoserialize, inheritSerialization } from 'cerialize'; -import { mapsTo, relationship } from '../../cache/builders/build-decorators'; +import { relationship } from '../../cache/builders/build-decorators'; import { WorkflowItem } from './workflowitem.model'; import { NormalizedSubmissionObject } from './normalized-submission-object.model'; import { Collection } from '../../shared/collection.model'; @@ -11,7 +11,6 @@ import { EPerson } from '../../eperson/models/eperson.model'; /** * An model class for a NormalizedWorkflowItem. */ -@mapsTo(WorkflowItem) @inheritSerialization(NormalizedSubmissionObject) export class NormalizedWorkflowItem extends NormalizedSubmissionObject { diff --git a/src/app/core/submission/models/normalized-workspaceitem.model.ts b/src/app/core/submission/models/normalized-workspaceitem.model.ts index 4275420191..47b7d59f41 100644 --- a/src/app/core/submission/models/normalized-workspaceitem.model.ts +++ b/src/app/core/submission/models/normalized-workspaceitem.model.ts @@ -2,7 +2,7 @@ import { autoserialize, inheritSerialization } from 'cerialize'; import { WorkspaceItem } from './workspaceitem.model'; import { NormalizedSubmissionObject } from './normalized-submission-object.model'; -import { mapsTo, relationship } from '../../cache/builders/build-decorators'; +import { relationship } from '../../cache/builders/build-decorators'; import { NormalizedDSpaceObject } from '../../cache/models/normalized-dspace-object.model'; import { Item } from '../../shared/item.model'; import { Collection } from '../../shared/collection.model'; @@ -12,7 +12,6 @@ import { EPerson } from '../../eperson/models/eperson.model'; /** * An model class for a NormalizedWorkspaceItem. */ -@mapsTo(WorkspaceItem) @inheritSerialization(NormalizedDSpaceObject) @inheritSerialization(NormalizedSubmissionObject) export class NormalizedWorkspaceItem extends NormalizedSubmissionObject { diff --git a/src/app/core/submission/models/submission-object.model.ts b/src/app/core/submission/models/submission-object.model.ts index de712ff7ea..237a838e3c 100644 --- a/src/app/core/submission/models/submission-object.model.ts +++ b/src/app/core/submission/models/submission-object.model.ts @@ -1,3 +1,4 @@ +import { autoserialize, autoserializeAs, deserialize, inheritSerialization } from 'cerialize'; import { Observable } from 'rxjs'; import { link } from '../../cache/builders/build-decorators'; @@ -12,6 +13,7 @@ import { DSpaceObject } from '../../shared/dspace-object.model'; import { HALLink } from '../../shared/hal-link.model'; import { Item } from '../../shared/item.model'; import { ITEM } from '../../shared/item.resource-type'; +import { excludeFromEquals } from '../../utilities/equals.decorators'; import { WorkspaceitemSectionsObject } from './workspaceitem-sections.model'; export interface SubmissionObjectError { @@ -22,63 +24,68 @@ export interface SubmissionObjectError { /** * An abstract model class for a SubmissionObject. */ +@inheritSerialization(DSpaceObject) export abstract class SubmissionObject extends DSpaceObject implements CacheableObject { - /** - * The workspaceitem/workflowitem identifier - */ + @excludeFromEquals + @autoserialize id: string; /** - * The workspaceitem/workflowitem identifier - */ - uuid: string; - - /** - * The workspaceitem/workflowitem last modified date + * The SubmissionObject last modified date */ + @autoserialize lastModified: Date; /** * The collection this submission applies to + * Will be undefined unless the collection HALLink has been resolved. */ @link(COLLECTION) collection?: Observable> | Collection; /** - * The submission item - */ - @link(ITEM) - item?: Observable> | Item; - - /** - * The workspaceitem/workflowitem last sections data + * The SubmissionObject's last section's data */ + @autoserialize sections: WorkspaceitemSectionsObject; + /** + * The SubmissionObject's last section's errors + */ + @autoserialize + errors: SubmissionObjectError[]; + + /** + * The HALLinks for this SubmissionObject + */ + @deserialize + _links: { + self: HALLink; + collection: HALLink; + item: HALLink; + submissionDefinition: HALLink; + submitter: HALLink; + }; + + /** + * The submission item + * Will be undefined unless the item HALLink has been resolved. + */ + @link(ITEM) + item?: Observable> | Item; /** * The configuration object that define this submission + * Will be undefined unless the submissionDefinition HALLink has been resolved. */ @link(SubmissionDefinitionsModel.type) submissionDefinition?: Observable> | SubmissionDefinitionsModel; /** - * The workspaceitem submitter + * The submitter for this SubmissionObject + * Will be undefined unless the submitter HALLink has been resolved. */ @link(EPERSON) submitter?: Observable> | EPerson; - /** - * The workspaceitem/workflowitem last sections errors - */ - errors: SubmissionObjectError[]; - - _links: { - self: HALLink, - collection: HALLink, - item: HALLink, - submissionDefinition: HALLink, - submitter: HALLink, - } - } diff --git a/src/app/core/submission/models/workflowitem.model.ts b/src/app/core/submission/models/workflowitem.model.ts index 4cfc4d7fa1..5d53300b81 100644 --- a/src/app/core/submission/models/workflowitem.model.ts +++ b/src/app/core/submission/models/workflowitem.model.ts @@ -1,9 +1,23 @@ -import { WorkspaceItem } from './workspaceitem.model'; -import { ResourceType } from '../../shared/resource-type'; +import { deserializeAs, inheritSerialization } from 'cerialize'; +import { inheritLinkAnnotations, resourceType } from '../../cache/builders/build-decorators'; +import { IDToUUIDSerializer } from '../../cache/id-to-uuid-serializer'; +import { WORKFLOWITEM } from '../../eperson/models/workflowitem.resource-type'; +import { SubmissionObject } from './submission-object.model'; /** * A model class for a WorkflowItem. */ -export class WorkflowItem extends WorkspaceItem { - static type = new ResourceType('workflowitem'); +@resourceType(WorkflowItem.type) +@inheritSerialization(SubmissionObject) +@inheritLinkAnnotations(SubmissionObject) +export class WorkflowItem extends SubmissionObject { + static type = WORKFLOWITEM; + + /** + * The universally unique identifier of this WorkflowItem + * This UUID is generated client-side and isn't used by the backend. + * It is based on the ID, so it will be the same for each refresh. + */ + @deserializeAs(new IDToUUIDSerializer(WorkflowItem.type.value), 'id') + uuid: string; } diff --git a/src/app/core/submission/models/workspaceitem.model.ts b/src/app/core/submission/models/workspaceitem.model.ts index c4bb5b7520..f25115ab52 100644 --- a/src/app/core/submission/models/workspaceitem.model.ts +++ b/src/app/core/submission/models/workspaceitem.model.ts @@ -1,10 +1,24 @@ +import { deserializeAs, inheritSerialization } from 'cerialize'; +import { inheritLinkAnnotations, resourceType } from '../../cache/builders/build-decorators'; +import { IDToUUIDSerializer } from '../../cache/id-to-uuid-serializer'; +import { DSpaceObject } from '../../shared/dspace-object.model'; import { SubmissionObject } from './submission-object.model'; import { ResourceType } from '../../shared/resource-type'; /** * A model class for a WorkspaceItem. */ +@resourceType(WorkspaceItem.type) +@inheritSerialization(SubmissionObject) +@inheritLinkAnnotations(SubmissionObject) export class WorkspaceItem extends SubmissionObject { static type = new ResourceType('workspaceitem'); + /** + * The universally unique identifier of this WorkspaceItem + * This UUID is generated client-side and isn't used by the backend. + * It is based on the ID, so it will be the same for each refresh. + */ + @deserializeAs(new IDToUUIDSerializer(WorkspaceItem.type.value), 'id') + uuid: string; } diff --git a/src/app/core/submission/submission-object-data.service.ts b/src/app/core/submission/submission-object-data.service.ts index 15ede18cb8..0348559eed 100644 --- a/src/app/core/submission/submission-object-data.service.ts +++ b/src/app/core/submission/submission-object-data.service.ts @@ -1,5 +1,6 @@ import { Injectable } from '@angular/core'; import { of as observableOf, Observable } from 'rxjs'; +import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { SubmissionService } from '../../submission/submission.service'; import { RemoteData } from '../data/remote-data'; import { RemoteDataError } from '../data/remote-data-error'; @@ -27,13 +28,14 @@ export class SubmissionObjectDataService { * Retrieve a submission object based on its ID. * * @param id The identifier of a submission object + * @param linksToFollow List of {@link FollowLinkConfig} that indicate which HALLinks should be automatically resolved */ - findById(id: string): Observable> { + findById(id: string, ...linksToFollow: Array>): Observable> { switch (this.submissionService.getSubmissionScope()) { case SubmissionScopeType.WorkspaceItem: - return this.workspaceitemDataService.findById(id); + return this.workspaceitemDataService.findById(id,...linksToFollow); case SubmissionScopeType.WorkflowItem: - return this.workflowItemDataService.findById(id); + return this.workflowItemDataService.findById(id,...linksToFollow); default: const error = new RemoteDataError( undefined, diff --git a/src/app/core/submission/submission-response-parsing.service.ts b/src/app/core/submission/submission-response-parsing.service.ts index dfb0d2af5f..5afc3b0138 100644 --- a/src/app/core/submission/submission-response-parsing.service.ts +++ b/src/app/core/submission/submission-response-parsing.service.ts @@ -12,6 +12,9 @@ import { BaseResponseParsingService } from '../data/base-response-parsing.servic import { GLOBAL_CONFIG } from '../../../config'; import { GlobalConfig } from '../../../config/global-config.interface'; import { ObjectCacheService } from '../cache/object-cache.service'; +import { NormalizedObjectSerializer } from '../dspace-rest-v2/normalized-object.serializer'; +import { Serializer } from '../serializer'; +import { GenericConstructor } from '../shared/generic-constructor'; import { NormalizedWorkspaceItem } from './models/normalized-workspaceitem.model'; import { NormalizedWorkflowItem } from './models/normalized-workflowitem.model'; import { FormFieldMetadataValueObject } from '../../shared/form/builder/models/form-field-metadata-value.model'; @@ -78,6 +81,8 @@ export class SubmissionResponseParsingService extends BaseResponseParsingService protected toCache = false; protected shouldDirectlyAttachEmbeds = true; + protected serializerConstructor: GenericConstructor> = NormalizedObjectSerializer; + constructor(@Inject(GLOBAL_CONFIG) protected EnvConfig: GlobalConfig, protected objectCache: ObjectCacheService, protected dsoParser: DSOResponseParsingService diff --git a/src/app/core/tasks/models/claimed-task-object.model.ts b/src/app/core/tasks/models/claimed-task-object.model.ts index dbeaef7900..ce87674c86 100644 --- a/src/app/core/tasks/models/claimed-task-object.model.ts +++ b/src/app/core/tasks/models/claimed-task-object.model.ts @@ -1,9 +1,11 @@ +import { resourceType } from '../../cache/builders/build-decorators'; import { CLAIMED_TASK } from './claimed-task-object.resource-type'; import { TaskObject } from './task-object.model'; /** * A model class for a ClaimedTask. */ +@resourceType(ClaimedTask.type) export class ClaimedTask extends TaskObject { static type = CLAIMED_TASK; } diff --git a/src/app/core/tasks/models/normalized-claimed-task-object.model.ts b/src/app/core/tasks/models/normalized-claimed-task-object.model.ts index d43a277f02..fa7f817e1d 100644 --- a/src/app/core/tasks/models/normalized-claimed-task-object.model.ts +++ b/src/app/core/tasks/models/normalized-claimed-task-object.model.ts @@ -1,5 +1,5 @@ import { NormalizedTaskObject } from './normalized-task-object.model'; -import { mapsTo, relationship } from '../../cache/builders/build-decorators'; +import { relationship } from '../../cache/builders/build-decorators'; import { autoserialize, inheritSerialization } from 'cerialize'; import { ClaimedTask } from './claimed-task-object.model'; import { EPerson } from '../../eperson/models/eperson.model'; @@ -9,7 +9,6 @@ import { WorkflowItem } from '../../submission/models/workflowitem.model'; /** * A normalized model class for a ClaimedTask. */ -@mapsTo(ClaimedTask) @inheritSerialization(NormalizedTaskObject) export class NormalizedClaimedTask extends NormalizedTaskObject { /** diff --git a/src/app/core/tasks/models/normalized-pool-task-object.model.ts b/src/app/core/tasks/models/normalized-pool-task-object.model.ts index bfc782f182..39c7877807 100644 --- a/src/app/core/tasks/models/normalized-pool-task-object.model.ts +++ b/src/app/core/tasks/models/normalized-pool-task-object.model.ts @@ -1,14 +1,13 @@ import { NormalizedTaskObject } from './normalized-task-object.model'; import { PoolTask } from './pool-task-object.model'; import { autoserialize, inheritSerialization } from 'cerialize'; -import { mapsTo, relationship } from '../../cache/builders/build-decorators'; +import { relationship } from '../../cache/builders/build-decorators'; import { Group } from '../../eperson/models/group.model'; import { WorkflowItem } from '../../submission/models/workflowitem.model'; /** * A normalized model class for a PoolTask. */ -@mapsTo(PoolTask) @inheritSerialization(NormalizedTaskObject) export class NormalizedPoolTask extends NormalizedTaskObject { /** diff --git a/src/app/core/tasks/models/normalized-task-object.model.ts b/src/app/core/tasks/models/normalized-task-object.model.ts index 2c96b95393..3fe999d91c 100644 --- a/src/app/core/tasks/models/normalized-task-object.model.ts +++ b/src/app/core/tasks/models/normalized-task-object.model.ts @@ -1,5 +1,5 @@ import { autoserialize, inheritSerialization } from 'cerialize'; -import { mapsTo, relationship } from '../../cache/builders/build-decorators'; +import { relationship } from '../../cache/builders/build-decorators'; import { NormalizedDSpaceObject } from '../../cache/models/normalized-dspace-object.model'; import { TaskObject } from './task-object.model'; import { DSpaceObject } from '../../shared/dspace-object.model'; @@ -10,7 +10,6 @@ import { WorkflowItem } from '../../submission/models/workflowitem.model'; /** * An abstract normalized model class for a TaskObject. */ -@mapsTo(TaskObject) @inheritSerialization(NormalizedDSpaceObject) export class NormalizedTaskObject extends NormalizedDSpaceObject { diff --git a/src/app/core/tasks/models/pool-task-object.model.ts b/src/app/core/tasks/models/pool-task-object.model.ts index 18c8031575..2546ba0c6f 100644 --- a/src/app/core/tasks/models/pool-task-object.model.ts +++ b/src/app/core/tasks/models/pool-task-object.model.ts @@ -1,9 +1,11 @@ +import { resourceType } from '../../cache/builders/build-decorators'; import { POOL_TASK } from './pool-task-object.resource-type'; import { TaskObject } from './task-object.model'; /** * A model class for a PoolTask. */ +@resourceType(PoolTask.type) export class PoolTask extends TaskObject { static type = POOL_TASK; } diff --git a/src/app/core/tasks/models/task-object.model.ts b/src/app/core/tasks/models/task-object.model.ts index 10f250beca..c851bc092f 100644 --- a/src/app/core/tasks/models/task-object.model.ts +++ b/src/app/core/tasks/models/task-object.model.ts @@ -1,5 +1,5 @@ import { Observable } from 'rxjs'; -import { link } from '../../cache/builders/build-decorators'; +import { link, resourceType } from '../../cache/builders/build-decorators'; import { CacheableObject } from '../../cache/object-cache.reducer'; import { RemoteData } from '../../data/remote-data'; @@ -15,6 +15,7 @@ import { TASK_OBJECT } from './task-object.resource-type'; /** * An abstract model class for a TaskObject. */ +@resourceType(TaskObject.type) export class TaskObject extends DSpaceObject implements CacheableObject { static type = TASK_OBJECT; @@ -34,19 +35,22 @@ export class TaskObject extends DSpaceObject implements CacheableObject { action: string; /** - * The group of this task + * The EPerson for this task + * Will be undefined unless the eperson HALLink has been resolved. */ @link(EPERSON) eperson?: Observable>; /** - * The group of this task + * The Group for this task + * Will be undefined unless the group HALLink has been resolved. */ @link(GROUP) group?: Observable>; /** - * The workflowitem object whom this task is related + * The WorkflowItem for this task + * Will be undefined unless the workflowitem HALLink has been resolved. */ @link(WorkflowItem.type) workflowitem?: Observable> | WorkflowItem; diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts index c6fc437a13..8a272b31ea 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts @@ -241,7 +241,7 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo this.listId = 'list-' + this.model.relationship.relationshipType; const submissionObject$ = this.submissionObjectService - .findById(this.model.submissionId).pipe( + .findById(this.model.submissionId, followLink('item'), followLink('collection')).pipe( getAllSucceededRemoteData(), getRemoteDataPayload() ); @@ -284,10 +284,10 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo this.ref.detectChanges(); })); - this.relationService.getRelatedItemsByLabel(this.item, this.model.relationship.relationshipType).pipe( + item$.pipe( + switchMap((item) => this.relationService.getRelatedItemsByLabel(item, this.model.relationship.relationshipType)), map((items: RemoteData>) => items.payload.page.map((item) => Object.assign(new ItemSearchResult(), { indexableObject: item }))), ).subscribe((relatedItems: Array>) => { - console.log('relatedItems', relatedItems); this.selectableListService.select(this.listId, relatedItems) }); } diff --git a/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.ts b/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.ts index 2378c8e251..59c1ae9a0e 100644 --- a/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.ts +++ b/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.ts @@ -54,7 +54,11 @@ export class WorkspaceitemActionsComponent extends MyDSpaceActionsComponent - - + + + + + diff --git a/src/app/shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component.ts index faf03425f0..432f69f28c 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component.ts @@ -1,16 +1,19 @@ import { Component } from '@angular/core'; import { Observable } from 'rxjs'; -import { find } from 'rxjs/operators'; +import { find, map } from 'rxjs/operators'; +import { LinkService } from '../../../../core/cache/builders/link.service'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { Item } from '../../../../core/shared/item.model'; import { ViewMode } from '../../../../core/shared/view-mode.model'; -import { RemoteData } from '../../../../core/data/remote-data'; -import { isNotUndefined } from '../../../empty.util'; import { WorkflowItem } from '../../../../core/submission/models/workflowitem.model'; -import { Item } from '../../../../core/shared/item.model'; -import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; +import { isNotUndefined } from '../../../empty.util'; import { listableObjectComponent } from '../../../object-collection/shared/listable-object/listable-object.decorator'; +import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; import { WorkflowItemSearchResult } from '../../../object-collection/shared/workflow-item-search-result.model'; +import { TruncatableService } from '../../../truncatable/truncatable.service'; +import { followLink } from '../../../utils/follow-link-config.model'; import { SearchResultListElementComponent } from '../../search-result-list-element/search-result-list-element.component'; /** @@ -28,18 +31,26 @@ export class WorkflowItemSearchResultListElementComponent extends SearchResultLi /** * The item object that belonging to the result object */ - public item: Item; + public item$: Observable; /** * Represent item's status */ public status = MyDspaceItemStatusType.WORKFLOW; + constructor( + protected truncatableService: TruncatableService, + protected linkService: LinkService + ) { + super(truncatableService); + } + /** * Initialize all instance variables */ ngOnInit() { super.ngOnInit(); + this.linkService.resolveLink(this.dso, followLink('item')); this.initItem(this.dso.item as Observable> ); } @@ -47,11 +58,10 @@ export class WorkflowItemSearchResultListElementComponent extends SearchResultLi * Retrieve item from result object */ initItem(item$: Observable>) { - item$.pipe( - find((rd: RemoteData) => rd.hasSucceeded && isNotUndefined(rd.payload)) - ).subscribe((rd: RemoteData) => { - this.item = rd.payload; - }); + this.item$ = item$.pipe( + find((rd: RemoteData) => rd.hasSucceeded && isNotUndefined(rd.payload)), + map((rd: RemoteData) => rd.payload) + ); } } diff --git a/src/app/shared/object-list/my-dspace-result-list-element/workspace-item-search-result/workspace-item-search-result-list-element.component.html b/src/app/shared/object-list/my-dspace-result-list-element/workspace-item-search-result/workspace-item-search-result-list-element.component.html index 79a31770d6..8966b4b1d8 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/workspace-item-search-result/workspace-item-search-result-list-element.component.html +++ b/src/app/shared/object-list/my-dspace-result-list-element/workspace-item-search-result/workspace-item-search-result-list-element.component.html @@ -1,7 +1,11 @@ - + + - + + + diff --git a/src/app/shared/object-list/my-dspace-result-list-element/workspace-item-search-result/workspace-item-search-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/workspace-item-search-result/workspace-item-search-result-list-element.component.ts index 830726c677..b9d89ef6ab 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/workspace-item-search-result/workspace-item-search-result-list-element.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/workspace-item-search-result/workspace-item-search-result-list-element.component.ts @@ -1,16 +1,19 @@ import { Component } from '@angular/core'; import { Observable } from 'rxjs'; -import { find } from 'rxjs/operators'; +import { find, map } from 'rxjs/operators'; +import { LinkService } from '../../../../core/cache/builders/link.service'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { Item } from '../../../../core/shared/item.model'; import { ViewMode } from '../../../../core/shared/view-mode.model'; import { WorkspaceItem } from '../../../../core/submission/models/workspaceitem.model'; -import { RemoteData } from '../../../../core/data/remote-data'; import { isNotUndefined } from '../../../empty.util'; -import { Item } from '../../../../core/shared/item.model'; -import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; import { listableObjectComponent } from '../../../object-collection/shared/listable-object/listable-object.decorator'; +import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; import { WorkspaceItemSearchResult } from '../../../object-collection/shared/workspace-item-search-result.model'; +import { TruncatableService } from '../../../truncatable/truncatable.service'; +import { followLink } from '../../../utils/follow-link-config.model'; import { SearchResultListElementComponent } from '../../search-result-list-element/search-result-list-element.component'; /** @@ -28,18 +31,26 @@ export class WorkspaceItemSearchResultListElementComponent extends SearchResultL /** * The item object that belonging to the result object */ - item: Item; + item$: Observable; /** * Represent item's status */ status = MyDspaceItemStatusType.WORKSPACE; + constructor( + protected truncatableService: TruncatableService, + protected linkService: LinkService + ) { + super(truncatableService); + } + /** * Initialize all instance variables */ ngOnInit() { super.ngOnInit(); + this.linkService.resolveLink(this.dso, followLink('item')); this.initItem(this.dso.item as Observable>); } @@ -47,10 +58,9 @@ export class WorkspaceItemSearchResultListElementComponent extends SearchResultL * Retrieve item from result object */ initItem(item$: Observable>) { - item$.pipe( - find((rd: RemoteData) => rd.hasSucceeded && isNotUndefined(rd.payload)) - ).subscribe((rd: RemoteData) => { - this.item = rd.payload; - }); + this.item$ = item$.pipe( + find((rd: RemoteData) => rd.hasSucceeded && isNotUndefined(rd.payload)), + map((rd: RemoteData) => rd.payload) + ); } } diff --git a/src/app/shared/search/search-query-response.model.ts b/src/app/shared/search/search-query-response.model.ts index da15a60631..2c9d11e2b3 100644 --- a/src/app/shared/search/search-query-response.model.ts +++ b/src/app/shared/search/search-query-response.model.ts @@ -1,6 +1,7 @@ import { autoserialize, autoserializeAs } from 'cerialize'; +import { DSpaceObject } from '../../core/shared/dspace-object.model'; import { PageInfo } from '../../core/shared/page-info.model'; -import { NormalizedSearchResult } from './normalized-search-result.model'; +import { SearchResult } from './search-result.model'; /** * Class representing the response returned by the server when performing a search request @@ -51,8 +52,8 @@ export class SearchQueryResponse { /** * The results for this query */ - @autoserializeAs(NormalizedSearchResult) - objects: NormalizedSearchResult[]; + @autoserializeAs(SearchResult) + objects: Array>; @autoserialize facets: any; // TODO diff --git a/src/app/shared/search/search-result.model.ts b/src/app/shared/search/search-result.model.ts index 8d26395021..415f81fae0 100644 --- a/src/app/shared/search/search-result.model.ts +++ b/src/app/shared/search/search-result.model.ts @@ -1,25 +1,40 @@ +import { autoserialize, deserialize } from 'cerialize'; +import { link } from '../../core/cache/builders/build-decorators'; import { DSpaceObject } from '../../core/shared/dspace-object.model'; -import { MetadataMap } from '../../core/shared/metadata.models'; -import { ListableObject } from '../object-collection/shared/listable-object.model'; -import { excludeFromEquals, fieldsForEquals } from '../../core/utilities/equals.decorators'; +import { DSPACE_OBJECT } from '../../core/shared/dspace-object.resource-type'; import { GenericConstructor } from '../../core/shared/generic-constructor'; +import { HALLink } from '../../core/shared/hal-link.model'; +import { MetadataMap } from '../../core/shared/metadata.models'; +import { excludeFromEquals, fieldsForEquals } from '../../core/utilities/equals.decorators'; +import { ListableObject } from '../object-collection/shared/listable-object.model'; /** * Represents a search result object of a certain () DSpaceObject */ export class SearchResult extends ListableObject { - /** - * The DSpaceObject that was found - */ - @fieldsForEquals('uuid') - indexableObject: T; - /** * The metadata that was used to find this item, hithighlighted */ @excludeFromEquals + @autoserialize hitHighlights: MetadataMap; + /** + * The HALLinks for this SearchResult + */ + @deserialize + _links: { + self: HALLink; + indexableObject: HALLink; + }; + + /** + * The DSpaceObject that was found + */ + @fieldsForEquals('uuid') + @link(DSPACE_OBJECT) + indexableObject: T; + /** * Method that returns as which type of object this object should be rendered */ diff --git a/src/app/submission/sections/upload/section-upload.component.ts b/src/app/submission/sections/upload/section-upload.component.ts index e835ff7452..0a589ffc33 100644 --- a/src/app/submission/sections/upload/section-upload.component.ts +++ b/src/app/submission/sections/upload/section-upload.component.ts @@ -164,9 +164,7 @@ export class SubmissionSectionUploadComponent extends SectionModelComponent { flatMap((submissionObject: SubmissionObjectEntry) => this.collectionDataService.findById(submissionObject.collection, followLink('defaultAccessConditions'))), filter((rd: RemoteData) => isNotUndefined((rd.payload))), tap((collectionRemoteData: RemoteData) => this.collectionName = collectionRemoteData.payload.name), - flatMap((collectionRemoteData: RemoteData) => { - return this.resourcePolicyService.findByHref((collectionRemoteData.payload as any).defaultAccessConditions); - }), + map((collectionRemoteData: RemoteData) => (collectionRemoteData.payload as any).defaultAccessConditions), filter((defaultAccessConditionsRemoteData: RemoteData) => defaultAccessConditionsRemoteData.hasSucceeded), tap((defaultAccessConditionsRemoteData: RemoteData) => { diff --git a/yarn.lock b/yarn.lock index 2d34ac734d..d37fab8e4b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10756,7 +10756,12 @@ reduce-function-call@^1.0.1, reduce-function-call@^1.0.2: dependencies: balanced-match "^0.4.2" -reflect-metadata@0.1.12, reflect-metadata@^0.1.2: +reflect-metadata@^0.1.13: + version "0.1.13" + resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" + integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== + +reflect-metadata@^0.1.2: version "0.1.12" resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.12.tgz#311bf0c6b63cd782f228a81abe146a2bfa9c56f2" integrity sha512-n+IyV+nGz3+0q3/Yf1ra12KpCyi001bi4XFxSjbiWWjfqb52iTTtpGXmCCAOWWIAn9KEuFZKGqBERHmrtScZ3A==