Merge pull request #3138 from 4Science/task/main/DURACOM-282

Fix error which prevent submitter to deposit a new item
This commit is contained in:
Tim Donohue
2024-06-20 11:23:25 -05:00
committed by GitHub
5 changed files with 411 additions and 17 deletions

View File

@@ -340,4 +340,384 @@ describe('jsonPatchOperationsReducer test suite', () => {
}); });
}); });
describe('dedupeOperationEntries', () => {
it('should not remove duplicated keys if operations are not sequential', () => {
initState = {
sections: {
children: {
publicationStep: {
body: [
{
operation: {
op: 'add',
path: '/sections/publicationStep/dc.date.issued',
value: [
{
value: '2024-06',
language: null,
authority: null,
display: '2024-06',
confidence: -1,
place: 0,
otherInformation: null,
},
],
},
timeCompleted: timestampBeforeStart,
},
{
operation: {
op: 'replace',
path: '/sections/publicationStep/dc.date.issued/0',
value: {
value: '2023-06-19',
language: null,
authority: null,
display: '2023-06-19',
confidence: -1,
place: 0,
otherInformation: null,
},
},
timeCompleted: timestampBeforeStart,
},
],
} as JsonPatchOperationsEntry,
},
transactionStartTime: null,
commitPending: false,
} as JsonPatchOperationsResourceEntry,
};
const value = [
{
value: '2024-06-19',
language: null,
authority: null,
display: '2024-06-19',
confidence: -1,
place: 0,
otherInformation: null,
},
];
const action = new NewPatchAddOperationAction(
'sections',
'publicationStep',
'/sections/publicationStep/dc.date.issued',
value);
const newState = jsonPatchOperationsReducer(initState, action);
const expectedBody: any = [
{
'operation': {
'op': 'add',
'path': '/sections/publicationStep/dc.date.issued',
'value': [
{
'value': '2024-06',
'language': null,
'authority': null,
'display': '2024-06',
'confidence': -1,
'place': 0,
'otherInformation': null,
},
],
},
'timeCompleted': timestampBeforeStart,
},
{
'operation': {
'op': 'replace',
'path': '/sections/publicationStep/dc.date.issued/0',
'value': {
'value': '2023-06-19',
'language': null,
'authority': null,
'display': '2023-06-19',
'confidence': -1,
'place': 0,
'otherInformation': null,
},
},
'timeCompleted': timestampBeforeStart,
},
{
'operation': {
'op': 'add',
'path': '/sections/publicationStep/dc.date.issued',
'value': [
{
'value': '2024-06-19',
'language': null,
'authority': null,
'display': '2024-06-19',
'confidence': -1,
'place': 0,
'otherInformation': null,
},
],
},
'timeCompleted': timestampBeforeStart,
},
];
expect(newState.sections.children.publicationStep.body).toEqual(expectedBody);
});
it('should remove duplicated keys if operations are sequential', () => {
initState = {
sections: {
children: {
publicationStep: {
body: [
{
operation: {
op: 'add',
path: '/sections/publicationStep/dc.date.issued',
value: [
{
value: '2024-06',
language: null,
authority: null,
display: '2024-06',
confidence: -1,
place: 0,
otherInformation: null,
},
],
},
timeCompleted: timestampBeforeStart,
},
{
operation: {
op: 'replace',
path: '/sections/publicationStep/dc.date.issued/0',
value: {
value: '2023-06-19',
language: null,
authority: null,
display: '2023-06-19',
confidence: -1,
place: 0,
otherInformation: null,
},
},
timeCompleted: timestampBeforeStart,
},
{
'operation': {
'op': 'add',
'path': '/sections/publicationStep/dc.date.issued',
'value': [
{
'value': '2024-06-19',
'language': null,
'authority': null,
'display': '2024-06-19',
'confidence': -1,
'place': 0,
'otherInformation': null,
},
],
},
'timeCompleted': timestampBeforeStart,
},
],
} as JsonPatchOperationsEntry,
},
transactionStartTime: null,
commitPending: false,
} as JsonPatchOperationsResourceEntry,
};
const value = [
{
value: '2024-06-20',
language: null,
authority: null,
display: '2024-06-20',
confidence: -1,
place: 0,
otherInformation: null,
},
];
const action = new NewPatchAddOperationAction(
'sections',
'publicationStep',
'/sections/publicationStep/dc.date.issued',
value);
const newState = jsonPatchOperationsReducer(initState, action);
const expectedBody: any = [
{
'operation': {
'op': 'add',
'path': '/sections/publicationStep/dc.date.issued',
'value': [
{
'value': '2024-06',
'language': null,
'authority': null,
'display': '2024-06',
'confidence': -1,
'place': 0,
'otherInformation': null,
},
],
},
'timeCompleted': timestampBeforeStart,
},
{
'operation': {
'op': 'replace',
'path': '/sections/publicationStep/dc.date.issued/0',
'value': {
'value': '2023-06-19',
'language': null,
'authority': null,
'display': '2023-06-19',
'confidence': -1,
'place': 0,
'otherInformation': null,
},
},
'timeCompleted': timestampBeforeStart,
},
{
'operation': {
'op': 'add',
'path': '/sections/publicationStep/dc.date.issued',
'value': [
{
'value': '2024-06-20',
'language': null,
'authority': null,
'display': '2024-06-20',
'confidence': -1,
'place': 0,
'otherInformation': null,
},
],
},
'timeCompleted': timestampBeforeStart,
},
];
expect(newState.sections.children.publicationStep.body).toEqual(expectedBody);
});
it('should remove duplicated keys if all operations have same key', () => {
initState = {
sections: {
children: {
publicationStep: {
body: [
{
operation: {
op: 'add',
path: '/sections/publicationStep/dc.date.issued',
value: [
{
value: '2024',
language: null,
authority: null,
display: '2024-06',
confidence: -1,
place: 0,
otherInformation: null,
},
],
},
timeCompleted: timestampBeforeStart,
},
{
'operation': {
'op': 'add',
'path': '/sections/publicationStep/dc.date.issued',
'value': [
{
'value': '2024-06',
'language': null,
'authority': null,
'display': '2024-06',
'confidence': -1,
'place': 0,
'otherInformation': null,
},
],
},
'timeCompleted': timestampBeforeStart,
},
{
'operation': {
'op': 'add',
'path': '/sections/publicationStep/dc.date.issued',
'value': [
{
'value': '2024-06-19',
'language': null,
'authority': null,
'display': '2024-06-19',
'confidence': -1,
'place': 0,
'otherInformation': null,
},
],
},
'timeCompleted': timestampBeforeStart,
},
],
} as JsonPatchOperationsEntry,
},
transactionStartTime: null,
commitPending: false,
} as JsonPatchOperationsResourceEntry,
};
const value = [
{
value: '2024-06-20',
language: null,
authority: null,
display: '2024-06-20',
confidence: -1,
place: 0,
otherInformation: null,
},
];
const action = new NewPatchAddOperationAction(
'sections',
'publicationStep',
'/sections/publicationStep/dc.date.issued',
value);
const newState = jsonPatchOperationsReducer(initState, action);
const expectedBody: any = [
{
'operation': {
'op': 'add',
'path': '/sections/publicationStep/dc.date.issued',
'value': [
{
'value': '2024-06-20',
'language': null,
'authority': null,
'display': '2024-06-20',
'confidence': -1,
'place': 0,
'otherInformation': null,
},
],
},
'timeCompleted': timestampBeforeStart,
},
];
expect(newState.sections.children.publicationStep.body).toEqual(expectedBody);
});
});
}); });

