Skip to content

Commit 83d5400

Browse files
authored
Merge pull request #4388 from atmire/backport-4060-submission-form-getting-stuck-in-loop-contribute-8
[Port dspace-8_x] Fix infinite loading submission forms
2 parents 7c45c78 + c38711d commit 83d5400

File tree

6 files changed

+74
-31
lines changed

6 files changed

+74
-31
lines changed

src/app/core/submission/submission-rest.service.spec.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { getTestScheduler } from 'jasmine-marbles';
2+
import { of } from 'rxjs';
23
import { TestScheduler } from 'rxjs/testing';
34

45
import { FormFieldMetadataValueObject } from '../../shared/form/builder/models/form-field-metadata-value.model';
@@ -13,6 +14,7 @@ import {
1314
SubmissionRequest,
1415
} from '../data/request.models';
1516
import { RequestService } from '../data/request.service';
17+
import { RequestEntry } from '../data/request-entry.model';
1618
import { SubmissionRestService } from './submission-rest.service';
1719

1820
describe('SubmissionRestService test suite', () => {
@@ -38,7 +40,9 @@ describe('SubmissionRestService test suite', () => {
3840
}
3941

4042
beforeEach(() => {
41-
requestService = getMockRequestService();
43+
requestService = getMockRequestService(of(Object.assign(new RequestEntry(), {
44+
request: new SubmissionRequest('mock-request-uuid', 'mock-request-href'),
45+
})));
4246
rdbService = getMockRemoteDataBuildService();
4347
scheduler = getTestScheduler();
4448
halService = new HALEndpointServiceStub(resourceEndpointURL);
@@ -62,7 +66,7 @@ describe('SubmissionRestService test suite', () => {
6266
scheduler.schedule(() => service.getDataById(resourceEndpoint, resourceScope).subscribe());
6367
scheduler.flush();
6468

65-
expect(requestService.send).toHaveBeenCalledWith(expected);
69+
expect(requestService.send).toHaveBeenCalledWith(expected, false);
6670
});
6771
});
6872

src/app/core/submission/submission-rest.service.ts

Lines changed: 62 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
11
import { Injectable } from '@angular/core';
2-
import { Observable } from 'rxjs';
2+
import {
3+
Observable,
4+
skipWhile,
5+
} from 'rxjs';
36
import {
47
distinctUntilChanged,
58
filter,
69
map,
710
mergeMap,
11+
switchMap,
812
tap,
913
} from 'rxjs/operators';
1014

1115
import {
1216
hasValue,
17+
hasValueOperator,
1318
isNotEmpty,
1419
} from '../../shared/empty.util';
1520
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
16-
import { ErrorResponse } from '../cache/response.models';
1721
import { RemoteData } from '../data/remote-data';
1822
import {
1923
DeleteRequest,
@@ -24,15 +28,30 @@ import {
2428
SubmissionRequest,
2529
} from '../data/request.models';
2630
import { RequestService } from '../data/request.service';
27-
import { RequestError } from '../data/request-error.model';
28-
import { RestRequest } from '../data/rest-request.model';
2931
import { HttpOptions } from '../dspace-rest/dspace-rest.service';
3032
import { HALEndpointService } from '../shared/hal-endpoint.service';
3133
import { getFirstCompletedRemoteData } from '../shared/operators';
3234
import { SubmitDataResponseDefinitionObject } from '../shared/submit-data-response-definition.model';
3335
import { URLCombiner } from '../url-combiner/url-combiner';
3436
import { SubmissionResponse } from './submission-response.model';
3537

38+
/**
39+
* Retrieve the first emitting payload's dataDefinition, or throw an error if the request failed
40+
*/
41+
export const getFirstDataDefinition = () =>
42+
(source: Observable<RemoteData<SubmissionResponse>>): Observable<SubmitDataResponseDefinitionObject> =>
43+
source.pipe(
44+
getFirstCompletedRemoteData(),
45+
map((response: RemoteData<SubmissionResponse>) => {
46+
if (response.hasFailed) {
47+
throw new Error(response.errorMessage);
48+
} else {
49+
return hasValue(response?.payload?.dataDefinition) ? response.payload.dataDefinition : [response.payload];
50+
}
51+
}),
52+
distinctUntilChanged(),
53+
);
54+
3655
/**
3756
* The service handling all submission REST requests
3857
*/
@@ -56,15 +75,7 @@ export class SubmissionRestService {
5675
*/
5776
protected fetchRequest(requestId: string): Observable<SubmitDataResponseDefinitionObject> {
5877
return this.rdbService.buildFromRequestUUID<SubmissionResponse>(requestId).pipe(
59-
getFirstCompletedRemoteData(),
60-
map((response: RemoteData<SubmissionResponse>) => {
61-
if (response.hasFailed) {
62-
throw new ErrorResponse({ statusText: response.errorMessage, statusCode: response.statusCode } as RequestError);
63-
} else {
64-
return hasValue(response.payload) ? response.payload.dataDefinition : response.payload;
65-
}
66-
}),
67-
distinctUntilChanged(),
78+
getFirstDataDefinition(),
6879
);
6980
}
7081

@@ -116,21 +127,52 @@ export class SubmissionRestService {
116127
* The endpoint link name
117128
* @param id
118129
* The submission Object to retrieve
130+
* @param useCachedVersionIfAvailable
131+
* If this is true, the request will only be sent if there's no valid & cached version. Defaults to false
119132
* @return Observable<SubmitDataResponseDefinitionObject>
120133
* server response
121134
*/
122-
public getDataById(linkName: string, id: string): Observable<SubmitDataResponseDefinitionObject> {
123-
const requestId = this.requestService.generateRequestId();
135+
public getDataById(linkName: string, id: string, useCachedVersionIfAvailable = false): Observable<SubmitDataResponseDefinitionObject> {
124136
return this.halService.getEndpoint(linkName).pipe(
125137
map((endpointURL: string) => this.getEndpointByIDHref(endpointURL, id)),
126138
filter((href: string) => isNotEmpty(href)),
127139
distinctUntilChanged(),
128-
map((endpointURL: string) => new SubmissionRequest(requestId, endpointURL)),
129-
tap((request: RestRequest) => {
130-
this.requestService.send(request);
140+
mergeMap((endpointURL: string) => {
141+
this.sendGetDataRequest(endpointURL, useCachedVersionIfAvailable);
142+
const startTime: number = new Date().getTime();
143+
return this.requestService.getByHref(endpointURL).pipe(
144+
map((requestEntry) => requestEntry?.request?.uuid),
145+
hasValueOperator(),
146+
distinctUntilChanged(),
147+
switchMap((requestId) => this.rdbService.buildFromRequestUUID<SubmissionResponse>(requestId)),
148+
// This skip ensures that if a stale object is present in the cache when you do a
149+
// call it isn't immediately returned, but we wait until the remote data for the new request
150+
// is created. If useCachedVersionIfAvailable is false it also ensures you don't get a
151+
// cached completed object
152+
skipWhile((rd: RemoteData<SubmissionResponse>) => rd.isStale || (!useCachedVersionIfAvailable && rd.lastUpdated < startTime)),
153+
tap((rd: RemoteData<SubmissionResponse>) => {
154+
if (hasValue(rd) && rd.isStale) {
155+
this.sendGetDataRequest(endpointURL, useCachedVersionIfAvailable);
156+
}
157+
}),
158+
);
131159
}),
132-
mergeMap(() => this.fetchRequest(requestId)),
133-
distinctUntilChanged());
160+
getFirstDataDefinition(),
161+
);
162+
}
163+
164+
/**
165+
* Send a GET SubmissionRequest
166+
*
167+
* @param href
168+
* Endpoint URL of the submission data
169+
* @param useCachedVersionIfAvailable
170+
* If this is true, the request will only be sent if there's no valid & cached version. Defaults to false
171+
*/
172+
private sendGetDataRequest(href: string, useCachedVersionIfAvailable = false) {
173+
const requestId = this.requestService.generateRequestId();
174+
const request = new SubmissionRequest(requestId, href);
175+
this.requestService.send(request, useCachedVersionIfAvailable);
134176
}
135177

136178
/**

src/app/submission/form/submission-form.component.spec.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ import { SubmissionFormSectionAddComponent } from './section-add/submission-form
4747
import { SubmissionFormComponent } from './submission-form.component';
4848
import { ThemedSubmissionUploadFilesComponent } from './submission-upload-files/themed-submission-upload-files.component';
4949

50-
describe('SubmissionFormComponent Component', () => {
50+
describe('SubmissionFormComponent', () => {
5151

5252
let comp: SubmissionFormComponent;
5353
let compAsAny: any;
@@ -227,7 +227,6 @@ describe('SubmissionFormComponent Component', () => {
227227
});
228228
scheduler.flush();
229229

230-
expect(comp.collectionId).toEqual(submissionObjectNew.collection.id);
231230
expect(comp.submissionDefinition).toEqual(submissionObjectNew.submissionDefinition);
232231
expect(comp.definitionId).toEqual(submissionObjectNew.submissionDefinition.name);
233232
expect(comp.sections).toEqual(submissionObjectNew.sections);
@@ -265,7 +264,6 @@ describe('SubmissionFormComponent Component', () => {
265264
});
266265
scheduler.flush();
267266

268-
expect(comp.collectionId).toEqual('45f2f3f1-ba1f-4f36-908a-3f1ea9a557eb');
269267
expect(submissionServiceStub.resetSubmissionObject).not.toHaveBeenCalled();
270268
done();
271269
});

src/app/submission/form/submission-form.component.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -286,13 +286,12 @@ export class SubmissionFormComponent implements OnChanges, OnDestroy {
286286
* new submission object
287287
*/
288288
onCollectionChange(submissionObject: SubmissionObject) {
289-
this.collectionId = (submissionObject.collection as Collection).id;
290289
if (this.definitionId !== (submissionObject.submissionDefinition as SubmissionDefinitionsModel).name) {
291290
this.sections = submissionObject.sections;
292291
this.submissionDefinition = (submissionObject.submissionDefinition as SubmissionDefinitionsModel);
293292
this.definitionId = this.submissionDefinition.name;
294293
this.submissionService.resetSubmissionObject(
295-
this.collectionId,
294+
(submissionObject.collection as Collection).id,
296295
this.submissionId,
297296
submissionObject._links.self.href,
298297
this.submissionDefinition,

src/app/workflowitems-edit-page/workflow-item-page.resolver.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import { Observable } from 'rxjs';
99
import { RemoteData } from '../core/data/remote-data';
1010
import { getFirstCompletedRemoteData } from '../core/shared/operators';
1111
import { WorkflowItem } from '../core/submission/models/workflowitem.model';
12+
import { SUBMISSION_LINKS_TO_FOLLOW } from '../core/submission/resolver/submission-links-to-follow';
1213
import { WorkflowItemDataService } from '../core/submission/workflowitem-data.service';
13-
import { followLink } from '../shared/utils/follow-link-config.model';
1414

1515
export const workflowItemPageResolver: ResolveFn<RemoteData<WorkflowItem>> = (
1616
route: ActivatedRouteSnapshot,
@@ -21,7 +21,7 @@ export const workflowItemPageResolver: ResolveFn<RemoteData<WorkflowItem>> = (
2121
route.params.id,
2222
true,
2323
false,
24-
followLink('item'),
24+
...SUBMISSION_LINKS_TO_FOLLOW,
2525
).pipe(
2626
getFirstCompletedRemoteData(),
2727
);

src/app/workspaceitems-edit-page/workspace-item-page.resolver.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import { Observable } from 'rxjs';
99
import { RemoteData } from '../core/data/remote-data';
1010
import { getFirstCompletedRemoteData } from '../core/shared/operators';
1111
import { WorkspaceItem } from '../core/submission/models/workspaceitem.model';
12+
import { SUBMISSION_LINKS_TO_FOLLOW } from '../core/submission/resolver/submission-links-to-follow';
1213
import { WorkspaceitemDataService } from '../core/submission/workspaceitem-data.service';
13-
import { followLink } from '../shared/utils/follow-link-config.model';
1414

1515
/**
1616
* Method for resolving a workflow item based on the parameters in the current route
@@ -28,7 +28,7 @@ export const workspaceItemPageResolver: ResolveFn<RemoteData<WorkspaceItem>> = (
2828
return workspaceItemService.findById(route.params.id,
2929
true,
3030
false,
31-
followLink('item'),
31+
...SUBMISSION_LINKS_TO_FOLLOW,
3232
).pipe(
3333
getFirstCompletedRemoteData(),
3434
);

0 commit comments

Comments
 (0)