Skip to content

Commit 9300269

Browse files
vins01-4scienceatarix83
authored andcommitted
[UXP-98] Fixes multiple import issue on unpaywall
1 parent 3d3c9f3 commit 9300269

1 file changed

Lines changed: 167 additions & 144 deletions

File tree

src/app/submission/sections/unpaywall/submission-section-unpaywall.component.ts

Lines changed: 167 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -56,29 +56,29 @@ const API_CHECK_INTERVAL = 3000;
5656
const MAX_TRIES = 5;
5757

5858
function attemptsGuardFactory(maxAttempts: number) {
59-
return (attemptsCount: number) => {
60-
if (attemptsCount > maxAttempts) {
61-
throw new Error('Exceeded pool requests, try again!');
62-
}
63-
};
59+
return (attemptsCount: number) => {
60+
if (attemptsCount > maxAttempts) {
61+
throw new Error('Exceeded pool requests, try again!');
62+
}
63+
};
6464
}
6565

6666
export function pollWhile<T>(
67-
pollInterval: number,
68-
isPollingActive: (res: T) => boolean,
69-
maxAttempts = Infinity,
70-
emitOnlyLast = true,
67+
pollInterval: number,
68+
isPollingActive: (res: T) => boolean,
69+
maxAttempts = Infinity,
70+
emitOnlyLast = true,
7171
): MonoTypeOperatorFunction<T> {
72-
return source$ => {
73-
const poll$ = timer(0, pollInterval).pipe(
74-
scan(attempts => ++attempts, 0),
75-
tap(attemptsGuardFactory(maxAttempts)),
76-
switchMap(() => source$),
77-
takeWhile(isPollingActive, true)
78-
);
79-
80-
return emitOnlyLast ? poll$.pipe(last()) : poll$;
81-
};
72+
return source$ => {
73+
const poll$ = timer(0, pollInterval).pipe(
74+
scan(attempts => ++attempts, 0),
75+
tap(attemptsGuardFactory(maxAttempts)),
76+
switchMap(() => source$),
77+
takeWhile(isPollingActive, true)
78+
);
79+
80+
return emitOnlyLast ? poll$.pipe(last()) : poll$;
81+
};
8282
}
8383

8484

@@ -100,15 +100,15 @@ export class SubmissionSectionUnpaywallComponent extends SectionModelComponent i
100100
public readonly UnpaywallSectionStatus = UnpaywallSectionStatus;
101101
public readonly status$ = new BehaviorSubject<UnpaywallSectionStatus>(null);
102102
public readonly loading$ = new BehaviorSubject<boolean>(true);
103-
public readonly unpaywallSection$ = new BehaviorSubject<WorkspaceitemSectionUnpaywallObject>(null);
103+
public readonly unpaywallSection$ = new BehaviorSubject<WorkspaceitemSectionUnpaywallObject>(null);
104104
public readonly uploadSection$ = new BehaviorSubject<UploadSection>(null);
105105

106-
protected readonly AlertType = AlertType;
107-
protected readonly section$ = new BehaviorSubject<WorkspaceitemSectionsObject>(null);
106+
protected readonly AlertType = AlertType;
107+
protected readonly section$ = new BehaviorSubject<WorkspaceitemSectionsObject>(null);
108108

109109
private readonly unsubscribe$ = new Subject<void>();
110-
private readonly fetch$ = new Subject<boolean>();
111-
private readonly stopFetch$ = new Subject<void>();
110+
private readonly fetch$ = new Subject<boolean>();
111+
private readonly stopFetch$ = new Subject<void>();
112112

113113
constructor(
114114
protected sectionService: SectionsService,
@@ -148,71 +148,84 @@ export class SubmissionSectionUnpaywallComponent extends SectionModelComponent i
148148
this.initUploadSectionResponseListener();
149149
}
150150

151-
protected initDoiChangesListener() {
152-
this.store.select(submissionObjectFromIdSelector(this.submissionId))
153-
.pipe(
154-
filter(submissionEntry => hasValue(submissionEntry?.definition) && hasValue(submissionEntry?.sections)),
155-
map(value => this.getDoiMetadataValue(value)),
156-
distinctUntilChanged(),
157-
takeUntil(this.unsubscribe$),
158-
)
159-
.subscribe(doiValue => !!doiValue ? this.showCurrentSection() : this.hideCurrentSection());
160-
}
151+
protected initDoiChangesListener() {
152+
this.store.select(submissionObjectFromIdSelector(this.submissionId))
153+
.pipe(
154+
filter(submissionEntry => hasValue(submissionEntry?.definition) && hasValue(submissionEntry?.sections)),
155+
map(value => this.getDoiMetadataValue(value)),
156+
distinctUntilChanged(),
157+
takeUntil(this.unsubscribe$),
158+
)
159+
.subscribe(doiValue => !!doiValue ? this.showCurrentSection() : this.hideCurrentSection());
160+
}
161161

