mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
[CST-5339] ORCID queue added.
This commit is contained in:
@@ -165,6 +165,10 @@ import { SubmissionAccessesModel } from './config/models/config-submission-acces
|
|||||||
import { ResearcherProfileService } from './profile/researcher-profile.service';
|
import { ResearcherProfileService } from './profile/researcher-profile.service';
|
||||||
import { ProfileClaimService } from '../profile-page/profile-claim/profile-claim.service';
|
import { ProfileClaimService } from '../profile-page/profile-claim/profile-claim.service';
|
||||||
import { ResearcherProfile } from './profile/model/researcher-profile.model';
|
import { ResearcherProfile } from './profile/model/researcher-profile.model';
|
||||||
|
import { OrcidQueueService } from './orcid/orcid-queue.service';
|
||||||
|
import { OrcidHistoryService } from './orcid/orcid-history.service';
|
||||||
|
import { OrcidQueue } from './orcid/model/orcid-queue.model';
|
||||||
|
import { OrcidHistory } from './orcid/model/orcid-history.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When not in production, endpoint responses can be mocked for testing purposes
|
* When not in production, endpoint responses can be mocked for testing purposes
|
||||||
@@ -290,7 +294,9 @@ const PROVIDERS = [
|
|||||||
GroupDataService,
|
GroupDataService,
|
||||||
FeedbackDataService,
|
FeedbackDataService,
|
||||||
ResearcherProfileService,
|
ResearcherProfileService,
|
||||||
ProfileClaimService
|
ProfileClaimService,
|
||||||
|
OrcidQueueService,
|
||||||
|
OrcidHistoryService,
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -351,7 +357,9 @@ export const models =
|
|||||||
Root,
|
Root,
|
||||||
SearchConfig,
|
SearchConfig,
|
||||||
SubmissionAccessesModel,
|
SubmissionAccessesModel,
|
||||||
ResearcherProfile
|
ResearcherProfile,
|
||||||
|
OrcidQueue,
|
||||||
|
OrcidHistory,
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
89
src/app/core/orcid/model/orcid-history.model.ts
Normal file
89
src/app/core/orcid/model/orcid-history.model.ts
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import { autoserialize, deserialize } from 'cerialize';
|
||||||
|
import { typedObject } from '../../cache/builders/build-decorators';
|
||||||
|
import { CacheableObject } from '../../cache/object-cache.reducer';
|
||||||
|
import { HALLink } from '../../shared/hal-link.model';
|
||||||
|
import { ResourceType } from '../../shared/resource-type';
|
||||||
|
import { excludeFromEquals } from '../../utilities/equals.decorators';
|
||||||
|
import { ORCID_HISTORY } from './orcid-history.resource-type';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class the represents a Orcid History.
|
||||||
|
*/
|
||||||
|
@typedObject
|
||||||
|
export class OrcidHistory extends CacheableObject {
|
||||||
|
|
||||||
|
static type = ORCID_HISTORY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The object type
|
||||||
|
*/
|
||||||
|
@excludeFromEquals
|
||||||
|
@autoserialize
|
||||||
|
type: ResourceType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The identifier of this Orcid History record
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the related entity
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
entityName: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The identifier of the owner of this Orcid History record.
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
ownerId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The identifier of the entity related to this Orcid History record.
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
entityId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of the entity related to this Orcid History record.
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
entityType: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The response status coming from ORCID api.
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
status: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The putCode assigned by ORCID to the entity.
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
putCode: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The last send attempt timestamp.
|
||||||
|
*/
|
||||||
|
lastAttempt: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The success send attempt timestamp.
|
||||||
|
*/
|
||||||
|
successAttempt: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The response coming from ORCID.
|
||||||
|
*/
|
||||||
|
responseMessage: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link HALLink}s for this Orcid History record
|
||||||
|
*/
|
||||||
|
@deserialize
|
||||||
|
_links: {
|
||||||
|
self: HALLink,
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
9
src/app/core/orcid/model/orcid-history.resource-type.ts
Normal file
9
src/app/core/orcid/model/orcid-history.resource-type.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { ResourceType } from '../../shared/resource-type';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The resource type for OrcidHistory
|
||||||
|
*
|
||||||
|
* Needs to be in a separate file to prevent circular
|
||||||
|
* dependencies in webpack.
|
||||||
|
*/
|
||||||
|
export const ORCID_HISTORY = new ResourceType('orcidhistory');
|
68
src/app/core/orcid/model/orcid-queue.model.ts
Normal file
68
src/app/core/orcid/model/orcid-queue.model.ts
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import { autoserialize, deserialize } from 'cerialize';
|
||||||
|
import { typedObject } from '../../cache/builders/build-decorators';
|
||||||
|
import { CacheableObject } from '../../cache/object-cache.reducer';
|
||||||
|
import { HALLink } from '../../shared/hal-link.model';
|
||||||
|
import { ResourceType } from '../../shared/resource-type';
|
||||||
|
import { excludeFromEquals } from '../../utilities/equals.decorators';
|
||||||
|
import { ORCID_QUEUE } from './orcid-queue.resource-type';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class the represents a Orcid Queue.
|
||||||
|
*/
|
||||||
|
@typedObject
|
||||||
|
export class OrcidQueue extends CacheableObject {
|
||||||
|
|
||||||
|
static type = ORCID_QUEUE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The object type
|
||||||
|
*/
|
||||||
|
@excludeFromEquals
|
||||||
|
@autoserialize
|
||||||
|
type: ResourceType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The identifier of this Orcid Queue record
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The record description.
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
description: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The identifier of the owner of this Orcid Queue record.
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
ownerId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The identifier of the entity related to this Orcid Queue record.
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
entityId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of this Orcid Queue record.
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
recordType: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The operation related to this Orcid Queue record.
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
operation: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link HALLink}s for this Orcid Queue record
|
||||||
|
*/
|
||||||
|
@deserialize
|
||||||
|
_links: {
|
||||||
|
self: HALLink,
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
9
src/app/core/orcid/model/orcid-queue.resource-type.ts
Normal file
9
src/app/core/orcid/model/orcid-queue.resource-type.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { ResourceType } from '../../shared/resource-type';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The resource type for OrcidQueue
|
||||||
|
*
|
||||||
|
* Needs to be in a separate file to prevent circular
|
||||||
|
* dependencies in webpack.
|
||||||
|
*/
|
||||||
|
export const ORCID_QUEUE = new ResourceType('orcidqueue');
|
95
src/app/core/orcid/orcid-history.service.ts
Normal file
95
src/app/core/orcid/orcid-history.service.ts
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
/* tslint:disable:max-classes-per-file */
|
||||||
|
|
||||||
|
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { catchError, distinctUntilChanged, map, switchMap, tap } from 'rxjs/operators';
|
||||||
|
import { isNotEmpty } from '../../shared/empty.util';
|
||||||
|
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||||
|
import { createFailedRemoteDataObject$ } from '../../shared/remote-data.utils';
|
||||||
|
import { dataService } from '../cache/builders/build-decorators';
|
||||||
|
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||||
|
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
|
import { RestResponse } from '../cache/response.models';
|
||||||
|
import { CoreState } from '../core.reducers';
|
||||||
|
import { DataService } from '../data/data.service';
|
||||||
|
import { DefaultChangeAnalyzer } from '../data/default-change-analyzer.service';
|
||||||
|
import { ItemDataService } from '../data/item-data.service';
|
||||||
|
import { RemoteData } from '../data/remote-data';
|
||||||
|
import { PostRequest, RestRequest } from '../data/request.models';
|
||||||
|
import { RequestService } from '../data/request.service';
|
||||||
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
|
import { getFinishedRemoteData, getResponseFromEntry, sendRequest } from '../shared/operators';
|
||||||
|
import { OrcidHistory } from './model/orcid-history.model';
|
||||||
|
import { ORCID_HISTORY } from './model/orcid-history.resource-type';
|
||||||
|
import { OrcidQueue } from './model/orcid-queue.model';
|
||||||
|
import { HttpOptions } from '../dspace-rest/dspace-rest.service';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A private DataService implementation to delegate specific methods to.
|
||||||
|
*/
|
||||||
|
class OrcidHistoryServiceImpl extends DataService<OrcidHistory> {
|
||||||
|
public linkPath = 'orcidhistories';
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected requestService: RequestService,
|
||||||
|
protected rdbService: RemoteDataBuildService,
|
||||||
|
protected store: Store<CoreState>,
|
||||||
|
protected objectCache: ObjectCacheService,
|
||||||
|
protected halService: HALEndpointService,
|
||||||
|
protected notificationsService: NotificationsService,
|
||||||
|
protected http: HttpClient,
|
||||||
|
protected comparator: DefaultChangeAnalyzer<OrcidHistory>) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A service that provides methods to make REST requests with Orcid History endpoint.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
@dataService(ORCID_HISTORY)
|
||||||
|
export class OrcidHistoryService {
|
||||||
|
|
||||||
|
dataService: OrcidHistoryServiceImpl;
|
||||||
|
|
||||||
|
responseMsToLive: number = 10 * 1000;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected requestService: RequestService,
|
||||||
|
protected rdbService: RemoteDataBuildService,
|
||||||
|
protected store: Store<CoreState>,
|
||||||
|
protected objectCache: ObjectCacheService,
|
||||||
|
protected halService: HALEndpointService,
|
||||||
|
protected notificationsService: NotificationsService,
|
||||||
|
protected http: HttpClient,
|
||||||
|
protected comparator: DefaultChangeAnalyzer<OrcidHistory>,
|
||||||
|
protected itemService: ItemDataService ) {
|
||||||
|
|
||||||
|
this.dataService = new OrcidHistoryServiceImpl(requestService, rdbService, store, objectCache, halService,
|
||||||
|
notificationsService, http, comparator);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
sendToORCID(orcidQueue: OrcidQueue): Observable<RemoteData<OrcidHistory>> {
|
||||||
|
const requestId = this.requestService.generateRequestId();
|
||||||
|
return this.getEndpoint().pipe(
|
||||||
|
map((endpointURL: string) => {
|
||||||
|
const options: HttpOptions = Object.create({});
|
||||||
|
let headers = new HttpHeaders();
|
||||||
|
headers = headers.append('Content-Type', 'text/uri-list');
|
||||||
|
options.headers = headers;
|
||||||
|
return new PostRequest(requestId, endpointURL, orcidQueue._links.self.href, options);
|
||||||
|
}),
|
||||||
|
sendRequest(this.requestService),
|
||||||
|
switchMap((request: RestRequest) => this.rdbService.buildFromRequestUUID(request.uuid) as Observable<RemoteData<OrcidHistory>>)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getEndpoint(): Observable<string> {
|
||||||
|
return this.halService.getEndpoint(this.dataService.linkPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
124
src/app/core/orcid/orcid-queue.service.ts
Normal file
124
src/app/core/orcid/orcid-queue.service.ts
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
/* tslint:disable:max-classes-per-file */
|
||||||
|
|
||||||
|
import { DataService } from '../data/data.service';
|
||||||
|
import { OrcidQueue } from './model/orcid-queue.model';
|
||||||
|
import { RequestService } from '../data/request.service';
|
||||||
|
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
import { CoreState } from '../core.reducers';
|
||||||
|
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
|
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { DefaultChangeAnalyzer } from '../data/default-change-analyzer.service';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { dataService } from '../cache/builders/build-decorators';
|
||||||
|
import { ORCID_QUEUE } from './model/orcid-queue.resource-type';
|
||||||
|
import { ItemDataService } from '../data/item-data.service';
|
||||||
|
import { combineLatest, Observable } from 'rxjs';
|
||||||
|
import { RemoteData } from '../data/remote-data';
|
||||||
|
import { PaginatedList } from '../data/paginated-list.model';
|
||||||
|
import { RequestParam } from '../cache/models/request-param.model';
|
||||||
|
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
||||||
|
import { NoContent } from '../shared/NoContent.model';
|
||||||
|
import { ConfigurationDataService } from '../data/configuration-data.service';
|
||||||
|
import { map } from 'rxjs/operators';
|
||||||
|
import { getFirstSucceededRemoteDataPayload } from '../shared/operators';
|
||||||
|
import { environment } from '../../../environments/environment';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A private DataService implementation to delegate specific methods to.
|
||||||
|
*/
|
||||||
|
class OrcidQueueServiceImpl extends DataService<OrcidQueue> {
|
||||||
|
public linkPath = 'orcidqueues';
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected requestService: RequestService,
|
||||||
|
protected rdbService: RemoteDataBuildService,
|
||||||
|
protected store: Store<CoreState>,
|
||||||
|
protected objectCache: ObjectCacheService,
|
||||||
|
protected halService: HALEndpointService,
|
||||||
|
protected notificationsService: NotificationsService,
|
||||||
|
protected http: HttpClient,
|
||||||
|
protected comparator: DefaultChangeAnalyzer<OrcidQueue>) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A service that provides methods to make REST requests with Orcid Queue endpoint.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
@dataService(ORCID_QUEUE)
|
||||||
|
export class OrcidQueueService {
|
||||||
|
|
||||||
|
dataService: OrcidQueueServiceImpl;
|
||||||
|
|
||||||
|
responseMsToLive: number = 10 * 1000;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected requestService: RequestService,
|
||||||
|
protected rdbService: RemoteDataBuildService,
|
||||||
|
protected store: Store<CoreState>,
|
||||||
|
protected objectCache: ObjectCacheService,
|
||||||
|
protected halService: HALEndpointService,
|
||||||
|
protected notificationsService: NotificationsService,
|
||||||
|
protected http: HttpClient,
|
||||||
|
protected comparator: DefaultChangeAnalyzer<OrcidQueue>,
|
||||||
|
protected configurationService: ConfigurationDataService,
|
||||||
|
protected router: Router,
|
||||||
|
protected itemService: ItemDataService ) {
|
||||||
|
|
||||||
|
this.dataService = new OrcidQueueServiceImpl(requestService, rdbService, store, objectCache, halService,
|
||||||
|
notificationsService, http, comparator);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param itemId It represent a Id of owner
|
||||||
|
* @param paginationOptions
|
||||||
|
* @returns { OrcidQueue }
|
||||||
|
*/
|
||||||
|
searchByOwnerId(itemId: string, paginationOptions: PaginationComponentOptions): Observable<RemoteData<PaginatedList<OrcidQueue>>> {
|
||||||
|
return this.dataService.searchBy('findByOwner', {
|
||||||
|
searchParams: [new RequestParam('ownerId', itemId)],
|
||||||
|
elementsPerPage: paginationOptions.pageSize,
|
||||||
|
currentPage: paginationOptions.currentPage
|
||||||
|
},false,
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param orcidQueueId represents a id of orcid queue
|
||||||
|
* @returns { NoContent }
|
||||||
|
*/
|
||||||
|
deleteById(orcidQueueId: number): Observable<RemoteData<NoContent>> {
|
||||||
|
return this.dataService.delete(orcidQueueId + '');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method will set linkPath to stale
|
||||||
|
*/
|
||||||
|
clearFindByOwnerRequests() {
|
||||||
|
this.requestService.setStaleByHrefSubstring(this.dataService.linkPath + '/search/findByOwner');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param profileId represent a uuid of that user
|
||||||
|
* @returns orcid authorized url of that user
|
||||||
|
*/
|
||||||
|
getOrcidAuthorizeUrl(profileId: string): Observable<string> {
|
||||||
|
return combineLatest([
|
||||||
|
this.configurationService.findByPropertyName('orcid.authorize-url').pipe(getFirstSucceededRemoteDataPayload()),
|
||||||
|
this.configurationService.findByPropertyName('orcid.application-client-id').pipe(getFirstSucceededRemoteDataPayload()),
|
||||||
|
this.configurationService.findByPropertyName('orcid.scope').pipe(getFirstSucceededRemoteDataPayload())]
|
||||||
|
).pipe(
|
||||||
|
map(([authorizeUrl, clientId, scopes]) => {
|
||||||
|
const redirectUri = environment.rest.baseUrl + '/api/cris/orcid/' + profileId + '/?url=' + encodeURIComponent(this.router.url);
|
||||||
|
return authorizeUrl.values[0] + '?client_id=' + clientId.values[0] + '&redirect_uri=' + redirectUri + '&response_type=code&scope='
|
||||||
|
+ scopes.values.join(' ');
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
@@ -38,6 +38,7 @@ import { OrcidAuthComponent } from './orcid-page/orcid-auth/orcid-auth.component
|
|||||||
import { OrcidPageComponent } from './orcid-page/orcid-page.component';
|
import { OrcidPageComponent } from './orcid-page/orcid-page.component';
|
||||||
import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap';
|
||||||
import { OrcidSettingComponent } from './orcid-page/orcid-sync/orcid-setting.component';
|
import { OrcidSettingComponent } from './orcid-page/orcid-sync/orcid-setting.component';
|
||||||
|
import { OrcidQueueComponent } from './orcid-page/orcid-queue/orcid-queue.component';
|
||||||
|
|
||||||
|
|
||||||
const ENTRY_COMPONENTS = [
|
const ENTRY_COMPONENTS = [
|
||||||
@@ -73,7 +74,8 @@ const DECLARATIONS = [
|
|||||||
VersionPageComponent,
|
VersionPageComponent,
|
||||||
OrcidPageComponent,
|
OrcidPageComponent,
|
||||||
OrcidAuthComponent,
|
OrcidAuthComponent,
|
||||||
OrcidSettingComponent
|
OrcidSettingComponent,
|
||||||
|
OrcidQueueComponent
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@@ -1,2 +1,3 @@
|
|||||||
<ds-orcid-auth></ds-orcid-auth>
|
<ds-orcid-auth></ds-orcid-auth>
|
||||||
<ds-orcid-setting></ds-orcid-setting>
|
<ds-orcid-setting></ds-orcid-setting>
|
||||||
|
<ds-orcid-queue></ds-orcid-queue>
|
||||||
|
@@ -0,0 +1,59 @@
|
|||||||
|
<div class="container custom-accordion mb-4">
|
||||||
|
<ngb-accordion activeIds="queue">
|
||||||
|
<ngb-panel title="{{'person.orcid.registry.queue' | translate}}" id="queue">
|
||||||
|
<ng-template ngbPanelContent>
|
||||||
|
<div class="container">
|
||||||
|
<ds-loading *ngIf="(processing$ | async)"></ds-loading>
|
||||||
|
|
||||||
|
<div *ngIf="!(processing$ | async) && (getList() | async)?.payload?.totalElements == 0"
|
||||||
|
class="row alert alert-info">
|
||||||
|
{{ 'person.page.orcid.sync-queue.empty-message' | translate}}
|
||||||
|
</div>
|
||||||
|
<ds-pagination *ngIf="!(processing$ | async) && (getList() | async)?.payload?.totalElements > 0"
|
||||||
|
[paginationOptions]="paginationOptions" [collectionSize]="(getList() | async)?.payload?.totalElements"
|
||||||
|
[retainScrollPosition]="false" [hideGear]="true" (paginationChange)="updateList()">
|
||||||
|
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table id="groups" class="table table-sm table-striped table-hover table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr class="text-center align-middle">
|
||||||
|
<th>{{'person.page.orcid.sync-queue.description' | translate}}</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor="let entry of (getList() | async)?.payload?.page">
|
||||||
|
<td class="text-center align-middle">
|
||||||
|
{{ entry.description }}
|
||||||
|
</td>
|
||||||
|
<td style="width: 20%" class="text-center">
|
||||||
|
<div class="btn-group edit-field">
|
||||||
|
<button [ngbTooltip]="getOperationTooltip(entry) | translate" container="body"
|
||||||
|
class="btn btn-outline-success my-1 col-md">
|
||||||
|
<i [ngClass]="getOperationClass(entry)"></i>
|
||||||
|
</button>
|
||||||
|
<button [ngbTooltip]="getIconTooltip(entry) | translate" container="body"
|
||||||
|
class="btn btn-outline-warning my-1 col-md">
|
||||||
|
<i [ngClass]="getIconClass(entry)"
|
||||||
|
aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
<button [ngbTooltip]="'person.page.orcid.sync-queue.send' | translate" container="body"
|
||||||
|
class="btn btn-outline-success my-1 col-md" (click)="send(entry)">
|
||||||
|
<i class="fas fa-cogs"></i>
|
||||||
|
</button>
|
||||||
|
<button [ngbTooltip]="'person.page.orcid.sync-queue.discard' | translate" container="body"
|
||||||
|
class="btn btn-outline-danger my-1 col-md" (click)="discardEntry(entry)">
|
||||||
|
<i class="fas fa-times"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</ds-pagination>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
</ngb-panel>
|
||||||
|
</ngb-accordion>
|
||||||
|
</div>
|
@@ -0,0 +1,250 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { BehaviorSubject, combineLatest, Observable, Subscription } from 'rxjs';
|
||||||
|
import { 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 { OrcidHistoryService } from '../../../core/orcid/orcid-history.service';
|
||||||
|
import { OrcidQueueService } from '../../../core/orcid/orcid-queue.service';
|
||||||
|
import { PaginationService } from '../../../core/pagination/pagination.service';
|
||||||
|
import { getFinishedRemoteData, 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';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-orcid-queue',
|
||||||
|
templateUrl: './orcid-queue.component.html',
|
||||||
|
styleUrls: ['./orcid-queue.component.scss']
|
||||||
|
})
|
||||||
|
export class OrcidQueueComponent implements OnInit {
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array to track all subscriptions and unsubscribe them onDestroy
|
||||||
|
* @type {Array}
|
||||||
|
*/
|
||||||
|
private subs: Subscription[] = [];
|
||||||
|
constructor(private orcidQueueService: OrcidQueueService,
|
||||||
|
protected translateService: TranslateService,
|
||||||
|
private paginationService: PaginationService,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private notificationsService: NotificationsService,
|
||||||
|
private orcidHistoryService: OrcidHistoryService,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.updateList();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateList() {
|
||||||
|
this.subs.push(
|
||||||
|
this.paginationService.getCurrentPagination(this.paginationOptions.id, this.paginationOptions).pipe(
|
||||||
|
tap(() => this.processing$.next(true)),
|
||||||
|
switchMap((config: PaginationComponentOptions) => this.orcidQueueService.searchByOwnerId(this.route.snapshot.paramMap.get('id'), config)),
|
||||||
|
getFirstCompletedRemoteData()
|
||||||
|
).subscribe((result: RemoteData<PaginatedList<OrcidQueue>>) => {
|
||||||
|
this.processing$.next(false);
|
||||||
|
this.list$.next(result);
|
||||||
|
this.orcidQueueService.clearFindByOwnerRequests();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the list of orcid queue records
|
||||||
|
*/
|
||||||
|
getList(): Observable<RemoteData<PaginatedList<OrcidQueue>>> {
|
||||||
|
return this.list$.asObservable();
|
||||||
|
}
|
||||||
|
|
||||||
|
getIconClass(orcidQueue: OrcidQueue): string {
|
||||||
|
if (!orcidQueue.recordType) {
|
||||||
|
return 'fa fa-user';
|
||||||
|
}
|
||||||
|
switch (orcidQueue.recordType.toLowerCase()) {
|
||||||
|
case 'publication':
|
||||||
|
return 'fa fa-book';
|
||||||
|
case 'funding':
|
||||||
|
return 'fa fa-wallet';
|
||||||
|
case 'education':
|
||||||
|
return 'fa fa-school';
|
||||||
|
case 'affiliation':
|
||||||
|
return 'fa 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';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getIconTooltip(orcidQueue: OrcidQueue): string {
|
||||||
|
if (!orcidQueue.recordType) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'person.page.orcid.sync-queue.tooltip.' + orcidQueue.recordType.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
getOperationBadgeClass(orcidQueue: OrcidQueue): string {
|
||||||
|
|
||||||
|
if (!orcidQueue.operation) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (orcidQueue.operation.toLowerCase()) {
|
||||||
|
case 'insert':
|
||||||
|
return 'badge badge-pill badge-success';
|
||||||
|
case 'update':
|
||||||
|
return 'badge badge-pill badge-primary';
|
||||||
|
case 'delete':
|
||||||
|
return 'badge badge-pill badge-danger';
|
||||||
|
default:
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getOperationTooltip(orcidQueue: OrcidQueue): string {
|
||||||
|
if (!orcidQueue.operation) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'person.page.orcid.sync-queue.tooltip.' + orcidQueue.operation.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
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 '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
discardEntry(orcidQueue: OrcidQueue) {
|
||||||
|
this.processing$.next(true);
|
||||||
|
this.subs.push(this.orcidQueueService.deleteById(orcidQueue.id).pipe(
|
||||||
|
getFinishedRemoteData()
|
||||||
|
).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( orcidQueue: OrcidQueue ) {
|
||||||
|
this.processing$.next(true);
|
||||||
|
this.subs.push(this.orcidHistoryService.sendToORCID(orcidQueue).pipe(
|
||||||
|
getFinishedRemoteData()
|
||||||
|
).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'));
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
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: -1 });
|
||||||
|
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: -1}, 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: -1 });
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this.notificationsService.error(this.translateService.get('person.page.orcid.sync-queue.send.error'), null, { timeOut: -1 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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: -1 }, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private getUnauthorizedErrorContent(): Observable<string> {
|
||||||
|
return this.orcidQueueService.getOrcidAuthorizeUrl(this.route.snapshot.paramMap.get('id')).pipe(
|
||||||
|
switchMap((authorizeUrl) => this.translateService.get('person.page.orcid.sync-queue.send.unauthorized-error.content', { orcid : authorizeUrl}))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unsubscribe from all subscriptions
|
||||||
|
*/
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.list$ = null;
|
||||||
|
this.subs.filter((subscription) => hasValue(subscription))
|
||||||
|
.forEach((subscription) => subscription.unsubscribe());
|
||||||
|
}
|
||||||
|
|
||||||
|
isProfileRecord(orcidQueue: OrcidQueue): boolean {
|
||||||
|
return orcidQueue.recordType !== 'Publication' && orcidQueue.recordType !== 'Project';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Reference in New Issue
Block a user