Files
dspace-angular/src/app/item-page/orcid-page/orcid-queue/orcid-queue.component.ts
2023-07-20 14:53:28 +02:00

303 lines
9.8 KiB
TypeScript

import { Component, Input, OnDestroy, OnInit, SimpleChanges } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { BehaviorSubject, combineLatest, Observable, Subscription } from 'rxjs';
import { debounceTime, distinctUntilChanged, switchMap, tap } from 'rxjs/operators';
import { PaginatedList } from '../../../core/data/paginated-list.model';
import { RemoteData } from '../../../core/data/remote-data';
import { OrcidHistory } from '../../../core/orcid/model/orcid-history.model';
import { OrcidQueue } from '../../../core/orcid/model/orcid-queue.model';
import { OrcidHistoryDataService } from '../../../core/orcid/orcid-history-data.service';
import { OrcidQueueDataService } from '../../../core/orcid/orcid-queue-data.service';
import { PaginationService } from '../../../core/pagination/pagination.service';
import { getFirstCompletedRemoteData } from '../../../core/shared/operators';
import { hasValue } from '../../../shared/empty.util';
import { NotificationsService } from '../../../shared/notifications/notifications.service';
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
import { AlertType } from '../../../shared/alert/alert-type';
import { Item } from '../../../core/shared/item.model';
import { OrcidAuthService } from '../../../core/orcid/orcid-auth.service';
@Component({
selector: 'ds-orcid-queue',
templateUrl: './orcid-queue.component.html',
styleUrls: ['./orcid-queue.component.scss']
})
export class OrcidQueueComponent implements OnInit, OnDestroy {
/**
* The item for which showing the orcid settings
*/
@Input() item: Item;
/**
* Pagination config used to display the list
*/
public paginationOptions: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), {
id: 'oqp',
pageSize: 5
});
/**
* A boolean representing if results are loading
*/
public processing$ = new BehaviorSubject<boolean>(false);
/**
* A list of orcid queue records
*/
private list$: BehaviorSubject<RemoteData<PaginatedList<OrcidQueue>>> = new BehaviorSubject<RemoteData<PaginatedList<OrcidQueue>>>({} as any);
/**
* The AlertType enumeration
* @type {AlertType}
*/
AlertTypeEnum = AlertType;
/**
* Array to track all subscriptions and unsubscribe them onDestroy
* @type {Array}
*/
private subs: Subscription[] = [];
constructor(private orcidAuthService: OrcidAuthService,
private orcidQueueService: OrcidQueueDataService,
protected translateService: TranslateService,
private paginationService: PaginationService,
private notificationsService: NotificationsService,
private orcidHistoryService: OrcidHistoryDataService,
) {
}
ngOnInit(): void {
this.updateList();
}
ngOnChanges(changes: SimpleChanges): void {
if (!changes.item.isFirstChange() && changes.item.currentValue !== changes.item.previousValue) {
this.updateList();
}
}
/**
* Retrieve queue list
*/
updateList() {
this.subs.push(
this.paginationService.getCurrentPagination(this.paginationOptions.id, this.paginationOptions).pipe(
debounceTime(100),
distinctUntilChanged(),
tap(() => this.processing$.next(true)),
switchMap((config: PaginationComponentOptions) => this.orcidQueueService.searchByProfileItemId(this.item.id, config, false)),
getFirstCompletedRemoteData()
).subscribe((result: RemoteData<PaginatedList<OrcidQueue>>) => {
this.processing$.next(false);
this.list$.next(result);
this.orcidQueueService.clearFindByProfileItemRequests();
})
);
}
/**
* Return the list of orcid queue records
*/
getList(): Observable<RemoteData<PaginatedList<OrcidQueue>>> {
return this.list$.asObservable();
}
/**
* Return the icon class for the queue object type
*
* @param orcidQueue The OrcidQueue object
*/
getIconClass(orcidQueue: OrcidQueue): string {
if (!orcidQueue.recordType) {
return 'fa fa-user';
}
switch (orcidQueue.recordType.toLowerCase()) {
case 'publication':
return 'fas fa-book';
case 'project':
return 'fas fa-wallet';
case 'education':
return 'fas fa-school';
case 'affiliation':
return 'fas fa-university';
case 'country':
return 'fas fa-globe-europe';
case 'external_ids':
case 'researcher_urls':
return 'fas fa-external-link-alt';
default:
return 'fa fa-user';
}
}
/**
* Return the icon tooltip message for the queue object type
*
* @param orcidQueue The OrcidQueue object
*/
getIconTooltip(orcidQueue: OrcidQueue): string {
if (!orcidQueue.recordType) {
return '';
}
return 'person.page.orcid.sync-queue.tooltip.' + orcidQueue.recordType.toLowerCase();
}
/**
* Return the icon tooltip message for the queue object operation
*
* @param orcidQueue The OrcidQueue object
*/
getOperationTooltip(orcidQueue: OrcidQueue): string {
if (!orcidQueue.operation) {
return '';
}
return 'person.page.orcid.sync-queue.tooltip.' + orcidQueue.operation.toLowerCase();
}
/**
* Return the icon class for the queue object operation
*
* @param orcidQueue The OrcidQueue object
*/
getOperationClass(orcidQueue: OrcidQueue): string {
if (!orcidQueue.operation) {
return '';
}
switch (orcidQueue.operation.toLowerCase()) {
case 'insert':
return 'fas fa-plus';
case 'update':
return 'fas fa-edit';
case 'delete':
return 'fas fa-trash-alt';
default:
return '';
}
}
/**
* Discard a queue entry from the synchronization
*
* @param orcidQueue The OrcidQueue object to discard
*/
discardEntry(orcidQueue: OrcidQueue) {
this.processing$.next(true);
this.subs.push(this.orcidQueueService.deleteById(orcidQueue.id).pipe(
getFirstCompletedRemoteData()
).subscribe((remoteData) => {
this.processing$.next(false);
if (remoteData.isSuccess) {
this.notificationsService.success(this.translateService.get('person.page.orcid.sync-queue.discard.success'));
this.updateList();
} else {
this.notificationsService.error(this.translateService.get('person.page.orcid.sync-queue.discard.error'));
}
}));
}
/**
* Send a queue entry to orcid for the synchronization
*
* @param orcidQueue The OrcidQueue object to synchronize
*/
send(orcidQueue: OrcidQueue) {
this.processing$.next(true);
this.subs.push(this.orcidHistoryService.sendToORCID(orcidQueue).pipe(
getFirstCompletedRemoteData()
).subscribe((remoteData) => {
this.processing$.next(false);
if (remoteData.isSuccess) {
this.handleOrcidHistoryRecordCreation(remoteData.payload);
} else if (remoteData.statusCode === 422) {
this.handleValidationErrors(remoteData);
} else {
this.notificationsService.error(this.translateService.get('person.page.orcid.sync-queue.send.error'));
}
}));
}
/**
* Return the error message for Unauthorized response
* @private
*/
private getUnauthorizedErrorContent(): Observable<string> {
return this.orcidAuthService.getOrcidAuthorizeUrl(this.item).pipe(
switchMap((authorizeUrl) => this.translateService.get(
'person.page.orcid.sync-queue.send.unauthorized-error.content',
{ orcid: authorizeUrl }
))
);
}
/**
* Manage notification by response
* @private
*/
private handleOrcidHistoryRecordCreation(orcidHistory: OrcidHistory) {
switch (orcidHistory.status) {
case 200:
case 201:
case 204:
this.notificationsService.success(this.translateService.get('person.page.orcid.sync-queue.send.success'));
this.updateList();
break;
case 400:
this.notificationsService.error(this.translateService.get('person.page.orcid.sync-queue.send.bad-request-error'), null, { timeOut: 0 });
break;
case 401:
combineLatest([
this.translateService.get('person.page.orcid.sync-queue.send.unauthorized-error.title'),
this.getUnauthorizedErrorContent()],
).subscribe(([title, content]) => {
this.notificationsService.error(title, content, { timeOut: 0 }, true);
});
break;
case 404:
this.notificationsService.warning(this.translateService.get('person.page.orcid.sync-queue.send.not-found-warning'));
break;
case 409:
this.notificationsService.error(this.translateService.get('person.page.orcid.sync-queue.send.conflict-error'), null, { timeOut: 0 });
break;
default:
this.notificationsService.error(this.translateService.get('person.page.orcid.sync-queue.send.error'), null, { timeOut: 0 });
}
}
/**
* Manage validation errors
* @private
*/
private handleValidationErrors(remoteData: RemoteData<OrcidHistory>) {
const translations = [this.translateService.get('person.page.orcid.sync-queue.send.validation-error')];
const errorMessage = remoteData.errorMessage;
if (errorMessage && errorMessage.indexOf('Error codes:') > 0) {
errorMessage.substring(errorMessage.indexOf(':') + 1).trim().split(',')
.forEach((error) => translations.push(this.translateService.get('person.page.orcid.sync-queue.send.validation-error.' + error)));
}
combineLatest(translations).subscribe((messages) => {
const title = messages.shift();
const content = '<ul>' + messages.map((message) => `<li>${message}</li>`).join('') + '</ul>';
this.notificationsService.error(title, content, { timeOut: 0 }, true);
});
}
/**
* Unsubscribe from all subscriptions
*/
ngOnDestroy(): void {
this.list$ = null;
this.subs.filter((subscription) => hasValue(subscription))
.forEach((subscription) => subscription.unsubscribe());
}
}