162-
protected initUnpaywallFetching() {
163-
this.fetch$.pipe(
164-
switchMap((refreshRequired) =>
165-
this.patchForRefresh(refreshRequired).pipe(
166-
pollWhile(
167-
API_CHECK_INTERVAL,
168-
res => this.isStillPending(res),
169-
MAX_TRIES
170-
),
171-
this.getUnpaywallSection(),
172-
takeUntil(this.stopFetch$),
173-
catchError(err => {
174-
this.notificationsService.error(err?.message);
175-
return of(Object.assign({}, { ...this.unpaywallSection$.getValue(), status: UnpaywallSectionStatus.NO_FILE }));
176-
}),
177-
)
178-
),
179-
takeUntil(this.unsubscribe$),
180-
).subscribe(unpaywall => {
181-
this.updateUnpaywall(unpaywall);
182-
183-
const isLoading = !unpaywall?.status || unpaywall?.status === UnpaywallSectionStatus.PENDING;
184-
this.loading$.next(isLoading);
185-
if (!isLoading) {
186-
this.stopFetch$.next();
187-
}
188-
});
189-
}
162+
protected initUnpaywallFetching() {
163+
this.fetch$.pipe(
164+
switchMap((refreshRequired) =>
165+
this.patchForRefresh(refreshRequired)
166+
.pipe(
167+
switchMap(sections => {
168+
let unpaywall = this.extractUnpaywallSection(sections);
169+
if (unpaywall.status !== UnpaywallSectionStatus.PENDING) {
170+
return of(unpaywall);
171+
} else {
172+
return this.patchForRefresh(false).pipe(
173+
pollWhile(
174+
API_CHECK_INTERVAL,
175+
res => this.isStillPending(res),
176+
MAX_TRIES
177+
),
178+
takeUntil(this.stopFetch$),
179+
this.getUnpaywallSection(),
180+
catchError(err => {
181+
this.notificationsService.error(err?.message);
182+
return of(Object.assign({}, {
183+
...this.unpaywallSection$.getValue(),
184+
status: UnpaywallSectionStatus.NO_FILE
185+
}));
186+
})
187+
);
188+
}
189+
})
190+
)
191+
),
192+
takeUntil(this.unsubscribe$),
193+
).subscribe(unpaywall => {
194+
this.updateUnpaywall(unpaywall);
195+
196+
const isLoading = !unpaywall?.status || unpaywall?.status === UnpaywallSectionStatus.PENDING;
197+
this.loading$.next(isLoading);
198+
if (!isLoading) {
199+
this.stopFetch$.next();
200+
}
201+
});
202+
}
190203

191-
protected initStatusNotification() {
192-
this.status$.pipe(
193-
filter(hasValue),
194-
takeUntil(this.unsubscribe$),
195-
).subscribe(status => this.handleStatusNotification(status));
196-
}
204+
protected initStatusNotification() {
205+
this.status$.pipe(
206+
filter(hasValue),
207+
takeUntil(this.unsubscribe$),
208+
).subscribe(status => this.handleStatusNotification(status));
209+
}
197210

