diff --git a/src/app/+search-page/paginated-search-options.model.ts b/src/app/+search-page/paginated-search-options.model.ts index 0c403af827..4f04480391 100644 --- a/src/app/+search-page/paginated-search-options.model.ts +++ b/src/app/+search-page/paginated-search-options.model.ts @@ -1,7 +1,6 @@ import { SortOptions } from '../core/cache/models/sort-options.model'; import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model'; import { isNotEmpty } from '../shared/empty.util'; -import { URLCombiner } from '../core/url-combiner/url-combiner'; import { SearchOptions } from './search-options.model'; export class PaginatedSearchOptions extends SearchOptions { 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 4c29a14675..a9f5223014 100644 --- a/src/app/core/cache/builders/remote-data-build.service.ts +++ b/src/app/core/cache/builders/remote-data-build.service.ts @@ -8,21 +8,17 @@ import { hasValue, isEmpty, isNotEmpty } from '../../../shared/empty.util'; import { PaginatedList } from '../../data/paginated-list'; import { RemoteData } from '../../data/remote-data'; import { RemoteDataError } from '../../data/remote-data-error'; -import { GetRequest, RestRequest } from '../../data/request.models'; +import { GetRequest } from '../../data/request.models'; import { RequestEntry } from '../../data/request.reducer'; import { RequestService } from '../../data/request.service'; -import { DSpaceObject } from '../../shared/dspace-object.model'; -import { GenericConstructor } from '../../shared/generic-constructor'; -import { NormalizedDSpaceObject } from '../models/normalized-dspace-object.model'; -import { NormalizedObjectFactory } from '../models/normalized-object-factory'; -import { CacheableObject } from '../object-cache.reducer'; import { ObjectCacheService } from '../object-cache.service'; -import { DSOSuccessResponse, ErrorResponse, SearchSuccessResponse } from '../response-cache.models'; +import { DSOSuccessResponse, ErrorResponse } from '../response-cache.models'; import { ResponseCacheEntry } from '../response-cache.reducer'; import { ResponseCacheService } from '../response-cache.service'; import { getMapsTo, getRelationMetadata, getRelationships } from './build-decorators'; import { NormalizedObject } from '../models/normalized-object.model'; +import { PageInfo } from '../../shared/page-info.model'; @Injectable() export class RemoteDataBuildService { @@ -159,35 +155,43 @@ export class RemoteDataBuildService { const relationships = getRelationships(normalized.constructor) || []; relationships.forEach((relationship: string) => { + let result; if (hasValue(normalized[relationship])) { const { resourceType, isList } = getRelationMetadata(normalized, relationship); - if (Array.isArray(normalized[relationship])) { - normalized[relationship].forEach((href: string) => { + const objectList = normalized[relationship].page || normalized[relationship]; + if (typeof objectList !== 'string') { + objectList.forEach((href: string) => { this.requestService.configure(new GetRequest(this.requestService.generateRequestId(), href)) }); const rdArr = []; - normalized[relationship].forEach((href: string) => { + objectList.forEach((href: string) => { rdArr.push(this.buildSingle(href)); }); if (isList) { - links[relationship] = this.aggregate(rdArr); + result = this.aggregate(rdArr); } else if (rdArr.length === 1) { - links[relationship] = rdArr[0]; + result = rdArr[0]; } } else { - this.requestService.configure(new GetRequest(this.requestService.generateRequestId(), normalized[relationship])); + this.requestService.configure(new GetRequest(this.requestService.generateRequestId(), objectList)); // The rest API can return a single URL to represent a list of resources (e.g. /items/:id/bitstreams) // in that case only 1 href will be stored in the normalized obj (so the isArray above fails), // but it should still be built as a list if (isList) { - links[relationship] = this.buildList(normalized[relationship]); + result = this.buildList(objectList); } else { - links[relationship] = this.buildSingle(normalized[relationship]); + result = this.buildSingle(objectList); } } + + if (hasValue(normalized[relationship].page)) { + links[relationship] = this.aggregatePaginatedList(result, normalized[relationship].pageInfo); + } else { + links[relationship] = result; + } } }); @@ -248,4 +252,8 @@ export class RemoteDataBuildService { }) } + aggregatePaginatedList(input: Observable>, pageInfo: PageInfo): Observable>> { + return input.map((rd) => Object.assign(rd, {payload: new PaginatedList(pageInfo, rd.payload)})); + } + } diff --git a/src/app/core/cache/response-cache.service.spec.ts b/src/app/core/cache/response-cache.service.spec.ts index 1def7faa02..77838b6eb6 100644 --- a/src/app/core/cache/response-cache.service.spec.ts +++ b/src/app/core/cache/response-cache.service.spec.ts @@ -46,7 +46,6 @@ describe('ResponseCacheService', () => { let testObj: ResponseCacheEntry; service.get(keys[1]).first().subscribe((entry) => { - console.log(entry); testObj = entry; }); expect(testObj.key).toEqual(keys[1]); diff --git a/src/app/core/data/base-response-parsing.service.ts b/src/app/core/data/base-response-parsing.service.ts index 018f9f5adb..b7bb5b5784 100644 --- a/src/app/core/data/base-response-parsing.service.ts +++ b/src/app/core/data/base-response-parsing.service.ts @@ -34,19 +34,26 @@ export abstract class BaseResponseParsingService { } else if (Array.isArray(data)) { return this.processArray(data, requestHref); } else if (isObjectLevel(data)) { - let object = this.deserialize(data, requestHref); - this.cache(object, requestHref); + const object = this.deserialize(data); if (isNotEmpty(data._embedded)) { - const list = {}; Object .keys(data._embedded) .filter((property) => data._embedded.hasOwnProperty(property)) .forEach((property) => { const parsedObj = this.process(data._embedded[property], requestHref); - list[property] = parsedObj; + if (isNotEmpty(parsedObj)) { + if (isPaginatedResponse(data._embedded[property])) { + object[property] = parsedObj; + object[property].page = parsedObj.page.map((obj) => obj.self); + } else if (isObjectLevel(data._embedded[property])) { + object[property] = parsedObj.self; + } else if (Array.isArray(parsedObj)) { + object[property] = parsedObj.map((obj) => obj.self) + } + } }); - object = Object.assign({}, object, list); } + this.cache(object, requestHref); return object; } const result = {}; @@ -83,7 +90,7 @@ export abstract class BaseResponseParsingService { return array; } - protected deserialize(obj, requestHref) { + protected deserialize(obj): any { const type: ObjectType = obj.type; if (hasValue(type)) { const normObjConstructor = this.objectFactory.getConstructor(type) as GenericConstructor; diff --git a/src/app/core/data/config-response-parsing.service.spec.ts b/src/app/core/data/config-response-parsing.service.spec.ts index 9be31024c1..654ee53651 100644 --- a/src/app/core/data/config-response-parsing.service.spec.ts +++ b/src/app/core/data/config-response-parsing.service.spec.ts @@ -1,5 +1,4 @@ import { ConfigSuccessResponse, ErrorResponse } from '../cache/response-cache.models'; -import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model'; import { ConfigResponseParsingService } from './config-response-parsing.service'; import { ObjectCacheService } from '../cache/object-cache.service'; import { GlobalConfig } from '../../../config/global-config.interface'; @@ -8,7 +7,8 @@ import { ConfigRequest } from './request.models'; import { Store } from '@ngrx/store'; import { CoreState } from '../core.reducers'; import { SubmissionDefinitionsModel } from '../shared/config/config-submission-definitions.model'; -import { SubmissionSectionModel } from '../shared/config/config-submission-section.model'; +import { PaginatedList } from './paginated-list'; +import { PageInfo } from '../shared/page-info.model'; describe('ConfigResponseParsingService', () => { let service: ConfigResponseParsingService; @@ -16,141 +16,143 @@ describe('ConfigResponseParsingService', () => { const EnvConfig = {} as GlobalConfig; const store = {} as Store; const objectCacheService = new ObjectCacheService(store); - + let validResponse; beforeEach(() => { service = new ConfigResponseParsingService(EnvConfig, objectCacheService); + validResponse = { + payload: { + id: 'traditional', + name: 'traditional', + type: 'submissiondefinition', + isDefault: true, + _links: { + sections: { + href: 'https://rest.api/config/submissiondefinitions/traditional/sections' + }, + self: { + href: 'https://rest.api/config/submissiondefinitions/traditional' + } + }, + _embedded: { + sections: { + page: { + number: 0, + size: 4, + totalPages: 1, totalElements: 4 + }, + _embedded: [ + { + id: 'traditionalpageone', header: 'submit.progressbar.describe.stepone', + mandatory: true, + sectionType: 'submission-form', + visibility: { + main: null, + other: 'READONLY' + }, + type: 'submissionsection', + _links: { + self: { + href: 'https://rest.api/config/submissionsections/traditionalpageone' + }, + config: { + href: 'https://rest.api/config/submissionforms/traditionalpageone' + } + } + }, { + id: 'traditionalpagetwo', + header: 'submit.progressbar.describe.steptwo', + mandatory: true, + sectionType: 'submission-form', + visibility: { + main: null, + other: 'READONLY' + }, + type: 'submissionsection', + _links: { + self: { + href: 'https://rest.api/config/submissionsections/traditionalpagetwo' + }, + config: { + href: 'https://rest.api/config/submissionforms/traditionalpagetwo' + } + } + }, { + id: 'upload', + header: 'submit.progressbar.upload', + mandatory: false, + sectionType: 'upload', + visibility: { + main: null, + other: 'READONLY' + }, + type: 'submissionsection', + _links: { + self: { + href: 'https://rest.api/config/submissionsections/upload' + }, + config: { + href: 'https://rest.api/config/submissionuploads/upload' + } + } + }, { + id: 'license', + header: 'submit.progressbar.license', + mandatory: true, + sectionType: 'license', + visibility: { + main: null, + other: 'READONLY' + }, + type: 'submissionsection', + _links: { + self: { + href: 'https://rest.api/config/submissionsections/license' + } + } + } + ], + _links: { + self: { + href: 'https://rest.api/config/submissiondefinitions/traditional/sections' + } + } + } + } + }, + statusCode: '200' + }; }); describe('parse', () => { const validRequest = new ConfigRequest('69f375b5-19f4-4453-8c7a-7dc5c55aafbb', 'https://rest.api/config/submissiondefinitions/traditional'); - const validResponse = { - payload: { - id:'traditional', - name:'traditional', - type:'submissiondefinition', - isDefault:true, - _links:{ - sections:{ - href:'https://rest.api/config/submissiondefinitions/traditional/sections' - },self:{ - href:'https://rest.api/config/submissiondefinitions/traditional' - } - }, - _embedded:{ - sections:{ - page:{ - number:0, - size:4, - totalPages:1,totalElements:4 - }, - _embedded:[ - { - id:'traditionalpageone',header:'submit.progressbar.describe.stepone', - mandatory:true, - sectionType:'submission-form', - visibility:{ - main:null, - other:'READONLY' - }, - type:'submissionsection', - _links:{ - self:{ - href:'https://rest.api/config/submissionsections/traditionalpageone' - }, - config:{ - href:'https://rest.api/config/submissionforms/traditionalpageone' - } - } - }, { - id:'traditionalpagetwo', - header:'submit.progressbar.describe.steptwo', - mandatory:true, - sectionType:'submission-form', - visibility:{ - main:null, - other:'READONLY' - }, - type:'submissionsection', - _links:{ - self:{ - href:'https://rest.api/config/submissionsections/traditionalpagetwo' - }, - config:{ - href:'https://rest.api/config/submissionforms/traditionalpagetwo' - } - } - }, { - id:'upload', - header:'submit.progressbar.upload', - mandatory:false, - sectionType:'upload', - visibility:{ - main:null, - other:'READONLY' - }, - type:'submissionsection', - _links:{ - self:{ - href:'https://rest.api/config/submissionsections/upload' - }, - config: { - href:'https://rest.api/config/submissionuploads/upload' - } - } - }, { - id:'license', - header:'submit.progressbar.license', - mandatory:true, - sectionType:'license', - visibility:{ - main:null, - other:'READONLY' - }, - type:'submissionsection', - _links:{ - self:{ - href:'https://rest.api/config/submissionsections/license' - } - } - } - ], - _links:{ - self:'https://rest.api/config/submissiondefinitions/traditional/sections' - } - } - } - }, - statusCode:'200' - }; - const invalidResponse1 = { payload: {}, - statusCode:'200' + statusCode: '200' }; const invalidResponse2 = { payload: { - id:'traditional', - name:'traditional', - type:'submissiondefinition', - isDefault:true, - _links:{}, - _embedded:{ - sections:{ - page:{ - number:0, - size:4, - totalPages:1,totalElements:4 + id: 'traditional', + name: 'traditional', + type: 'submissiondefinition', + isDefault: true, + _links: {}, + _embedded: { + sections: { + page: { + number: 0, + size: 4, + totalPages: 1, totalElements: 4 }, - _embedded:[{},{}], - _links:{ - self:'https://rest.api/config/submissiondefinitions/traditional/sections' + _embedded: [{}, {}], + _links: { + self: 'https://rest.api/config/submissiondefinitions/traditional/sections' } } } }, - statusCode:'200' + statusCode: '200' }; const invalidResponse3 = { @@ -159,61 +161,24 @@ describe('ConfigResponseParsingService', () => { page: { size: 20, totalElements: 2, totalPages: 1, number: 0 } }, statusCode: '500' }; - - const definitions = [ + const pageinfo = Object.assign(new PageInfo(), { elementsPerPage: 4, totalElements: 4, totalPages: 1, currentPage: 1 }); + const definitions = Object.assign(new SubmissionDefinitionsModel(), { isDefault: true, name: 'traditional', type: 'submissiondefinition', - _links: {}, - sections: [ - Object.assign(new SubmissionSectionModel(), { - header: 'submit.progressbar.describe.stepone', - mandatory: true, - sectionType: 'submission-form', - visibility:{ - main:null, - other:'READONLY' - }, - type: 'submissionsection', - _links: {} - }), - Object.assign(new SubmissionSectionModel(), { - header: 'submit.progressbar.describe.steptwo', - mandatory: true, - sectionType: 'submission-form', - visibility:{ - main:null, - other:'READONLY' - }, - type: 'submissionsection', - _links: {} - }), - Object.assign(new SubmissionSectionModel(), { - header: 'submit.progressbar.upload', - mandatory: false, - sectionType: 'upload', - visibility:{ - main:null, - other:'READONLY' - }, - type: 'submissionsection', - _links: {} - }), - Object.assign(new SubmissionSectionModel(), { - header: 'submit.progressbar.license', - mandatory: true, - sectionType: 'license', - visibility:{ - main:null, - other:'READONLY' - }, - type: 'submissionsection', - _links: {} - }) - ] - }) - ]; + _links: { + sections: 'https://rest.api/config/submissiondefinitions/traditional/sections', + self: 'https://rest.api/config/submissiondefinitions/traditional' + }, + self: 'https://rest.api/config/submissiondefinitions/traditional', + sections: new PaginatedList(pageinfo, [ + 'https://rest.api/config/submissionsections/traditionalpageone', + 'https://rest.api/config/submissionsections/traditionalpagetwo', + 'https://rest.api/config/submissionsections/upload', + 'https://rest.api/config/submissionsections/license' + ]) + }); it('should return a ConfigSuccessResponse if data contains a valid config endpoint response', () => { const response = service.parse(validRequest, validResponse); @@ -232,11 +197,8 @@ describe('ConfigResponseParsingService', () => { expect(response.constructor).toBe(ErrorResponse); }); - fit('should return a ConfigSuccessResponse with the ConfigDefinitions in data', () => { + it('should return a ConfigSuccessResponse with the ConfigDefinitions in data', () => { const response = service.parse(validRequest, validResponse); - debugger; - console.log(definitions); - console.log((response as any).configDefinition); expect((response as any).configDefinition).toEqual(definitions); }); diff --git a/src/app/core/data/config-response-parsing.service.ts b/src/app/core/data/config-response-parsing.service.ts index 320d931c29..b67fa2caf1 100644 --- a/src/app/core/data/config-response-parsing.service.ts +++ b/src/app/core/data/config-response-parsing.service.ts @@ -27,7 +27,6 @@ export class ConfigResponseParsingService extends BaseResponseParsingService imp } parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse { - console.log(data); if (isNotEmpty(data.payload) && isNotEmpty(data.payload._links) && data.statusCode === '200') { const configDefinition = this.process(data.payload, request.href); return new ConfigSuccessResponse(configDefinition, data.statusCode, this.processPageInfo(data.payload));