From 090fb94c4da57df2dbe415f520d5cd0e4ae32f85 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 30 Jul 2020 18:50:47 +0200 Subject: [PATCH] Normalized submission section data when retrieving them from item object --- ...ynamic-form-control-container.component.ts | 5 +- .../models/ds-dynamic-input.model.ts | 12 ++- .../objects/submission-objects.actions.ts | 9 ++- .../submission-objects.effects.spec.ts | 2 +- .../objects/submission-objects.effects.ts | 74 ++++++++++++++----- .../objects/submission-objects.reducer.ts | 2 +- 6 files changed, 76 insertions(+), 28 deletions(-) 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 edc6efc5f9..7873f3a86e 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 @@ -116,6 +116,7 @@ import { followLink } from '../../../utils/follow-link-config.model'; import { paginatedRelationsToItems } from '../../../../+item-page/simple/item-types/shared/item-relationships-utils'; import { RelationshipOptions } from '../models/relationship-options.model'; import { FormBuilderService } from '../form-builder.service'; +import { FormFieldMetadataValueObject } from '../models/form-field-metadata-value.model'; export function dsDynamicFormControlMapFn(model: DynamicFormControlModel): Type | null { switch (model.type) { @@ -303,9 +304,9 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo } if (hasValue(this.model.metadataValue)) { - this.value = Object.assign(new MetadataValue(), this.model.metadataValue); + this.value = Object.assign(new FormFieldMetadataValueObject(), this.model.metadataValue); } else { - this.value = Object.assign(new MetadataValue(), this.model.value); + this.value = Object.assign(new FormFieldMetadataValueObject(), this.model.value); } if (hasValue(this.value) && this.value.isVirtual) { diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-input.model.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-input.model.ts index c91a0aea54..290e29dc65 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-input.model.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-input.model.ts @@ -1,4 +1,9 @@ -import { DynamicFormControlLayout, DynamicInputModel, DynamicInputModelConfig, serializable } from '@ng-dynamic-forms/core'; +import { + DynamicFormControlLayout, + DynamicInputModel, + DynamicInputModelConfig, + serializable +} from '@ng-dynamic-forms/core'; import { Subject } from 'rxjs'; import { LanguageCode } from '../../models/form-field-language-value.model'; @@ -6,7 +11,6 @@ import { VocabularyOptions } from '../../../../../core/submission/vocabularies/m import { hasValue } from '../../../../empty.util'; import { FormFieldMetadataValueObject } from '../../models/form-field-metadata-value.model'; import { RelationshipOptions } from '../../models/relationship-options.model'; -import { MetadataValue } from '../../../../../core/shared/metadata.models'; export interface DsDynamicInputModelConfig extends DynamicInputModelConfig { vocabularyOptions?: VocabularyOptions; @@ -19,7 +23,7 @@ export interface DsDynamicInputModelConfig extends DynamicInputModelConfig { metadataFields: string[]; submissionId: string; hasSelectableMetadata: boolean; - metadataValue?: MetadataValue; + metadataValue?: FormFieldMetadataValueObject; } @@ -34,7 +38,7 @@ export class DsDynamicInputModel extends DynamicInputModel { @serializable() metadataFields: string[]; @serializable() submissionId: string; @serializable() hasSelectableMetadata: boolean; - @serializable() metadataValue: MetadataValue; + @serializable() metadataValue: FormFieldMetadataValueObject; constructor(config: DsDynamicInputModelConfig, layout?: DynamicFormControlLayout) { super(config, layout); diff --git a/src/app/submission/objects/submission-objects.actions.ts b/src/app/submission/objects/submission-objects.actions.ts index 1e3e44aba9..73c070846c 100644 --- a/src/app/submission/objects/submission-objects.actions.ts +++ b/src/app/submission/objects/submission-objects.actions.ts @@ -42,7 +42,8 @@ export const SubmissionObjectActionTypes = { DISABLE_SECTION: type('dspace/submission/DISABLE_SECTION'), SECTION_STATUS_CHANGE: type('dspace/submission/SECTION_STATUS_CHANGE'), SECTION_LOADING_STATUS_CHANGE: type('dspace/submission/SECTION_LOADING_STATUS_CHANGE'), - UPLOAD_SECTION_DATA: type('dspace/submission/UPLOAD_SECTION_DATA'), + UPDATE_SECTION_DATA: type('dspace/submission/UPDATE_SECTION_DATA'), + UPDATE_SECTION_DATA_SUCCESS: type('dspace/submission/UPDATE_SECTION_DATA_SUCCESS'), SAVE_AND_DEPOSIT_SUBMISSION: type('dspace/submission/SAVE_AND_DEPOSIT_SUBMISSION'), DEPOSIT_SUBMISSION: type('dspace/submission/DEPOSIT_SUBMISSION'), DEPOSIT_SUBMISSION_SUCCESS: type('dspace/submission/DEPOSIT_SUBMISSION_SUCCESS'), @@ -199,7 +200,7 @@ export class DisableSectionAction implements Action { } export class UpdateSectionDataAction implements Action { - type = SubmissionObjectActionTypes.UPLOAD_SECTION_DATA; + type = SubmissionObjectActionTypes.UPDATE_SECTION_DATA; payload: { submissionId: string; sectionId: string; @@ -227,6 +228,10 @@ export class UpdateSectionDataAction implements Action { } } +export class UpdateSectionDataSuccessAction implements Action { + type = SubmissionObjectActionTypes.UPDATE_SECTION_DATA_SUCCESS; +} + export class RemoveSectionErrorsAction implements Action { type = SubmissionObjectActionTypes.REMOVE_SECTION_ERRORS; payload: { diff --git a/src/app/submission/objects/submission-objects.effects.spec.ts b/src/app/submission/objects/submission-objects.effects.spec.ts index 6c2e9eefc6..c35968c0a0 100644 --- a/src/app/submission/objects/submission-objects.effects.spec.ts +++ b/src/app/submission/objects/submission-objects.effects.spec.ts @@ -276,7 +276,7 @@ describe('SubmissionObjectEffects test suite', () => { describe('saveSubmissionSuccess$', () => { - it('should return a UPLOAD_SECTION_DATA action for each updated section', () => { + it('should return a UPDATE_SECTION_DATA action for each updated section', () => { store.nextState({ submission: { objects: submissionState diff --git a/src/app/submission/objects/submission-objects.effects.ts b/src/app/submission/objects/submission-objects.effects.ts index 2dfed9ee47..ecda75759e 100644 --- a/src/app/submission/objects/submission-objects.effects.ts +++ b/src/app/submission/objects/submission-objects.effects.ts @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'; import { Actions, Effect, ofType } from '@ngrx/effects'; import { Store } from '@ngrx/store'; import { TranslateService } from '@ngx-translate/core'; -import { isEqual, union } from 'lodash'; +import { union } from 'lodash'; import { from as observableFrom, Observable, of as observableOf } from 'rxjs'; import { catchError, filter, map, mergeMap, switchMap, take, tap, withLatestFrom } from 'rxjs/operators'; @@ -40,7 +40,8 @@ import { SaveSubmissionSectionFormSuccessAction, SubmissionObjectAction, SubmissionObjectActionTypes, - UpdateSectionDataAction + UpdateSectionDataAction, + UpdateSectionDataSuccessAction } from './submission-objects.actions'; import { SubmissionObjectEntry, SubmissionSectionObject } from './submission-objects.reducer'; import { Item } from '../../core/shared/item.model'; @@ -48,6 +49,8 @@ import { RemoteData } from '../../core/data/remote-data'; import { getFirstSucceededRemoteDataPayload } from '../../core/shared/operators'; import { SubmissionObjectDataService } from '../../core/submission/submission-object-data.service'; import { followLink } from '../../shared/utils/follow-link-config.model'; +import { normalizeSectionData } from '../../core/submission/submission-response-parsing.service'; +import { difference } from '../../shared/object.util'; @Injectable() export class SubmissionObjectEffects { @@ -69,7 +72,7 @@ export class SubmissionObjectEffects { if (sectionDefinition.sectionType !== SectionsType.SubmissionForm) { sectionData = (isNotUndefined(action.payload.sections) && isNotUndefined(action.payload.sections[sectionId])) ? action.payload.sections[sectionId] : Object.create(null); } else { - sectionData = action.payload.item.metadata; + sectionData = normalizeSectionData(action.payload.item.metadata); } const sectionErrors = null; mappedActions.push( @@ -246,28 +249,37 @@ export class SubmissionObjectEffects { * Adds all metadata an item to the SubmissionForm sections of the submission */ @Effect() addAllMetadataToSectionData = this.actions$.pipe( - ofType(SubmissionObjectActionTypes.UPLOAD_SECTION_DATA), + ofType(SubmissionObjectActionTypes.UPDATE_SECTION_DATA), switchMap((action: UpdateSectionDataAction) => { return this.sectionService.getSectionState(action.payload.submissionId, action.payload.sectionId) .pipe(map((section: SubmissionSectionObject) => [action, section]), take(1)); }), filter(([action, section]: [UpdateSectionDataAction, SubmissionSectionObject]) => section.sectionType === SectionsType.SubmissionForm), switchMap(([action, section]: [UpdateSectionDataAction, SubmissionSectionObject]) => { - const submissionObject$ = this.submissionObjectService - .findById(action.payload.submissionId, followLink('item')).pipe( - getFirstSucceededRemoteDataPayload() + if (section.sectionType === SectionsType.SubmissionForm) { + const submissionObject$ = this.submissionObjectService + .findById(action.payload.submissionId, followLink('item')).pipe( + getFirstSucceededRemoteDataPayload() + ); + + const item$ = submissionObject$.pipe( + switchMap((submissionObject: SubmissionObject) => (submissionObject.item as Observable>).pipe( + getFirstSucceededRemoteDataPayload(), + ))); + + return item$.pipe( + map((item: Item) => item.metadata), + map((metadata: any) => { + if (!this.isEqual(action.payload.data, normalizeSectionData(metadata))) { + return new UpdateSectionDataAction(action.payload.submissionId, action.payload.sectionId, normalizeSectionData(metadata), action.payload.errors) + } else { + return new UpdateSectionDataSuccessAction(); + } + }) ); - - const item$ = submissionObject$.pipe( - switchMap((submissionObject: SubmissionObject) => (submissionObject.item as Observable>).pipe( - getFirstSucceededRemoteDataPayload(), - ))); - - return item$.pipe( - map((item: Item) => item.metadata), - filter((metadata) => !isEqual(action.payload.data, metadata)), - map((metadata: any) => new UpdateSectionDataAction(action.payload.submissionId, action.payload.sectionId, metadata, action.payload.errors)) - ); + } else { + return observableOf(new UpdateSectionDataSuccessAction()); + } }), ); @@ -382,4 +394,30 @@ export class SubmissionObjectEffects { return mappedActions; } + /** + * Check if the section data has been enriched by the server + * + * @param sectionData + * the section metadata retrieved from the server + * @param itemData + * the item data retrieved from the server + */ + isEqual(sectionData: any, itemData: any): boolean { + const diffResult = []; + + // compare current form data state with section data retrieved from store + const diffObj = difference(sectionData, itemData); + + // iterate over differences to check whether they are actually different + Object.keys(diffObj) + .forEach((key) => { + diffObj[key].forEach((value) => { + if (value.hasOwnProperty('value')) { + diffResult.push(value); + } + }); + }); + return isEmpty(diffResult); + } + } diff --git a/src/app/submission/objects/submission-objects.reducer.ts b/src/app/submission/objects/submission-objects.reducer.ts index e0aeefd7b6..098160c737 100644 --- a/src/app/submission/objects/submission-objects.reducer.ts +++ b/src/app/submission/objects/submission-objects.reducer.ts @@ -262,7 +262,7 @@ export function submissionObjectReducer(state = initialState, action: Submission return changeSectionState(state, action as EnableSectionAction, true); } - case SubmissionObjectActionTypes.UPLOAD_SECTION_DATA: { + case SubmissionObjectActionTypes.UPDATE_SECTION_DATA: { return updateSectionData(state, action as UpdateSectionDataAction); }