fix issue where combining entities and authority control in the same field wouldn't work

This commit is contained in:
Art Lowel
2020-09-01 14:25:50 +02:00
parent 92207cf66d
commit f167d5a629
8 changed files with 76 additions and 58 deletions

View File

@@ -81,6 +81,7 @@ export class DsDynamicLookupComponent extends DsDynamicVocabularyComponent imple
*/ */
public hasAuthorityValue() { public hasAuthorityValue() {
return hasValue(this.model.value) return hasValue(this.model.value)
&& typeof this.model.value === 'object'
&& this.model.value.hasAuthority(); && this.model.value.hasAuthority();
} }

View File

@@ -2,10 +2,19 @@ import { Injectable } from '@angular/core';
import { Actions, Effect, ofType } from '@ngrx/effects'; import { Actions, Effect, ofType } from '@ngrx/effects';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { union } from 'lodash'; import { isEqual, union } from 'lodash';
import { from as observableFrom, Observable, of as observableOf } from 'rxjs'; import { from as observableFrom, Observable, of as observableOf } from 'rxjs';
import { catchError, filter, map, mergeMap, switchMap, take, tap, withLatestFrom } from 'rxjs/operators'; import {
catchError,
filter,
map,
mergeMap,
switchMap,
take,
tap,
withLatestFrom
} from 'rxjs/operators';
import { SubmissionObject } from '../../core/submission/models/submission-object.model'; import { SubmissionObject } from '../../core/submission/models/submission-object.model';
import { WorkflowItem } from '../../core/submission/models/workflowitem.model'; import { WorkflowItem } from '../../core/submission/models/workflowitem.model';
import { WorkspaceitemSectionUploadObject } from '../../core/submission/models/workspaceitem-section-upload.model'; import { WorkspaceitemSectionUploadObject } from '../../core/submission/models/workspaceitem-section-upload.model';
@@ -49,8 +58,6 @@ import { RemoteData } from '../../core/data/remote-data';
import { getFirstSucceededRemoteDataPayload } from '../../core/shared/operators'; import { getFirstSucceededRemoteDataPayload } from '../../core/shared/operators';
import { SubmissionObjectDataService } from '../../core/submission/submission-object-data.service'; import { SubmissionObjectDataService } from '../../core/submission/submission-object-data.service';
import { followLink } from '../../shared/utils/follow-link-config.model'; 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() @Injectable()
export class SubmissionObjectEffects { export class SubmissionObjectEffects {
@@ -72,9 +79,7 @@ export class SubmissionObjectEffects {
if (sectionDefinition.sectionType !== SectionsType.SubmissionForm) { if (sectionDefinition.sectionType !== SectionsType.SubmissionForm) {
sectionData = (isNotUndefined(action.payload.sections) && isNotUndefined(action.payload.sections[sectionId])) ? action.payload.sections[sectionId] : Object.create(null); sectionData = (isNotUndefined(action.payload.sections) && isNotUndefined(action.payload.sections[sectionId])) ? action.payload.sections[sectionId] : Object.create(null);
} else { } else {
// Normalize item metadata before to init section sectionData = action.payload.item.metadata;
// TODO to review after https://github.com/DSpace/dspace-angular/issues/818 is resolved
sectionData = normalizeSectionData(action.payload.item.metadata);
} }
const sectionErrors = null; const sectionErrors = null;
mappedActions.push( mappedActions.push(
@@ -253,7 +258,7 @@ export class SubmissionObjectEffects {
@Effect() addAllMetadataToSectionData = this.actions$.pipe( @Effect() addAllMetadataToSectionData = this.actions$.pipe(
ofType(SubmissionObjectActionTypes.UPDATE_SECTION_DATA), ofType(SubmissionObjectActionTypes.UPDATE_SECTION_DATA),
switchMap((action: UpdateSectionDataAction) => { switchMap((action: UpdateSectionDataAction) => {
return this.sectionService.getSectionState(action.payload.submissionId, action.payload.sectionId) return this.sectionService.getSectionState(action.payload.submissionId, action.payload.sectionId, SectionsType.Upload)
.pipe(map((section: SubmissionSectionObject) => [action, section]), take(1)); .pipe(map((section: SubmissionSectionObject) => [action, section]), take(1));
}), }),
filter(([action, section]: [UpdateSectionDataAction, SubmissionSectionObject]) => section.sectionType === SectionsType.SubmissionForm), filter(([action, section]: [UpdateSectionDataAction, SubmissionSectionObject]) => section.sectionType === SectionsType.SubmissionForm),
@@ -271,15 +276,8 @@ export class SubmissionObjectEffects {
return item$.pipe( return item$.pipe(
map((item: Item) => item.metadata), map((item: Item) => item.metadata),
map((metadata: any) => { filter((metadata) => !isEqual(action.payload.data, metadata)),
if (!this.isEqual(action.payload.data, normalizeSectionData(metadata))) { map((metadata: any) => new UpdateSectionDataAction(action.payload.submissionId, action.payload.sectionId, metadata, action.payload.errors))
// Normalize item metadata before to update section
// TODO to review after https://github.com/DSpace/dspace-angular/issues/818 is resolved
return new UpdateSectionDataAction(action.payload.submissionId, action.payload.sectionId, normalizeSectionData(metadata), action.payload.errors)
} else {
return new UpdateSectionDataSuccessAction();
}
})
); );
} else { } else {
return observableOf(new UpdateSectionDataSuccessAction()); return observableOf(new UpdateSectionDataSuccessAction());
@@ -397,31 +395,4 @@ export class SubmissionObjectEffects {
} }
return mappedActions; 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);
}
} }

View File

@@ -1,6 +1,10 @@
import { Component, Inject } from '@angular/core'; import { Component, Inject } from '@angular/core';
import { Observable, of as observableOf, Subscription } from 'rxjs'; import { Observable, of as observableOf, Subscription } from 'rxjs';
import { Field, Option, SubmissionCcLicence } from '../../../core/submission/models/submission-cc-license.model'; import {
Field,
Option,
SubmissionCcLicence
} from '../../../core/submission/models/submission-cc-license.model';
import { getRemoteDataPayload, getSucceededRemoteData } from '../../../core/shared/operators'; import { getRemoteDataPayload, getSucceededRemoteData } from '../../../core/shared/operators';
import { distinctUntilChanged, filter, map, take } from 'rxjs/operators'; import { distinctUntilChanged, filter, map, take } from 'rxjs/operators';
import { SubmissionCcLicenseDataService } from '../../../core/submission/submission-cc-license-data.service'; import { SubmissionCcLicenseDataService } from '../../../core/submission/submission-cc-license-data.service';
@@ -228,7 +232,7 @@ export class SubmissionSectionCcLicensesComponent extends SectionModelComponent
onSectionInit(): void { onSectionInit(): void {
this.pathCombiner = new JsonPatchOperationPathCombiner('sections', this.sectionData.id); this.pathCombiner = new JsonPatchOperationPathCombiner('sections', this.sectionData.id);
this.subscriptions.push( this.subscriptions.push(
this.sectionService.getSectionState(this.submissionId, this.sectionData.id).pipe( this.sectionService.getSectionState(this.submissionId, this.sectionData.id, SectionsType.CcLicense).pipe(
filter((sectionState) => { filter((sectionState) => {
return isNotEmpty(sectionState) && (isNotEmpty(sectionState.data) || isNotEmpty(sectionState.errors)) return isNotEmpty(sectionState) && (isNotEmpty(sectionState.data) || isNotEmpty(sectionState.errors))
}), }),

View File

@@ -3,6 +3,7 @@
[ngClass]="{ 'section-focus' : sectionRef.isSectionActive() }" [ngClass]="{ 'section-focus' : sectionRef.isSectionActive() }"
[mandatory]="sectionData.mandatory" [mandatory]="sectionData.mandatory"
[submissionId]="submissionId" [submissionId]="submissionId"
[sectionType]="sectionData.sectionType"
[sectionId]="sectionData.id"> [sectionId]="sectionData.id">
<ngb-accordion #acc="ngbAccordion" <ngb-accordion #acc="ngbAccordion"
*ngIf="(sectionRef.isEnabled() | async)" *ngIf="(sectionRef.isEnabled() | async)"

View File

@@ -160,7 +160,7 @@ export class SubmissionSectionformComponent extends SectionModelComponent {
tap((config: SubmissionFormsModel) => this.formConfig = config), tap((config: SubmissionFormsModel) => this.formConfig = config),
flatMap(() => flatMap(() =>
observableCombineLatest( observableCombineLatest(
this.sectionService.getSectionData(this.submissionId, this.sectionData.id), this.sectionService.getSectionData(this.submissionId, this.sectionData.id, this.sectionData.sectionType),
this.submissionObjectService.getHrefByID(this.submissionId).pipe(take(1)).pipe( this.submissionObjectService.getHrefByID(this.submissionId).pipe(take(1)).pipe(
switchMap((href: string) => { switchMap((href: string) => {
this.objectCache.remove(href); this.objectCache.remove(href);
@@ -318,7 +318,7 @@ export class SubmissionSectionformComponent extends SectionModelComponent {
/** /**
* Subscribe to section state * Subscribe to section state
*/ */
this.sectionService.getSectionState(this.submissionId, this.sectionData.id).pipe( this.sectionService.getSectionState(this.submissionId, this.sectionData.id, this.sectionData.sectionType).pipe(
filter((sectionState: SubmissionSectionObject) => { filter((sectionState: SubmissionSectionObject) => {
return isNotEmpty(sectionState) && (isNotEmpty(sectionState.data) || isNotEmpty(sectionState.errors)) return isNotEmpty(sectionState) && (isNotEmpty(sectionState.data) || isNotEmpty(sectionState.errors))
}), }),

View File

@@ -9,6 +9,7 @@ import { hasValue, isNotEmpty, isNotNull } from '../../shared/empty.util';
import { SubmissionSectionError, SubmissionSectionObject } from '../objects/submission-objects.reducer'; import { SubmissionSectionError, SubmissionSectionObject } from '../objects/submission-objects.reducer';
import parseSectionErrorPaths, { SectionErrorPath } from '../utils/parseSectionErrorPaths'; import parseSectionErrorPaths, { SectionErrorPath } from '../utils/parseSectionErrorPaths';
import { SubmissionService } from '../submission.service'; import { SubmissionService } from '../submission.service';
import { SectionsType } from './sections-type';
/** /**
* Directive for handling generic section functionality * Directive for handling generic section functionality
@@ -31,6 +32,12 @@ export class SectionsDirective implements OnDestroy, OnInit {
*/ */
@Input() sectionId: string; @Input() sectionId: string;
/**
* The section type
* @type {SectionsType}
*/
@Input() sectionType: SectionsType;
/** /**
* The submission id * The submission id
* @type {string} * @type {string}
@@ -104,7 +111,7 @@ export class SectionsDirective implements OnDestroy, OnInit {
})); }));
this.subs.push( this.subs.push(
this.sectionService.getSectionState(this.submissionId, this.sectionId).pipe( this.sectionService.getSectionState(this.submissionId, this.sectionId, this.sectionType).pipe(
map((state: SubmissionSectionObject) => state.errors)) map((state: SubmissionSectionObject) => state.errors))
.subscribe((errors: SubmissionSectionError[]) => { .subscribe((errors: SubmissionSectionError[]) => {
if (isNotEmpty(errors)) { if (isNotEmpty(errors)) {

View File

@@ -14,7 +14,11 @@ import { NotificationsServiceStub } from '../../shared/testing/notifications-ser
import { SubmissionServiceStub } from '../../shared/testing/submission-service.stub'; import { SubmissionServiceStub } from '../../shared/testing/submission-service.stub';
import { getMockTranslateService } from '../../shared/mocks/translate.service.mock'; import { getMockTranslateService } from '../../shared/mocks/translate.service.mock';
import { SectionsService } from './sections.service'; import { SectionsService } from './sections.service';
import { mockSectionsData, mockSectionsErrors, mockSubmissionState } from '../../shared/mocks/submission.mock'; import {
mockSectionsData,
mockSectionsErrors,
mockSubmissionState
} from '../../shared/mocks/submission.mock';
import { import {
DisableSectionAction, DisableSectionAction,
EnableSectionAction, EnableSectionAction,
@@ -23,12 +27,17 @@ import {
SectionStatusChangeAction, SectionStatusChangeAction,
UpdateSectionDataAction UpdateSectionDataAction
} from '../objects/submission-objects.actions'; } from '../objects/submission-objects.actions';
import { FormAddError, FormClearErrorsAction, FormRemoveErrorAction } from '../../shared/form/form.actions'; import {
FormAddError,
FormClearErrorsAction,
FormRemoveErrorAction
} from '../../shared/form/form.actions';
import parseSectionErrors from '../utils/parseSectionErrors'; import parseSectionErrors from '../utils/parseSectionErrors';
import { SubmissionScopeType } from '../../core/submission/submission-scope-type'; import { SubmissionScopeType } from '../../core/submission/submission-scope-type';
import { SubmissionSectionError } from '../objects/submission-objects.reducer'; import { SubmissionSectionError } from '../objects/submission-objects.reducer';
import { getMockScrollToService } from '../../shared/mocks/scroll-to-service.mock'; import { getMockScrollToService } from '../../shared/mocks/scroll-to-service.mock';
import { storeModuleConfig } from '../../app.reducer'; import { storeModuleConfig } from '../../app.reducer';
import { SectionsType } from './sections-type';
describe('SectionsService test suite', () => { describe('SectionsService test suite', () => {
let notificationsServiceStub: NotificationsServiceStub; let notificationsServiceStub: NotificationsServiceStub;
@@ -151,7 +160,7 @@ describe('SectionsService test suite', () => {
b: sectionData[sectionId] b: sectionData[sectionId]
}); });
expect(service.getSectionData(submissionId, sectionId)).toBeObservable(expected); expect(service.getSectionData(submissionId, sectionId, SectionsType.SubmissionForm)).toBeObservable(expected);
}); });
}); });
@@ -175,7 +184,7 @@ describe('SectionsService test suite', () => {
b: sectionState b: sectionState
}); });
expect(service.getSectionState(submissionId, sectionId)).toBeObservable(expected); expect(service.getSectionState(submissionId, sectionId, SectionsType.SubmissionForm)).toBeObservable(expected);
}); });
}); });

View File

@@ -1,7 +1,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { combineLatest, Observable } from 'rxjs'; import { combineLatest, Observable } from 'rxjs';
import { distinctUntilChanged, filter, map, take } from 'rxjs/operators'; import { distinctUntilChanged, filter, map, take, tap } from 'rxjs/operators';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { ScrollToConfigOptions, ScrollToService } from '@nicky-lenaers/ngx-scroll-to'; import { ScrollToConfigOptions, ScrollToService } from '@nicky-lenaers/ngx-scroll-to';
@@ -25,6 +25,8 @@ import { FormAddError, FormClearErrorsAction, FormRemoveErrorAction } from '../.
import { NotificationsService } from '../../shared/notifications/notifications.service'; import { NotificationsService } from '../../shared/notifications/notifications.service';
import { SubmissionService } from '../submission.service'; import { SubmissionService } from '../submission.service';
import { WorkspaceitemSectionDataType } from '../../core/submission/models/workspaceitem-sections.model'; import { WorkspaceitemSectionDataType } from '../../core/submission/models/workspaceitem-sections.model';
import { SectionsType } from './sections-type';
import { normalizeSectionData } from '../../core/submission/submission-response-parsing.service';
/** /**
* A service that provides methods used in submission process. * A service that provides methods used in submission process.
@@ -129,12 +131,23 @@ export class SectionsService {
* The submission id * The submission id
* @param sectionId * @param sectionId
* The section id * The section id
* @param sectionType
* The type of section to retrieve
* @return Observable<WorkspaceitemSectionDataType> * @return Observable<WorkspaceitemSectionDataType>
* observable of [WorkspaceitemSectionDataType] * observable of [WorkspaceitemSectionDataType]
*/ */
public getSectionData(submissionId: string, sectionId: string): Observable<WorkspaceitemSectionDataType> { public getSectionData(submissionId: string, sectionId: string, sectionType: SectionsType): Observable<WorkspaceitemSectionDataType> {
return this.store.select(submissionSectionDataFromIdSelector(submissionId, sectionId)).pipe( return this.store.select(submissionSectionDataFromIdSelector(submissionId, sectionId)).pipe(
distinctUntilChanged()); map((sectionData: WorkspaceitemSectionDataType) => {
if (sectionType === SectionsType.SubmissionForm) {
return normalizeSectionData(sectionData)
}
else {
return sectionData;
}
}),
distinctUntilChanged(),
);
} }
/** /**
@@ -159,14 +172,26 @@ export class SectionsService {
* The submission id * The submission id
* @param sectionId * @param sectionId
* The section id * The section id
* @param sectionType
* The type of section to retrieve
* @return Observable<SubmissionSectionObject> * @return Observable<SubmissionSectionObject>
* observable of [SubmissionSectionObject] * observable of [SubmissionSectionObject]
*/ */
public getSectionState(submissionId: string, sectionId: string): Observable<SubmissionSectionObject> { public getSectionState(submissionId: string, sectionId: string, sectionType: SectionsType): Observable<SubmissionSectionObject> {
return this.store.select(submissionSectionFromIdSelector(submissionId, sectionId)).pipe( return this.store.select(submissionSectionFromIdSelector(submissionId, sectionId)).pipe(
filter((sectionObj: SubmissionSectionObject) => hasValue(sectionObj)), filter((sectionObj: SubmissionSectionObject) => hasValue(sectionObj)),
map((sectionObj: SubmissionSectionObject) => sectionObj), map((sectionObj: SubmissionSectionObject) => sectionObj),
distinctUntilChanged(), map((sectionState: SubmissionSectionObject) => {
if (hasValue(sectionState.data) && sectionType === SectionsType.SubmissionForm) {
return Object.assign({}, sectionState, {
data: normalizeSectionData(sectionState.data)
})
}
else {
return sectionState;
}
}),
distinctUntilChanged()
); );
} }