mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
Merge pull request #4387 from atmire/w2p-127655_fix-submission-form-getting-stuck-in-loop_contribute-7.6
[Port dspace-7_x] Fix infinite loading submission forms
This commit is contained in:
@@ -14,6 +14,8 @@ import {
|
||||
SubmissionRequest
|
||||
} from '../data/request.models';
|
||||
import { FormFieldMetadataValueObject } from '../../shared/form/builder/models/form-field-metadata-value.model';
|
||||
import { of } from 'rxjs';
|
||||
import { RequestEntry } from '../data/request-entry.model';
|
||||
|
||||
describe('SubmissionRestService test suite', () => {
|
||||
let scheduler: TestScheduler;
|
||||
@@ -38,7 +40,9 @@ describe('SubmissionRestService test suite', () => {
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
requestService = getMockRequestService();
|
||||
requestService = getMockRequestService(of(Object.assign(new RequestEntry(), {
|
||||
request: new SubmissionRequest('mock-request-uuid', 'mock-request-href'),
|
||||
})));
|
||||
rdbService = getMockRemoteDataBuildService();
|
||||
scheduler = getTestScheduler();
|
||||
halService = new HALEndpointServiceStub(resourceEndpointURL);
|
||||
@@ -62,7 +66,7 @@ describe('SubmissionRestService test suite', () => {
|
||||
scheduler.schedule(() => service.getDataById(resourceEndpoint, resourceScope).subscribe());
|
||||
scheduler.flush();
|
||||
|
||||
expect(requestService.send).toHaveBeenCalledWith(expected);
|
||||
expect(requestService.send).toHaveBeenCalledWith(expected, false);
|
||||
});
|
||||
});
|
||||
|
||||
|
@@ -1,10 +1,10 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { Observable } from 'rxjs';
|
||||
import { distinctUntilChanged, filter, map, mergeMap, tap } from 'rxjs/operators';
|
||||
import { Observable, skipWhile } from 'rxjs';
|
||||
import { distinctUntilChanged, filter, map, mergeMap, switchMap, tap } from 'rxjs/operators';
|
||||
|
||||
import { RequestService } from '../data/request.service';
|
||||
import { hasValue, isNotEmpty } from '../../shared/empty.util';
|
||||
import { hasValue, hasValueOperator, isNotEmpty } from '../../shared/empty.util';
|
||||
import {
|
||||
DeleteRequest,
|
||||
PostRequest,
|
||||
@@ -19,11 +19,25 @@ import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { getFirstCompletedRemoteData } from '../shared/operators';
|
||||
import { URLCombiner } from '../url-combiner/url-combiner';
|
||||
import { ErrorResponse } from '../cache/response.models';
|
||||
import { RemoteData } from '../data/remote-data';
|
||||
import { SubmissionResponse } from './submission-response.model';
|
||||
import { RequestError } from '../data/request-error.model';
|
||||
import { RestRequest } from '../data/rest-request.model';
|
||||
|
||||
/**
|
||||
* Retrieve the first emitting payload's dataDefinition, or throw an error if the request failed
|
||||
*/
|
||||
export const getFirstDataDefinition = () =>
|
||||
(source: Observable<RemoteData<SubmissionResponse>>): Observable<SubmitDataResponseDefinitionObject> =>
|
||||
source.pipe(
|
||||
getFirstCompletedRemoteData(),
|
||||
map((response: RemoteData<SubmissionResponse>) => {
|
||||
if (response.hasFailed) {
|
||||
throw new Error(response.errorMessage);
|
||||
} else {
|
||||
return hasValue(response?.payload?.dataDefinition) ? response.payload.dataDefinition : [response.payload];
|
||||
}
|
||||
}),
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
|
||||
/**
|
||||
* The service handling all submission REST requests
|
||||
@@ -48,15 +62,7 @@ export class SubmissionRestService {
|
||||
*/
|
||||
protected fetchRequest(requestId: string): Observable<SubmitDataResponseDefinitionObject> {
|
||||
return this.rdbService.buildFromRequestUUID<SubmissionResponse>(requestId).pipe(
|
||||
getFirstCompletedRemoteData(),
|
||||
map((response: RemoteData<SubmissionResponse>) => {
|
||||
if (response.hasFailed) {
|
||||
throw new ErrorResponse({ statusText: response.errorMessage, statusCode: response.statusCode } as RequestError);
|
||||
} else {
|
||||
return hasValue(response.payload) ? response.payload.dataDefinition : response.payload;
|
||||
}
|
||||
}),
|
||||
distinctUntilChanged()
|
||||
getFirstDataDefinition(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -108,21 +114,52 @@ export class SubmissionRestService {
|
||||
* The endpoint link name
|
||||
* @param id
|
||||
* The submission Object to retrieve
|
||||
* @param useCachedVersionIfAvailable
|
||||
* If this is true, the request will only be sent if there's no valid & cached version. Defaults to false
|
||||
* @return Observable<SubmitDataResponseDefinitionObject>
|
||||
* server response
|
||||
*/
|
||||
public getDataById(linkName: string, id: string): Observable<SubmitDataResponseDefinitionObject> {
|
||||
const requestId = this.requestService.generateRequestId();
|
||||
public getDataById(linkName: string, id: string, useCachedVersionIfAvailable = false): Observable<SubmitDataResponseDefinitionObject> {
|
||||
return this.halService.getEndpoint(linkName).pipe(
|
||||
map((endpointURL: string) => this.getEndpointByIDHref(endpointURL, id)),
|
||||
filter((href: string) => isNotEmpty(href)),
|
||||
distinctUntilChanged(),
|
||||
map((endpointURL: string) => new SubmissionRequest(requestId, endpointURL)),
|
||||
tap((request: RestRequest) => {
|
||||
this.requestService.send(request);
|
||||
mergeMap((endpointURL: string) => {
|
||||
this.sendGetDataRequest(endpointURL, useCachedVersionIfAvailable);
|
||||
const startTime: number = new Date().getTime();
|
||||
return this.requestService.getByHref(endpointURL).pipe(
|
||||
map((requestEntry) => requestEntry?.request?.uuid),
|
||||
hasValueOperator(),
|
||||
distinctUntilChanged(),
|
||||
switchMap((requestId) => this.rdbService.buildFromRequestUUID<SubmissionResponse>(requestId)),
|
||||
// This skip ensures that if a stale object is present in the cache when you do a
|
||||
// call it isn't immediately returned, but we wait until the remote data for the new request
|
||||
// is created. If useCachedVersionIfAvailable is false it also ensures you don't get a
|
||||
// cached completed object
|
||||
skipWhile((rd: RemoteData<SubmissionResponse>) => rd.isStale || (!useCachedVersionIfAvailable && rd.lastUpdated < startTime)),
|
||||
tap((rd: RemoteData<SubmissionResponse>) => {
|
||||
if (hasValue(rd) && rd.isStale) {
|
||||
this.sendGetDataRequest(endpointURL, useCachedVersionIfAvailable);
|
||||
}
|
||||
})
|
||||
);
|
||||
}),
|
||||
mergeMap(() => this.fetchRequest(requestId)),
|
||||
distinctUntilChanged());
|
||||
getFirstDataDefinition(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a GET SubmissionRequest
|
||||
*
|
||||
* @param href
|
||||
* Endpoint URL of the submission data
|
||||
* @param useCachedVersionIfAvailable
|
||||
* If this is true, the request will only be sent if there's no valid & cached version. Defaults to false
|
||||
*/
|
||||
private sendGetDataRequest(href: string, useCachedVersionIfAvailable = false) {
|
||||
const requestId = this.requestService.generateRequestId();
|
||||
const request = new SubmissionRequest(requestId, href);
|
||||
this.requestService.send(request, useCachedVersionIfAvailable);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -27,7 +27,7 @@ import { TestScheduler } from 'rxjs/testing';
|
||||
import { SectionsService } from '../sections/sections.service';
|
||||
import { VisibilityType } from '../sections/visibility-type';
|
||||
|
||||
describe('SubmissionFormComponent Component', () => {
|
||||
describe('SubmissionFormComponent', () => {
|
||||
|
||||
let comp: SubmissionFormComponent;
|
||||
let compAsAny: any;
|
||||
@@ -197,7 +197,6 @@ describe('SubmissionFormComponent Component', () => {
|
||||
});
|
||||
scheduler.flush();
|
||||
|
||||
expect(comp.collectionId).toEqual(submissionObjectNew.collection.id);
|
||||
expect(comp.submissionDefinition).toEqual(submissionObjectNew.submissionDefinition);
|
||||
expect(comp.definitionId).toEqual(submissionObjectNew.submissionDefinition.name);
|
||||
expect(comp.sections).toEqual(submissionObjectNew.sections);
|
||||
@@ -235,7 +234,6 @@ describe('SubmissionFormComponent Component', () => {
|
||||
});
|
||||
scheduler.flush();
|
||||
|
||||
expect(comp.collectionId).toEqual('45f2f3f1-ba1f-4f36-908a-3f1ea9a557eb');
|
||||
expect(submissionServiceStub.resetSubmissionObject).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
|
@@ -250,13 +250,12 @@ export class SubmissionFormComponent implements OnChanges, OnDestroy {
|
||||
* new submission object
|
||||
*/
|
||||
onCollectionChange(submissionObject: SubmissionObject) {
|
||||
this.collectionId = (submissionObject.collection as Collection).id;
|
||||
if (this.definitionId !== (submissionObject.submissionDefinition as SubmissionDefinitionsModel).name) {
|
||||
this.sections = submissionObject.sections;
|
||||
this.submissionDefinition = (submissionObject.submissionDefinition as SubmissionDefinitionsModel);
|
||||
this.definitionId = this.submissionDefinition.name;
|
||||
this.submissionService.resetSubmissionObject(
|
||||
this.collectionId,
|
||||
(submissionObject.collection as Collection).id,
|
||||
this.submissionId,
|
||||
submissionObject._links.self.href,
|
||||
this.submissionDefinition,
|
||||
|
@@ -27,6 +27,7 @@ export class WorkflowItemPageResolver implements Resolve<RemoteData<WorkflowItem
|
||||
true,
|
||||
false,
|
||||
followLink('item'),
|
||||
followLink('collection'),
|
||||
).pipe(
|
||||
getFirstCompletedRemoteData(),
|
||||
);
|
||||
|
@@ -4,14 +4,14 @@ import { Observable } from 'rxjs';
|
||||
import { RemoteData } from '../core/data/remote-data';
|
||||
import { followLink } from '../shared/utils/follow-link-config.model';
|
||||
import { WorkspaceitemDataService } from '../core/submission/workspaceitem-data.service';
|
||||
import { WorkflowItem } from '../core/submission/models/workflowitem.model';
|
||||
import { WorkspaceItem } from '../core/submission/models/workspaceitem.model';
|
||||
import { getFirstCompletedRemoteData } from '../core/shared/operators';
|
||||
|
||||
/**
|
||||
* This class represents a resolver that requests a specific workflow item before the route is activated
|
||||
*/
|
||||
@Injectable()
|
||||
export class WorkspaceItemPageResolver implements Resolve<RemoteData<WorkflowItem>> {
|
||||
export class WorkspaceItemPageResolver implements Resolve<RemoteData<WorkspaceItem>> {
|
||||
constructor(private workspaceItemService: WorkspaceitemDataService) {
|
||||
}
|
||||
|
||||
@@ -22,11 +22,12 @@ export class WorkspaceItemPageResolver implements Resolve<RemoteData<WorkflowIte
|
||||
* @returns Observable<<RemoteData<Item>> Emits the found workflow item based on the parameters in the current route,
|
||||
* or an error if something went wrong
|
||||
*/
|
||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<RemoteData<WorkflowItem>> {
|
||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<RemoteData<WorkspaceItem>> {
|
||||
return this.workspaceItemService.findById(route.params.id,
|
||||
true,
|
||||
false,
|
||||
followLink('item'),
|
||||
followLink('collection'),
|
||||
).pipe(
|
||||
getFirstCompletedRemoteData(),
|
||||
);
|
||||
|
Reference in New Issue
Block a user