View File

@@ -406,14 +406,18 @@ function addOperationToList(body: JsonPatchOperationObject[], actionType, target
* @returns deduped JSON patch operation object entries * @returns deduped JSON patch operation object entries
*/ */
function dedupeOperationEntries(body: JsonPatchOperationObject[]): JsonPatchOperationObject[] { function dedupeOperationEntries(body: JsonPatchOperationObject[]): JsonPatchOperationObject[] {
const ops = new Map<string, any>(); const ops = new Map<string, number>();
for (let i = body.length - 1; i >= 0; i--) { for (let i = body.length - 1; i >= 0; i--) {
const patch = body[i].operation; const patch = body[i].operation;
const key = `${patch.op}-${patch.path}`; const key = `${patch.op}-${patch.path}`;
if (!ops.has(key)) { if (!ops.has(key)) {
ops.set(key, patch); ops.set(key, i);
} else { } else {
body.splice(i, 1); const entry = ops.get(key);
if (entry - 1 === i) {
body.splice(i, 1);
ops.set(key, i);
}
} }
} }

View File

@@ -8,7 +8,10 @@ import {
import { getDSORoute } from '../../app-routing-paths'; import { getDSORoute } from '../../app-routing-paths';
import { Breadcrumb } from '../../breadcrumbs/breadcrumb/breadcrumb.model'; import { Breadcrumb } from '../../breadcrumbs/breadcrumb/breadcrumb.model';
import { hasValue } from '../../shared/empty.util'; import {
hasValue,
isEmpty,
} from '../../shared/empty.util';
import { SubmissionService } from '../../submission/submission.service'; import { SubmissionService } from '../../submission/submission.service';
import { BreadcrumbsProviderService } from '../breadcrumbs/breadcrumbsProviderService'; import { BreadcrumbsProviderService } from '../breadcrumbs/breadcrumbsProviderService';
import { DSOBreadcrumbsService } from '../breadcrumbs/dso-breadcrumbs.service'; import { DSOBreadcrumbsService } from '../breadcrumbs/dso-breadcrumbs.service';
@@ -46,6 +49,10 @@ export class SubmissionParentBreadcrumbsService implements BreadcrumbsProviderSe
* @param submissionObject The {@link SubmissionObject} for which the parent breadcrumb structure needs to be created * @param submissionObject The {@link SubmissionObject} for which the parent breadcrumb structure needs to be created
*/ */
getBreadcrumbs(submissionObject: SubmissionObject): Observable<Breadcrumb[]> { getBreadcrumbs(submissionObject: SubmissionObject): Observable<Breadcrumb[]> {
if (isEmpty(submissionObject)) {
return observableOf([]);
}
return combineLatest([ return combineLatest([
(submissionObject.collection as Observable<RemoteData<Collection>>).pipe( (submissionObject.collection as Observable<RemoteData<Collection>>).pipe(
getFirstCompletedRemoteData(), getFirstCompletedRemoteData(),

View File

@@ -13,6 +13,7 @@ import {
isNotEmpty, isNotEmpty,
} from '../../shared/empty.util'; } from '../../shared/empty.util';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { ErrorResponse } from '../cache/response.models';
import { RemoteData } from '../data/remote-data'; import { RemoteData } from '../data/remote-data';
import { import {
DeleteRequest, DeleteRequest,
@@ -23,6 +24,7 @@ import {
SubmissionRequest, SubmissionRequest,
} from '../data/request.models'; } from '../data/request.models';
import { RequestService } from '../data/request.service'; import { RequestService } from '../data/request.service';
import { RequestError } from '../data/request-error.model';
import { RestRequest } from '../data/rest-request.model'; import { RestRequest } from '../data/rest-request.model';
import { HttpOptions } from '../dspace-rest/dspace-rest.service'; import { HttpOptions } from '../dspace-rest/dspace-rest.service';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
@@ -57,7 +59,7 @@ export class SubmissionRestService {
getFirstCompletedRemoteData(), getFirstCompletedRemoteData(),
map((response: RemoteData<SubmissionResponse>) => { map((response: RemoteData<SubmissionResponse>) => {
if (response.hasFailed) { if (response.hasFailed) {
throw new Error(response.errorMessage); throw new ErrorResponse({ statusText: response.errorMessage, statusCode: response.statusCode } as RequestError);
} else { } else {
return hasValue(response.payload) ? response.payload.dataDefinition : response.payload; return hasValue(response.payload) ? response.payload.dataDefinition : response.payload;
} }

View File

@@ -227,20 +227,21 @@ export class SubmissionSectionUploadComponent extends SectionModelComponent {
this.changeDetectorRef.detectChanges(); this.changeDetectorRef.detectChanges();
}), }),
// retrieve submission's bitstream data from state // retrieve submission's bitstream data from state
combineLatest([this.configMetadataForm$, combineLatest([
this.bitstreamService.getUploadedFilesData(this.submissionId, this.sectionData.id)]).pipe( this.configMetadataForm$,
filter(([configMetadataForm, { files }]: [SubmissionFormsModel, WorkspaceitemSectionUploadObject]) => { this.bitstreamService.getUploadedFilesData(this.submissionId, this.sectionData.id),
return isNotEmpty(configMetadataForm) && isNotEmpty(files); ]).pipe(
filter(([configMetadataForm, sectionUploadObject]: [SubmissionFormsModel, WorkspaceitemSectionUploadObject]) => {
return isNotEmpty(configMetadataForm) && isNotEmpty(sectionUploadObject);
}), }),
distinctUntilChanged()) distinctUntilChanged(),
.subscribe(([configMetadataForm, { primary, files }]: [SubmissionFormsModel, WorkspaceitemSectionUploadObject]) => { ).subscribe(([configMetadataForm, { primary, files }]: [SubmissionFormsModel, WorkspaceitemSectionUploadObject]) => {
this.primaryBitstreamUUID = primary; this.primaryBitstreamUUID = primary;
this.fileList = files; this.fileList = files;
this.fileNames = Array.from(files, file => this.getFileName(configMetadataForm, file)); this.fileNames = Array.from(files, file => this.getFileName(configMetadataForm, file));
}, this.changeDetectorRef.detectChanges();
), }),
); );
} }