finished embedded object fixes

This commit is contained in:
Lotte Hofstede
2018-05-25 12:48:00 +02:00
parent 2a8de6dc49
commit 0bcc8ee007
6 changed files with 172 additions and 198 deletions

View File

@@ -1,7 +1,6 @@
import { SortOptions } from '../core/cache/models/sort-options.model'; import { SortOptions } from '../core/cache/models/sort-options.model';
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model'; import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
import { isNotEmpty } from '../shared/empty.util'; import { isNotEmpty } from '../shared/empty.util';
import { URLCombiner } from '../core/url-combiner/url-combiner';
import { SearchOptions } from './search-options.model'; import { SearchOptions } from './search-options.model';
export class PaginatedSearchOptions extends SearchOptions { export class PaginatedSearchOptions extends SearchOptions {

View File

@@ -8,21 +8,17 @@ import { hasValue, isEmpty, isNotEmpty } from '../../../shared/empty.util';
import { PaginatedList } from '../../data/paginated-list'; import { PaginatedList } from '../../data/paginated-list';
import { RemoteData } from '../../data/remote-data'; import { RemoteData } from '../../data/remote-data';
import { RemoteDataError } from '../../data/remote-data-error'; 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 { RequestEntry } from '../../data/request.reducer';
import { RequestService } from '../../data/request.service'; 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 { 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 { ResponseCacheEntry } from '../response-cache.reducer';
import { ResponseCacheService } from '../response-cache.service'; import { ResponseCacheService } from '../response-cache.service';
import { getMapsTo, getRelationMetadata, getRelationships } from './build-decorators'; import { getMapsTo, getRelationMetadata, getRelationships } from './build-decorators';
import { NormalizedObject } from '../models/normalized-object.model'; import { NormalizedObject } from '../models/normalized-object.model';
import { PageInfo } from '../../shared/page-info.model';
@Injectable() @Injectable()
export class RemoteDataBuildService { export class RemoteDataBuildService {
@@ -159,35 +155,43 @@ export class RemoteDataBuildService {
const relationships = getRelationships(normalized.constructor) || []; const relationships = getRelationships(normalized.constructor) || [];
relationships.forEach((relationship: string) => { relationships.forEach((relationship: string) => {
let result;
if (hasValue(normalized[relationship])) { if (hasValue(normalized[relationship])) {
const { resourceType, isList } = getRelationMetadata(normalized, relationship); const { resourceType, isList } = getRelationMetadata(normalized, relationship);
if (Array.isArray(normalized[relationship])) { const objectList = normalized[relationship].page || normalized[relationship];
normalized[relationship].forEach((href: string) => { if (typeof objectList !== 'string') {
objectList.forEach((href: string) => {
this.requestService.configure(new GetRequest(this.requestService.generateRequestId(), href)) this.requestService.configure(new GetRequest(this.requestService.generateRequestId(), href))
}); });
const rdArr = []; const rdArr = [];
normalized[relationship].forEach((href: string) => { objectList.forEach((href: string) => {
rdArr.push(this.buildSingle(href)); rdArr.push(this.buildSingle(href));
}); });
if (isList) { if (isList) {
links[relationship] = this.aggregate(rdArr); result = this.aggregate(rdArr);
} else if (rdArr.length === 1) { } else if (rdArr.length === 1) {
links[relationship] = rdArr[0]; result = rdArr[0];
} }
} else { } 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) // 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), // 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 // but it should still be built as a list
if (isList) { if (isList) {
links[relationship] = this.buildList(normalized[relationship]); result = this.buildList(objectList);
} else { } 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<T>(input: Observable<RemoteData<T[]>>, pageInfo: PageInfo): Observable<RemoteData<PaginatedList<T>>> {
return input.map((rd) => Object.assign(rd, {payload: new PaginatedList(pageInfo, rd.payload)}));
}
} }

View File

@@ -46,7 +46,6 @@ describe('ResponseCacheService', () => {
let testObj: ResponseCacheEntry; let testObj: ResponseCacheEntry;
service.get(keys[1]).first().subscribe((entry) => { service.get(keys[1]).first().subscribe((entry) => {
console.log(entry);
testObj = entry; testObj = entry;
}); });
expect(testObj.key).toEqual(keys[1]); expect(testObj.key).toEqual(keys[1]);

View File

@@ -34,19 +34,26 @@ export abstract class BaseResponseParsingService {
} else if (Array.isArray(data)) { } else if (Array.isArray(data)) {
return this.processArray(data, requestHref); return this.processArray(data, requestHref);
} else if (isObjectLevel(data)) { } else if (isObjectLevel(data)) {
let object = this.deserialize(data, requestHref); const object = this.deserialize(data);
this.cache(object, requestHref);
if (isNotEmpty(data._embedded)) { if (isNotEmpty(data._embedded)) {
const list = {};
Object Object
.keys(data._embedded) .keys(data._embedded)
.filter((property) => data._embedded.hasOwnProperty(property)) .filter((property) => data._embedded.hasOwnProperty(property))
.forEach((property) => { .forEach((property) => {
const parsedObj = this.process<ObjectDomain, ObjectType>(data._embedded[property], requestHref); const parsedObj = this.process<ObjectDomain, ObjectType>(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; return object;
} }
const result = {}; const result = {};
@@ -83,7 +90,7 @@ export abstract class BaseResponseParsingService {
return array; return array;
} }
protected deserialize<ObjectDomain, ObjectType>(obj, requestHref) { protected deserialize<ObjectDomain, ObjectType>(obj): any {
const type: ObjectType = obj.type; const type: ObjectType = obj.type;
if (hasValue(type)) { if (hasValue(type)) {
const normObjConstructor = this.objectFactory.getConstructor(type) as GenericConstructor<ObjectDomain>; const normObjConstructor = this.objectFactory.getConstructor(type) as GenericConstructor<ObjectDomain>;

View File

@@ -1,5 +1,4 @@
import { ConfigSuccessResponse, ErrorResponse } from '../cache/response-cache.models'; 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 { ConfigResponseParsingService } from './config-response-parsing.service';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { GlobalConfig } from '../../../config/global-config.interface'; import { GlobalConfig } from '../../../config/global-config.interface';
@@ -8,7 +7,8 @@ import { ConfigRequest } from './request.models';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { CoreState } from '../core.reducers'; import { CoreState } from '../core.reducers';
import { SubmissionDefinitionsModel } from '../shared/config/config-submission-definitions.model'; 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', () => { describe('ConfigResponseParsingService', () => {
let service: ConfigResponseParsingService; let service: ConfigResponseParsingService;
@@ -16,141 +16,143 @@ describe('ConfigResponseParsingService', () => {
const EnvConfig = {} as GlobalConfig; const EnvConfig = {} as GlobalConfig;
const store = {} as Store<CoreState>; const store = {} as Store<CoreState>;
const objectCacheService = new ObjectCacheService(store); const objectCacheService = new ObjectCacheService(store);
let validResponse;
beforeEach(() => { beforeEach(() => {
service = new ConfigResponseParsingService(EnvConfig, objectCacheService); 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', () => { describe('parse', () => {
const validRequest = new ConfigRequest('69f375b5-19f4-4453-8c7a-7dc5c55aafbb', 'https://rest.api/config/submissiondefinitions/traditional'); 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 = { const invalidResponse1 = {
payload: {}, payload: {},
statusCode:'200' statusCode: '200'
}; };
const invalidResponse2 = { const invalidResponse2 = {
payload: { payload: {
id:'traditional', id: 'traditional',
name:'traditional', name: 'traditional',
type:'submissiondefinition', type: 'submissiondefinition',
isDefault:true, isDefault: true,
_links:{}, _links: {},
_embedded:{ _embedded: {
sections:{ sections: {
page:{ page: {
number:0, number: 0,
size:4, size: 4,
totalPages:1,totalElements:4 totalPages: 1, totalElements: 4
}, },
_embedded:[{},{}], _embedded: [{}, {}],
_links:{ _links: {
self:'https://rest.api/config/submissiondefinitions/traditional/sections' self: 'https://rest.api/config/submissiondefinitions/traditional/sections'
} }
} }
} }
}, },
statusCode:'200' statusCode: '200'
}; };
const invalidResponse3 = { const invalidResponse3 = {
@@ -159,61 +161,24 @@ describe('ConfigResponseParsingService', () => {
page: { size: 20, totalElements: 2, totalPages: 1, number: 0 } page: { size: 20, totalElements: 2, totalPages: 1, number: 0 }
}, statusCode: '500' }, statusCode: '500'
}; };
const pageinfo = Object.assign(new PageInfo(), { elementsPerPage: 4, totalElements: 4, totalPages: 1, currentPage: 1 });
const definitions = [ const definitions =
Object.assign(new SubmissionDefinitionsModel(), { Object.assign(new SubmissionDefinitionsModel(), {
isDefault: true, isDefault: true,
name: 'traditional', name: 'traditional',
type: 'submissiondefinition', type: 'submissiondefinition',
_links: {}, _links: {
sections: [ sections: 'https://rest.api/config/submissiondefinitions/traditional/sections',
Object.assign(new SubmissionSectionModel(), { self: 'https://rest.api/config/submissiondefinitions/traditional'
header: 'submit.progressbar.describe.stepone', },
mandatory: true, self: 'https://rest.api/config/submissiondefinitions/traditional',
sectionType: 'submission-form', sections: new PaginatedList(pageinfo, [
visibility:{ 'https://rest.api/config/submissionsections/traditionalpageone',
main:null, 'https://rest.api/config/submissionsections/traditionalpagetwo',
other:'READONLY' 'https://rest.api/config/submissionsections/upload',
}, 'https://rest.api/config/submissionsections/license'
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: {}
})
]
})
];
it('should return a ConfigSuccessResponse if data contains a valid config endpoint response', () => { it('should return a ConfigSuccessResponse if data contains a valid config endpoint response', () => {
const response = service.parse(validRequest, validResponse); const response = service.parse(validRequest, validResponse);
@@ -232,11 +197,8 @@ describe('ConfigResponseParsingService', () => {
expect(response.constructor).toBe(ErrorResponse); 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); const response = service.parse(validRequest, validResponse);
debugger;
console.log(definitions);
console.log((response as any).configDefinition);
expect((response as any).configDefinition).toEqual(definitions); expect((response as any).configDefinition).toEqual(definitions);
}); });

View File

@@ -27,7 +27,6 @@ export class ConfigResponseParsingService extends BaseResponseParsingService imp
} }
parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse { parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse {
console.log(data);
if (isNotEmpty(data.payload) && isNotEmpty(data.payload._links) && data.statusCode === '200') { if (isNotEmpty(data.payload) && isNotEmpty(data.payload._links) && data.statusCode === '200') {
const configDefinition = this.process<ConfigObject,ConfigType>(data.payload, request.href); const configDefinition = this.process<ConfigObject,ConfigType>(data.payload, request.href);
return new ConfigSuccessResponse(configDefinition, data.statusCode, this.processPageInfo(data.payload)); return new ConfigSuccessResponse(configDefinition, data.statusCode, this.processPageInfo(data.payload));