198-
protected handleStatusNotification(status: UnpaywallSectionStatus) {
199-
switch (status) {
200-
case UnpaywallSectionStatus.NOT_FOUND:
201-
this.notificationsService.error(this.translate.instant('submission.sections.unpaywall.status.not-found'));
202-
break;
203-
case UnpaywallSectionStatus.NO_FILE:
204-
this.notificationsService.warning(this.translate.instant('submission.sections.unpaywall.status.no-file'));
205-
break;
206-
case UnpaywallSectionStatus.SUCCESSFUL:
207-
this.notificationsService.success(this.translate.instant('submission.sections.unpaywall.status.successful'));
208-
break;
209-
case UnpaywallSectionStatus.IMPORTED:
210-
this.notificationsService.success(this.translate.instant('submission.sections.unpaywall.status.imported'));
211-
break;
212-
default:
213-
break;
214-
}
211+
protected handleStatusNotification(status: UnpaywallSectionStatus) {
212+
switch (status) {
213+
case UnpaywallSectionStatus.NOT_FOUND:
214+
this.notificationsService.error(this.translate.instant('submission.sections.unpaywall.status.not-found'));
215+
break;
216+
case UnpaywallSectionStatus.NO_FILE:
217+
this.notificationsService.warning(this.translate.instant('submission.sections.unpaywall.status.no-file'));
218+
break;
219+
case UnpaywallSectionStatus.SUCCESSFUL:
220+
this.notificationsService.success(this.translate.instant('submission.sections.unpaywall.status.successful'));
221+
break;
222+
case UnpaywallSectionStatus.IMPORTED:
223+
this.notificationsService.success(this.translate.instant('submission.sections.unpaywall.status.imported'));
224+
break;
225+
default:
226+
break;
215227
}
228+
}
216229

217230
protected onSectionDestroy(): void {
218231
this.unsubscribe$.next();
@@ -255,62 +268,69 @@ export class SubmissionSectionUnpaywallComponent extends SectionModelComponent i
255268
private updateUnpaywall(unpaywall: WorkspaceitemSectionUnpaywallObject) {
256269
this.unpaywallSection$.next(unpaywall);
257270
this.status$.next(unpaywall?.status);
258-
}
271+
}
259272

260273
private isStillPending(response: WorkspaceitemSectionsObject) {
261274
return hasNoValue(response?.unpaywall) || (response?.unpaywall as WorkspaceitemSectionUnpaywallObject)?.status === UnpaywallSectionStatus.PENDING;
262275
}
263276

264-
private handleFileUpload() {
265-
this.patchForAccept()
266-
.pipe(
267-
filter(hasValue),
268-
take(1),
269-
).subscribe(
270-
() => {
271-
this.loading$.next(true);
272-
this.fetch$.next(false);
273-
this.listenToUploadSectionChanges();
274-
},
275-
() => this.stopFetch$.next()
276-
);
277-
}
277+
private handleFileUpload() {
278+
this.loading$.next(true);
279+
this.listenToUploadSectionChanges();
280+
this.patchForAccept()
281+
.pipe(
282+
filter(hasValue),
283+
take(1),
284+
this.getUnpaywallSection(),
285+
catchError(err => {
286+
this.notificationsService.error(err?.message);
287+
return of(Object.assign({}, {
288+
...this.unpaywallSection$.getValue(),
289+
status: UnpaywallSectionStatus.NO_FILE
290+
}));
291+
}),
292+
).subscribe((unpaywall) => {
293+
this.updateUnpaywall(unpaywall);
294+
this.loading$.next(false);
295+
this.stopFetch$.next();
296+
});
297+
}
278298

279-
private listenToUploadSectionChanges() {
280-
this.uploadSection$.pipe(
281-
pairwise(),
282-
filter(([prev, curr]) => this.isAnyFieldChangedInLength(curr, prev)),
283-
map(([prev, curr]) => this.mapNewFilesByKey(curr, prev)),
284-
take(1),
285-
takeUntil(this.stopFetch$),
286-
)
287-
.subscribe(uploadedFiles => Object.keys(uploadedFiles).forEach(key => this.uploadFiles(uploadedFiles, key)));
288-
}
299+
private listenToUploadSectionChanges() {
300+
this.uploadSection$.pipe(
301+
pairwise(),
302+
takeUntil(this.stopFetch$),
303+
filter(([prev, curr]) => this.isAnyFieldChangedInLength(curr, prev)),
304+
map(([prev, curr]) => this.mapNewFilesByKey(curr, prev)),
305+
take(1),
306+
)
307+
.subscribe(uploadedFiles => Object.keys(uploadedFiles).forEach(key => this.uploadFiles(uploadedFiles, key)));
308+
}
289309

290310
private stopApiCheck(): void {
291311
this.loading$.next(false);
292312
this.stopFetch$.next();
293313
}
294314

295-
private patchForRefresh(refreshRequired = false): Observable<WorkspaceitemSectionsObject> {
296-
const { operation, linkPath } = this.createOperation('refresh', refreshRequired);
297-
return this.sendPatchRequest(linkPath, operation);
298-
}
315+
private patchForRefresh(refreshRequired = false): Observable<WorkspaceitemSectionsObject> {
316+
const { operation, linkPath } = this.createOperation('refresh', refreshRequired);
317+
return this.sendPatchRequest(linkPath, operation);
318+
}
299319

300-
private patchForAccept(accepted: boolean = true): Observable<WorkspaceitemSectionsObject> {
301-
const { operation, linkPath } = this.createOperation('accept', accepted);
302-
return this.sendPatchRequest(linkPath, operation);
303-
}
320+
private patchForAccept(accepted: boolean = true): Observable<WorkspaceitemSectionsObject> {
321+
const { operation, linkPath } = this.createOperation('accept', accepted);
322+
return this.sendPatchRequest(linkPath, operation);
323+
}
304324

305-
private createOperation(operationPath: string, value: boolean) {
306-
const pathCombiner = new JsonPatchOperationPathCombiner('sections', this.sectionData.id);
307-
const path = pathCombiner.getPath(operationPath);
308-
const operation = { op: 'add', path: path.path, value } as Operation;
309-
const linkPath = this.submissionService.getSubmissionObjectLinkName();
310-
return { operation, linkPath };
311-
}
325+
private createOperation(operationPath: string, value: boolean) {
326+
const pathCombiner = new JsonPatchOperationPathCombiner('sections', this.sectionData.id);
327+
const path = pathCombiner.getPath(operationPath);
328+
const operation = { op: 'add', path: path.path, value } as Operation;
329+
const linkPath = this.submissionService.getSubmissionObjectLinkName();
330+
return { operation, linkPath };
331+
}
312332

313-
private sendPatchRequest(linkPath: string, operation: Operation) {
333+
private sendPatchRequest(linkPath: string, operation: Operation): Observable<WorkspaceitemSectionsObject> {
314334
return this.halService.getEndpoint(linkPath)
315335
.pipe(
316336
map((endpoint: string) => endpoint.concat(`/${this.submissionId}`)),
@@ -321,10 +341,13 @@ export class SubmissionSectionUnpaywallComponent extends SectionModelComponent i
321341
);
322342
}
323343

324-
private getUnpaywallSection() {
325-
return (source$: Observable<WorkspaceitemSectionsObject>) =>
326-
source$.pipe(map(sections => sections?.unpaywall as WorkspaceitemSectionUnpaywallObject));
327-
}
344+
private getUnpaywallSection() {
345+
return (source$: Observable<WorkspaceitemSectionsObject>) => source$.pipe(map(this.extractUnpaywallSection));
346+
}
347+
348+
private extractUnpaywallSection(sections: WorkspaceitemSectionsObject) {
349+
return sections?.unpaywall as WorkspaceitemSectionUnpaywallObject;
350+
}
328351

329352
private getDoiMetadataValue(value: SubmissionObjectEntry): string {
330353
return value.sections[value.definition]?.data?.[DOI_METADATA]?.[0]?.value;
@@ -345,30 +368,30 @@ export class SubmissionSectionUnpaywallComponent extends SectionModelComponent i
345368
}
346369

347370
private mapNewFilesByKey(curr: UploadSection, prev: UploadSection) {
348-
return Object.assign({},
349-
...Object.keys(curr)
350-
.map(key => ({ [key]: this.getNewFiles(curr, prev, key) }))
351-
);
352-
}
371+
return Object.assign({},
372+
...Object.keys(curr)
373+
.map(key => ({ [key]: this.getNewFiles(curr, prev, key) }))
374+
);
375+
}
353376

354-
private uploadFiles(uploadedFiles: { [p: string]: any }, key: string) {
355-
return uploadedFiles[key].forEach(file => this.sectionUploadService.addUploadedFile(this.submissionId, key, file.uuid, file));
356-
}
377+
private uploadFiles(uploadedFiles: { [p: string]: any }, key: string) {
378+
return uploadedFiles[key].forEach(file => this.sectionUploadService.addUploadedFile(this.submissionId, key, file.uuid, file));
379+
}
357380

358381
private getNewFiles(curr: UploadSection, prev: UploadSection, key: string) {
359-
return curr[key]?.files?.filter(file => this.notContainsFile(prev[key], file));
360-
}
382+
return curr[key]?.files?.filter(file => this.notContainsFile(prev[key], file));
383+
}
361384

362385
private notContainsFile(uploadSection: WorkspaceitemSectionUploadObject, file: WorkspaceitemSectionUploadFileObject) {
363-
return hasNoValue(uploadSection?.files) || !uploadSection?.files.some(f => f.uuid === file.uuid);
364-
}
386+
return hasNoValue(uploadSection?.files) || !uploadSection?.files.some(f => f.uuid === file.uuid);
387+
}
365388

366389
private isAnyFieldChangedInLength(curr: UploadSection, prev: UploadSection) {
367390
return Object.keys(curr).some(key => this.isLengthDifferent(curr, prev, key));
368-
}
391+
}
369392

370393
private isLengthDifferent(curr: UploadSection, prev: UploadSection, key: string) {
371-
return curr[key]?.files.length !== prev[key]?.files.length;
394+
return curr[key]?.files.length !== prev[key]?.files.length;
372395
}
373396

374397
}

0 commit comments

Comments
 (0)