[CST-5337] OAIRE-ELD correction service should support multiple providers.

This commit is contained in:
Pratik Rajkotiya
2022-03-03 12:11:50 +05:30
parent 4ca51387d1
commit 8ce3148dea
30 changed files with 1099 additions and 16 deletions

View File

@@ -0,0 +1,45 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot, Router } from '@angular/router';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { PaginatedList } from '../../../core/data/paginated-list.model';
import { NotificationsBrokerSourceObject } from '../../../core/notifications/broker/models/notifications-broker-source.model';
import { NotificationsBrokerSourceService } from '../../../notifications/broker/source/notifications-broker-source.service';
/**
* This class represents a resolver that retrieve the route data before the route is activated.
*/
@Injectable()
export class SourceDataResolver implements Resolve<Observable<NotificationsBrokerSourceObject[]>> {
/**
* Initialize the effect class variables.
* @param {NotificationsBrokerSourceService} notificationsBrokerSourceService
*/
constructor(
private notificationsBrokerSourceService: NotificationsBrokerSourceService,
private router: Router
) { }
/**
* Method for resolving the parameters in the current route.
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
* @returns Observable<NotificationsBrokerSourceObject[]>
*/
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<NotificationsBrokerSourceObject[]> {
return this.notificationsBrokerSourceService.getSources(5,0).pipe(
map((sources: PaginatedList<NotificationsBrokerSourceObject>) => {
if (sources.page.length === 1) {
this.router.navigate([this.getResolvedUrl(route) + '/' + sources.page[0].id]);
}
return sources.page;
}));
}
/**
*
* @param route url path
* @returns url path
*/
getResolvedUrl(route: ActivatedRouteSnapshot): string {
return route.pathFromRoot.map(v => v.url.map(segment => segment.toString()).join('/')).join('/');
}
}

View File

@@ -0,0 +1,32 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
/**
* Interface for the route parameters.
*/
export interface AdminNotificationsBrokerSourcePageParams {
pageId?: string;
pageSize?: number;
currentPage?: number;
}
/**
* This class represents a resolver that retrieve the route data before the route is activated.
*/
@Injectable()
export class AdminNotificationsBrokerSourcePageResolver implements Resolve<AdminNotificationsBrokerSourcePageParams> {
/**
* Method for resolving the parameters in the current route.
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
* @returns AdminNotificationsBrokerSourcePageParams Emits the route parameters
*/
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): AdminNotificationsBrokerSourcePageParams {
return {
pageId: route.queryParams.pageId,
pageSize: parseInt(route.queryParams.pageSize, 10),
currentPage: parseInt(route.queryParams.page, 10)
};
}
}

View File

@@ -0,0 +1 @@
<ds-notifications-broker-source></ds-notifications-broker-source>

View File

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AdminNotificationsBrokerSourcePageComponent } from './admin-notifications-broker-source-page.component';
describe('AdminNotificationsBrokerSourcePageComponent', () => {
let component: AdminNotificationsBrokerSourcePageComponent;
let fixture: ComponentFixture<AdminNotificationsBrokerSourcePageComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ AdminNotificationsBrokerSourcePageComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(AdminNotificationsBrokerSourcePageComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,7 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'ds-admin-notifications-broker-source-page-component',
templateUrl: './admin-notifications-broker-source-page.component.html',
})
export class AdminNotificationsBrokerSourcePageComponent {}

View File

@@ -9,13 +9,16 @@ import { AdminNotificationsBrokerTopicsPageComponent } from './admin-notificatio
import { AdminNotificationsBrokerEventsPageComponent } from './admin-notifications-broker-events-page/admin-notifications-broker-events-page.component';
import { AdminNotificationsBrokerTopicsPageResolver } from './admin-notifications-broker-topics-page/admin-notifications-broker-topics-page-resolver.service';
import { AdminNotificationsBrokerEventsPageResolver } from './admin-notifications-broker-events-page/admin-notifications-broker-events-page.resolver';
import { AdminNotificationsBrokerSourcePageComponent } from './admin-notifications-broker-source-page-component/admin-notifications-broker-source-page.component';
import { AdminNotificationsBrokerSourcePageResolver } from './admin-notifications-broker-source-page-component/admin-notifications-broker-source-page-resolver.service';
import { SourceDataResolver } from './admin-notifications-broker-source-page-component/admin-notifications-broker-source-data.reslover';
@NgModule({
imports: [
RouterModule.forChild([
{
canActivate: [ AuthenticatedGuard ],
path: `${NOTIFICATIONS_EDIT_PATH}`,
path: `${NOTIFICATIONS_EDIT_PATH}/:sourceId`,
component: AdminNotificationsBrokerTopicsPageComponent,
pathMatch: 'full',
resolve: {
@@ -30,7 +33,23 @@ import { AdminNotificationsBrokerEventsPageResolver } from './admin-notification
},
{
canActivate: [ AuthenticatedGuard ],
path: `${NOTIFICATIONS_EDIT_PATH}/:id`,
path: `${NOTIFICATIONS_EDIT_PATH}`,
component: AdminNotificationsBrokerSourcePageComponent,
pathMatch: 'full',
resolve: {
breadcrumb: I18nBreadcrumbResolver,
openaireBrokerSourceParams: AdminNotificationsBrokerSourcePageResolver,
sourceData: SourceDataResolver
},
data: {
title: 'admin.notifications.source.breadcrumbs',
breadcrumbKey: 'admin.notifications.source',
showBreadcrumbsFluid: false
}
},
{
canActivate: [ AuthenticatedGuard ],
path: `${NOTIFICATIONS_EDIT_PATH}/:sourceId/:topicId`,
component: AdminNotificationsBrokerEventsPageComponent,
pathMatch: 'full',
resolve: {
@@ -48,8 +67,10 @@ import { AdminNotificationsBrokerEventsPageResolver } from './admin-notification
providers: [
I18nBreadcrumbResolver,
I18nBreadcrumbsService,
SourceDataResolver,
AdminNotificationsBrokerTopicsPageResolver,
AdminNotificationsBrokerEventsPageResolver
AdminNotificationsBrokerEventsPageResolver,
AdminNotificationsBrokerSourcePageResolver
]
})
/**

View File

@@ -6,6 +6,7 @@ import { AdminNotificationsRoutingModule } from './admin-notifications-routing.m
import { AdminNotificationsBrokerTopicsPageComponent } from './admin-notifications-broker-topics-page/admin-notifications-broker-topics-page.component';
import { AdminNotificationsBrokerEventsPageComponent } from './admin-notifications-broker-events-page/admin-notifications-broker-events-page.component';
import { NotificationsModule } from '../../notifications/notifications.module';
import { AdminNotificationsBrokerSourcePageComponent } from './admin-notifications-broker-source-page-component/admin-notifications-broker-source-page.component';
@NgModule({
imports: [
@@ -17,7 +18,8 @@ import { NotificationsModule } from '../../notifications/notifications.module';
],
declarations: [
AdminNotificationsBrokerTopicsPageComponent,
AdminNotificationsBrokerEventsPageComponent
AdminNotificationsBrokerEventsPageComponent,
AdminNotificationsBrokerSourcePageComponent
],
entryComponents: []
})

View File

@@ -164,6 +164,7 @@ import { GroupDataService } from './eperson/group-data.service';
import { SubmissionAccessesModel } from './config/models/config-submission-accesses.model';
import { NotificationsBrokerTopicObject } from './notifications/broker/models/notifications-broker-topic.model';
import { NotificationsBrokerEventObject } from './notifications/broker/models/notifications-broker-event.model';
import { NotificationsBrokerSourceObject } from './notifications/broker/models/notifications-broker-source.model';
/**
* When not in production, endpoint responses can be mocked for testing purposes
@@ -349,7 +350,8 @@ export const models =
NotificationsBrokerEventObject,
Root,
SearchConfig,
SubmissionAccessesModel
SubmissionAccessesModel,
NotificationsBrokerSourceObject
];
@NgModule({

View File

@@ -0,0 +1,9 @@
import { ResourceType } from '../../../shared/resource-type';
/**
* The resource type for the Notifications Broker source
*
* Needs to be in a separate file to prevent circular
* dependencies in webpack.
*/
export const NOTIFICATIONS_BROKER_SOURCE_OBJECT = new ResourceType('nbsource');

View File

@@ -0,0 +1,52 @@
import { autoserialize, deserialize } from 'cerialize';
import { CacheableObject } from '../../../cache/object-cache.reducer';
import { excludeFromEquals } from '../../../utilities/equals.decorators';
import { ResourceType } from '../../../shared/resource-type';
import { HALLink } from '../../../shared/hal-link.model';
import { typedObject } from '../../../cache/builders/build-decorators';
import { NOTIFICATIONS_BROKER_SOURCE_OBJECT } from './notifications-broker-source-object.resource-type';
/**
* The interface representing the Notifications Broker source model
*/
@typedObject
export class NotificationsBrokerSourceObject implements CacheableObject {
/**
* A string representing the kind of object, e.g. community, item, …
*/
static type = NOTIFICATIONS_BROKER_SOURCE_OBJECT;
/**
* The Notifications Broker source id
*/
@autoserialize
id: string;
/**
* The date of the last udate from Notifications
*/
@autoserialize
lastEvent: string;
/**
* The total number of suggestions provided by Notifications for this source
*/
@autoserialize
totalEvents: number;
/**
* The type of this ConfigObject
*/
@excludeFromEquals
@autoserialize
type: ResourceType;
/**
* The links to all related resources returned by the rest api.
*/
@deserialize
_links: {
self: HALLink,
};
}

View File

@@ -0,0 +1,133 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { mergeMap, take } from 'rxjs/operators';
import { CoreState } from '../../../core.reducers';
import { HALEndpointService } from '../../../shared/hal-endpoint.service';
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
import { RemoteDataBuildService } from '../../../cache/builders/remote-data-build.service';
import { ObjectCacheService } from '../../../cache/object-cache.service';
import { dataService } from '../../../cache/builders/build-decorators';
import { RequestService } from '../../../data/request.service';
import { FindListOptions } from '../../../data/request.models';
import { DataService } from '../../../data/data.service';
import { ChangeAnalyzer } from '../../../data/change-analyzer';
import { DefaultChangeAnalyzer } from '../../../data/default-change-analyzer.service';
import { RemoteData } from '../../../data/remote-data';
import { NotificationsBrokerSourceObject } from '../models/notifications-broker-source.model';
import { NOTIFICATIONS_BROKER_SOURCE_OBJECT } from '../models/notifications-broker-source-object.resource-type';
import { FollowLinkConfig } from '../../../../shared/utils/follow-link-config.model';
import { PaginatedList } from '../../../data/paginated-list.model';
/* tslint:disable:max-classes-per-file */
/**
* A private DataService implementation to delegate specific methods to.
*/
class DataServiceImpl extends DataService<NotificationsBrokerSourceObject> {
/**
* The REST endpoint.
*/
protected linkPath = 'nbsources';
/**
* Initialize service variables
* @param {RequestService} requestService
* @param {RemoteDataBuildService} rdbService
* @param {Store<CoreState>} store
* @param {ObjectCacheService} objectCache
* @param {HALEndpointService} halService
* @param {NotificationsService} notificationsService
* @param {HttpClient} http
* @param {ChangeAnalyzer<NotificationsBrokerSourceObject>} comparator
*/
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: ChangeAnalyzer<NotificationsBrokerSourceObject>) {
super();
}
}
/**
* The service handling all Notifications Broker source REST requests.
*/
@Injectable()
@dataService(NOTIFICATIONS_BROKER_SOURCE_OBJECT)
export class NotificationsBrokerSourceRestService {
/**
* A private DataService implementation to delegate specific methods to.
*/
private dataService: DataServiceImpl;
/**
* Initialize service variables
* @param {RequestService} requestService
* @param {RemoteDataBuildService} rdbService
* @param {ObjectCacheService} objectCache
* @param {HALEndpointService} halService
* @param {NotificationsService} notificationsService
* @param {HttpClient} http
* @param {DefaultChangeAnalyzer<NotificationsBrokerSourceObject>} comparator
*/
constructor(
protected requestService: RequestService,
protected rdbService: RemoteDataBuildService,
protected objectCache: ObjectCacheService,
protected halService: HALEndpointService,
protected notificationsService: NotificationsService,
protected http: HttpClient,
protected comparator: DefaultChangeAnalyzer<NotificationsBrokerSourceObject>) {
this.dataService = new DataServiceImpl(requestService, rdbService, null, objectCache, halService, notificationsService, http, comparator);
}
/**
* Return the list of Notifications Broker source.
*
* @param options
* Find list options object.
* @param linksToFollow
* List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved.
* @return Observable<RemoteData<PaginatedList<NotificationsBrokerSourceObject>>>
* The list of Notifications Broker source.
*/
public getSources(options: FindListOptions = {}, ...linksToFollow: FollowLinkConfig<NotificationsBrokerSourceObject>[]): Observable<RemoteData<PaginatedList<NotificationsBrokerSourceObject>>> {
return this.dataService.getBrowseEndpoint(options, 'nbsources').pipe(
take(1),
mergeMap((href: string) => this.dataService.findAllByHref(href, options, true, true, ...linksToFollow)),
);
}
/**
* Clear FindAll source requests from cache
*/
public clearFindAllSourceRequests() {
this.requestService.setStaleByHrefSubstring('nbsources');
}
/**
* Return a single Notifications Broker source.
*
* @param id
* The Notifications Broker source id
* @param linksToFollow
* List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved.
* @return Observable<RemoteData<NotificationsBrokerSourceObject>>
* The Notifications Broker source.
*/
public getSource(id: string, ...linksToFollow: FollowLinkConfig<NotificationsBrokerSourceObject>[]): Observable<RemoteData<NotificationsBrokerSourceObject>> {
const options = {};
return this.dataService.getBrowseEndpoint(options, 'nbsources').pipe(
take(1),
mergeMap((href: string) => this.dataService.findByHref(href + '/' + id, true, true, ...linksToFollow))
);
}
}

View File

@@ -135,7 +135,7 @@ export class NotificationsBrokerEventsComponent implements OnInit {
this.isEventPageLoading.next(true);
this.activatedRoute.paramMap.pipe(
map((params) => params.get('id')),
map((params) => params.get('topicId')),
take(1)
).subscribe((id: string) => {
const regEx = /!/g;

View File

@@ -0,0 +1,99 @@
import { Action } from '@ngrx/store';
import { type } from '../../../shared/ngrx/type';
import { NotificationsBrokerSourceObject } from '../../../core/notifications/broker/models/notifications-broker-source.model';
/**
* For each action type in an action group, make a simple
* enum object for all of this group's action types.
*
* The 'type' utility function coerces strings into string
* literal types and runs a simple check to guarantee all
* action types in the application are unique.
*/
export const NotificationsBrokerSourceActionTypes = {
ADD_SOURCE: type('dspace/integration/notifications/broker/ADD_SOURCE'),
RETRIEVE_ALL_SOURCE: type('dspace/integration/notifications/broker/RETRIEVE_ALL_SOURCE'),
RETRIEVE_ALL_SOURCE_ERROR: type('dspace/integration/notifications/broker/RETRIEVE_ALL_SOURCE_ERROR'),
};
/* tslint:disable:max-classes-per-file */
/**
* An ngrx action to retrieve all the Notifications Broker source.
*/
export class RetrieveAllSourceAction implements Action {
type = NotificationsBrokerSourceActionTypes.RETRIEVE_ALL_SOURCE;
payload: {
elementsPerPage: number;
currentPage: number;
};
/**
* Create a new RetrieveAllSourceAction.
*
* @param elementsPerPage
* the number of source per page
* @param currentPage
* The page number to retrieve
*/
constructor(elementsPerPage: number, currentPage: number) {
this.payload = {
elementsPerPage,
currentPage
};
}
}
/**
* An ngrx action for retrieving 'all Notifications Broker source' error.
*/
export class RetrieveAllSourceErrorAction implements Action {
type = NotificationsBrokerSourceActionTypes.RETRIEVE_ALL_SOURCE_ERROR;
}
/**
* An ngrx action to load the Notifications Broker source objects.
* Called by the ??? effect.
*/
export class AddSourceAction implements Action {
type = NotificationsBrokerSourceActionTypes.ADD_SOURCE;
payload: {
source: NotificationsBrokerSourceObject[];
totalPages: number;
currentPage: number;
totalElements: number;
};
/**
* Create a new AddSourceAction.
*
* @param source
* the list of source
* @param totalPages
* the total available pages of source
* @param currentPage
* the current page
* @param totalElements
* the total available Notifications Broker source
*/
constructor(source: NotificationsBrokerSourceObject[], totalPages: number, currentPage: number, totalElements: number) {
this.payload = {
source,
totalPages,
currentPage,
totalElements
};
}
}
/* tslint:enable:max-classes-per-file */
/**
* Export a type alias of all actions in this action group
* so that reducers can easily compose action types.
*/
export type NotificationsBrokerSourceActions
= RetrieveAllSourceAction
|RetrieveAllSourceErrorAction
|AddSourceAction;

View File

@@ -0,0 +1,58 @@
<div class="container">
<div class="row">
<div class="col-12">
<h2 class="border-bottom pb-2">{{'notifications.broker.title'| translate}}</h2>
<p>{{'notifications.broker.source.description'| translate}}</p>
</div>
</div>
<div class="row">
<div class="col-12">
<h3 class="border-bottom pb-2">{{'notifications.broker.source'| translate}}</h3>
<ds-loading class="container" *ngIf="(isSourceLoading() | async)" message="{{'notifications.broker.loading' | translate}}"></ds-loading>
<ds-pagination *ngIf="!(isSourceLoading() | async)"
[paginationOptions]="paginationConfig"
[collectionSize]="(totalElements$ | async)"
[hideGear]="false"
[hideSortOptions]="true"
(paginationChange)="getNotificationsBrokerSource()">
<ds-loading class="container" *ngIf="(isSourceProcessing() | async)" message="'notifications.broker.loading' | translate"></ds-loading>
<ng-container *ngIf="!(isSourceProcessing() | async)">
<div *ngIf="(sources$|async)?.length == 0" class="alert alert-info w-100 mb-2 mt-2" role="alert">
{{'notifications.broker.noSource' | translate}}
</div>
<div *ngIf="(sources$|async)?.length != 0" class="table-responsive mt-2">
<table id="epeople" class="table table-striped table-hover table-bordered">
<thead>
<tr>
<th scope="col">{{'notifications.broker.table.source' | translate}}</th>
<th scope="col">{{'notifications.broker.table.last-event' | translate}}</th>
<th scope="col">{{'notifications.broker.table.actions' | translate}}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let sourceElement of (sources$ | async); let i = index">
<td>{{sourceElement.id}}</td>
<td>{{sourceElement.lastEvent}}</td>
<td>
<div class="btn-group edit-field">
<button
class="btn btn-outline-primary btn-sm"
title="{{'notifications.broker.button.detail' | translate }}"
[routerLink]="[sourceElement.id]">
<span class="badge badge-info">{{sourceElement.totalEvents}}</span>
<i class="fas fa-info fa-fw"></i>
</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</ng-container>
</ds-pagination>
</div>
</div>
</div>

View File

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { NotificationsBrokerSourceComponent } from './notifications-broker-source.component';
describe('NotificationsBrokerSourceComponent', () => {
let component: NotificationsBrokerSourceComponent;
let fixture: ComponentFixture<NotificationsBrokerSourceComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ NotificationsBrokerSourceComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(NotificationsBrokerSourceComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,139 @@
import { Component, OnInit } from '@angular/core';
import { PaginationService } from '../../../core/pagination/pagination.service';
import { Observable, Subscription } from 'rxjs';
import { distinctUntilChanged, take } from 'rxjs/operators';
import { SortOptions } from '../../../core/cache/models/sort-options.model';
import { NotificationsBrokerSourceObject } from '../../../core/notifications/broker/models/notifications-broker-source.model';
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
import { NotificationsStateService } from '../../notifications-state.service';
import { AdminNotificationsBrokerSourcePageParams } from '../../../admin/admin-notifications/admin-notifications-broker-source-page-component/admin-notifications-broker-source-page-resolver.service';
import { hasValue } from '../../../shared/empty.util';
@Component({
selector: 'ds-notifications-broker-source',
templateUrl: './notifications-broker-source.component.html',
styleUrls: ['./notifications-broker-source.component.scss']
})
export class NotificationsBrokerSourceComponent implements OnInit {
/**
* The pagination system configuration for HTML listing.
* @type {PaginationComponentOptions}
*/
public paginationConfig: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), {
id: 'btp',
pageSize: 10,
pageSizeOptions: [5, 10, 20, 40, 60]
});
/**
* The Notifications Broker source list sort options.
* @type {SortOptions}
*/
public paginationSortConfig: SortOptions;
/**
* The Notifications Broker source list.
*/
public sources$: Observable<NotificationsBrokerSourceObject[]>;
/**
* The total number of Notifications Broker sources.
*/
public totalElements$: Observable<number>;
/**
* Array to track all the component subscriptions. Useful to unsubscribe them with 'onDestroy'.
* @type {Array}
*/
protected subs: Subscription[] = [];
/**
* Initialize the component variables.
* @param {PaginationService} paginationService
* @param {NotificationsStateService} notificationsStateService
*/
constructor(
private paginationService: PaginationService,
private notificationsStateService: NotificationsStateService,
) { }
/**
* Component initialization.
*/
ngOnInit(): void {
this.sources$ = this.notificationsStateService.getNotificationsBrokerSource();
this.totalElements$ = this.notificationsStateService.getNotificationsBrokerSourceTotals();
}
/**
* First Notifications Broker source loading after view initialization.
*/
ngAfterViewInit(): void {
this.subs.push(
this.notificationsStateService.isNotificationsBrokerSourceLoaded().pipe(
take(1)
).subscribe(() => {
this.getNotificationsBrokerSource();
})
);
}
/**
* Returns the information about the loading status of the Notifications Broker source (if it's running or not).
*
* @return Observable<boolean>
* 'true' if the source are loading, 'false' otherwise.
*/
public isSourceLoading(): Observable<boolean> {
return this.notificationsStateService.isNotificationsBrokerSourceLoading();
}
/**
* Returns the information about the processing status of the Notifications Broker source (if it's running or not).
*
* @return Observable<boolean>
* 'true' if there are operations running on the source (ex.: a REST call), 'false' otherwise.
*/
public isSourceProcessing(): Observable<boolean> {
return this.notificationsStateService.isNotificationsBrokerSourceProcessing();
}
/**
* Dispatch the Notifications Broker source retrival.
*/
public getNotificationsBrokerSource(): void {
this.paginationService.getCurrentPagination(this.paginationConfig.id, this.paginationConfig).pipe(
distinctUntilChanged(),
).subscribe((options: PaginationComponentOptions) => {
this.notificationsStateService.dispatchRetrieveNotificationsBrokerSource(
options.pageSize,
options.currentPage
);
});
}
/**
* Update pagination Config from route params
*
* @param eventsRouteParams
*/
protected updatePaginationFromRouteParams(eventsRouteParams: AdminNotificationsBrokerSourcePageParams) {
if (eventsRouteParams.currentPage) {
this.paginationConfig.currentPage = eventsRouteParams.currentPage;
}
if (eventsRouteParams.pageSize) {
if (this.paginationConfig.pageSizeOptions.includes(eventsRouteParams.pageSize)) {
this.paginationConfig.pageSize = eventsRouteParams.pageSize;
} else {
this.paginationConfig.pageSize = this.paginationConfig.pageSizeOptions[0];
}
}
}
/**
* Unsubscribe from all subscriptions.
*/
ngOnDestroy(): void {
this.subs
.filter((sub) => hasValue(sub))
.forEach((sub) => sub.unsubscribe());
}
}

View File

@@ -0,0 +1,87 @@
import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { Actions, Effect, ofType } from '@ngrx/effects';
import { TranslateService } from '@ngx-translate/core';
import { catchError, map, switchMap, tap, withLatestFrom } from 'rxjs/operators';
import { of as observableOf } from 'rxjs';
import {
AddSourceAction,
NotificationsBrokerSourceActionTypes,
RetrieveAllSourceAction,
RetrieveAllSourceErrorAction,
} from './notifications-broker-source.actions';
import { NotificationsBrokerSourceObject } from '../../../core/notifications/broker/models/notifications-broker-source.model';
import { PaginatedList } from '../../../core/data/paginated-list.model';
import { NotificationsBrokerSourceService } from './notifications-broker-source.service';
import { NotificationsService } from '../../../shared/notifications/notifications.service';
import { NotificationsBrokerSourceRestService } from '../../../core/notifications/broker/source/notifications-broker-source-rest.service';
/**
* Provides effect methods for the Notifications Broker source actions.
*/
@Injectable()
export class NotificationsBrokerSourceEffects {
/**
* Retrieve all Notifications Broker source managing pagination and errors.
*/
@Effect() retrieveAllSource$ = this.actions$.pipe(
ofType(NotificationsBrokerSourceActionTypes.RETRIEVE_ALL_SOURCE),
withLatestFrom(this.store$),
switchMap(([action, currentState]: [RetrieveAllSourceAction, any]) => {
return this.notificationsBrokerSourceService.getSources(
action.payload.elementsPerPage,
action.payload.currentPage
).pipe(
map((sources: PaginatedList<NotificationsBrokerSourceObject>) =>
new AddSourceAction(sources.page, sources.totalPages, sources.currentPage, sources.totalElements)
),
catchError((error: Error) => {
if (error) {
console.error(error.message);
}
return observableOf(new RetrieveAllSourceErrorAction());
})
);
})
);
/**
* Show a notification on error.
*/
@Effect({ dispatch: false }) retrieveAllSourceErrorAction$ = this.actions$.pipe(
ofType(NotificationsBrokerSourceActionTypes.RETRIEVE_ALL_SOURCE_ERROR),
tap(() => {
this.notificationsService.error(null, this.translate.get('notifications.broker.source.error.service.retrieve'));
})
);
/**
* Clear find all source requests from cache.
*/
@Effect({ dispatch: false }) addSourceAction$ = this.actions$.pipe(
ofType(NotificationsBrokerSourceActionTypes.ADD_SOURCE),
tap(() => {
this.notificationsBrokerSourceDataService.clearFindAllSourceRequests();
})
);
/**
* Initialize the effect class variables.
* @param {Actions} actions$
* @param {Store<any>} store$
* @param {TranslateService} translate
* @param {NotificationsService} notificationsService
* @param {NotificationsBrokerSourceService} notificationsBrokerSourceService
* @param {NotificationsBrokerSourceRestService} notificationsBrokerSourceDataService
*/
constructor(
private actions$: Actions,
private store$: Store<any>,
private translate: TranslateService,
private notificationsService: NotificationsService,
private notificationsBrokerSourceService: NotificationsBrokerSourceService,
private notificationsBrokerSourceDataService: NotificationsBrokerSourceRestService
) { }
}

View File

@@ -0,0 +1,72 @@
import { NotificationsBrokerSourceObject } from '../../../core/notifications/broker/models/notifications-broker-source.model';
import { NotificationsBrokerSourceActionTypes, NotificationsBrokerSourceActions } from './notifications-broker-source.actions';
/**
* The interface representing the Notifications Broker source state.
*/
export interface NotificationsBrokerSourceState {
source: NotificationsBrokerSourceObject[];
processing: boolean;
loaded: boolean;
totalPages: number;
currentPage: number;
totalElements: number;
}
/**
* Used for the Notifications Broker source state initialization.
*/
const notificationsBrokerSourceInitialState: NotificationsBrokerSourceState = {
source: [],
processing: false,
loaded: false,
totalPages: 0,
currentPage: 0,
totalElements: 0
};
/**
* The Notifications Broker Source Reducer
*
* @param state
* the current state initialized with notificationsBrokerSourceInitialState
* @param action
* the action to perform on the state
* @return NotificationsBrokerSourceState
* the new state
*/
export function notificationsBrokerSourceReducer(state = notificationsBrokerSourceInitialState, action: NotificationsBrokerSourceActions): NotificationsBrokerSourceState {
switch (action.type) {
case NotificationsBrokerSourceActionTypes.RETRIEVE_ALL_SOURCE: {
return Object.assign({}, state, {
source: [],
processing: true
});
}
case NotificationsBrokerSourceActionTypes.ADD_SOURCE: {
return Object.assign({}, state, {
source: action.payload.source,
processing: false,
loaded: true,
totalPages: action.payload.totalPages,
currentPage: state.currentPage,
totalElements: action.payload.totalElements
});
}
case NotificationsBrokerSourceActionTypes.RETRIEVE_ALL_SOURCE_ERROR: {
return Object.assign({}, state, {
processing: false,
loaded: true,
totalPages: 0,
currentPage: 0,
totalElements: 0
});
}
default: {
return state;
}
}
}

View File

@@ -0,0 +1,55 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { find, map } from 'rxjs/operators';
import { NotificationsBrokerSourceRestService } from '../../../core/notifications/broker/source/notifications-broker-source-rest.service';
import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model';
import { FindListOptions } from '../../../core/data/request.models';
import { RemoteData } from '../../../core/data/remote-data';
import { PaginatedList } from '../../../core/data/paginated-list.model';
import { NotificationsBrokerSourceObject } from '../../../core/notifications/broker/models/notifications-broker-source.model';
/**
* The service handling all Notifications Broker source requests to the REST service.
*/
@Injectable()
export class NotificationsBrokerSourceService {
/**
* Initialize the service variables.
* @param {NotificationsBrokerSourceRestService} notificationsBrokerSourceRestService
*/
constructor(
private notificationsBrokerSourceRestService: NotificationsBrokerSourceRestService
) { }
/**
* Return the list of Notifications Broker source managing pagination and errors.
*
* @param elementsPerPage
* The number of the source per page
* @param currentPage
* The page number to retrieve
* @return Observable<PaginatedList<NotificationsBrokerSourceObject>>
* The list of Notifications Broker source.
*/
public getSources(elementsPerPage, currentPage): Observable<PaginatedList<NotificationsBrokerSourceObject>> {
const sortOptions = new SortOptions('name', SortDirection.ASC);
const findListOptions: FindListOptions = {
elementsPerPage: elementsPerPage,
currentPage: currentPage,
sort: sortOptions
};
return this.notificationsBrokerSourceRestService.getSources(findListOptions).pipe(
find((rd: RemoteData<PaginatedList<NotificationsBrokerSourceObject>>) => !rd.isResponsePending),
map((rd: RemoteData<PaginatedList<NotificationsBrokerSourceObject>>) => {
if (rd.hasSucceeded) {
return rd.payload;
} else {
throw new Error('Can\'t retrieve Notifications Broker source from the Broker source REST service');
}
})
);
}
}

View File

@@ -2,7 +2,7 @@
<div class="row">
<div class="col-12">
<h2 class="border-bottom pb-2">{{'notifications.broker.title'| translate}}</h2>
<p>{{'notifications.broker.topics.description'| translate}}</p>
<p>{{'notifications.broker.topics.description'| translate:{source: sourceId} }}</p>
</div>
</div>
<div class="row">

View File

@@ -1,7 +1,7 @@
import { Component, OnInit } from '@angular/core';
import { Observable, Subscription } from 'rxjs';
import { distinctUntilChanged, take } from 'rxjs/operators';
import { distinctUntilChanged, map, take } from 'rxjs/operators';
import { SortOptions } from '../../../core/cache/models/sort-options.model';
import { NotificationsBrokerTopicObject } from '../../../core/notifications/broker/models/notifications-broker-topic.model';
@@ -10,6 +10,8 @@ import { PaginationComponentOptions } from '../../../shared/pagination/paginatio
import { NotificationsStateService } from '../../notifications-state.service';
import { AdminNotificationsBrokerTopicsPageParams } from '../../../admin/admin-notifications/admin-notifications-broker-topics-page/admin-notifications-broker-topics-page-resolver.service';
import { PaginationService } from '../../../core/pagination/pagination.service';
import { ActivatedRoute } from '@angular/router';
import { NotificationsBrokerTopicsService } from './notifications-broker-topics.service';
/**
* Component to display the Notifications Broker topic list.
@@ -48,6 +50,12 @@ export class NotificationsBrokerTopicsComponent implements OnInit {
*/
protected subs: Subscription[] = [];
/**
* This property represents a sourceId which is used to retrive a topic
* @type {string}
*/
public sourceId: string;
/**
* Initialize the component variables.
* @param {PaginationService} paginationService
@@ -55,8 +63,18 @@ export class NotificationsBrokerTopicsComponent implements OnInit {
*/
constructor(
private paginationService: PaginationService,
private activatedRoute: ActivatedRoute,
private notificationsStateService: NotificationsStateService,
) { }
private notificationsBrokerTopicsService: NotificationsBrokerTopicsService
) {
this.activatedRoute.paramMap.pipe(
map((params) => params.get('sourceId')),
take(1)
).subscribe((id: string) => {
this.sourceId = id;
this.notificationsBrokerTopicsService.setSourceId(this.sourceId);
});
}
/**
* Component initialization.

View File

@@ -7,6 +7,7 @@ import { FindListOptions } from '../../../core/data/request.models';
import { RemoteData } from '../../../core/data/remote-data';
import { PaginatedList } from '../../../core/data/paginated-list.model';
import { NotificationsBrokerTopicObject } from '../../../core/notifications/broker/models/notifications-broker-topic.model';
import { RequestParam } from '../../../core/cache/models/request-param.model';
/**
* The service handling all Notifications Broker topic requests to the REST service.
@@ -22,6 +23,11 @@ export class NotificationsBrokerTopicsService {
private notificationsBrokerTopicRestService: NotificationsBrokerTopicRestService
) { }
/**
* sourceId used to get topics
*/
sourceId: string;
/**
* Return the list of Notifications Broker topics managing pagination and errors.
*
@@ -38,7 +44,8 @@ export class NotificationsBrokerTopicsService {
const findListOptions: FindListOptions = {
elementsPerPage: elementsPerPage,
currentPage: currentPage,
sort: sortOptions
sort: sortOptions,
searchParams: [new RequestParam('source', this.sourceId)]
};
return this.notificationsBrokerTopicRestService.getTopics(findListOptions).pipe(
@@ -52,4 +59,12 @@ export class NotificationsBrokerTopicsService {
})
);
}
/**
* set sourceId which is used to get topics
* @param sourceId string
*/
setSourceId(sourceId: string) {
this.sourceId = sourceId;
}
}

View File

@@ -8,11 +8,19 @@ import {
getNotificationsBrokerTopicsTotalsSelector,
isNotificationsBrokerTopicsLoadedSelector,
notificationsBrokerTopicsObjectSelector,
isNotificationsBrokerTopicsProcessingSelector
isNotificationsBrokerTopicsProcessingSelector,
notificationsBrokerSourceObjectSelector,
isNotificationsBrokerSourceLoadedSelector,
isNotificationsBrokerSourceProcessingSelector,
getNotificationsBrokerSourceTotalPagesSelector,
getNotificationsBrokerSourceCurrentPageSelector,
getNotificationsBrokerSourceTotalsSelector
} from './selectors';
import { NotificationsBrokerTopicObject } from '../core/notifications/broker/models/notifications-broker-topic.model';
import { NotificationsState } from './notifications.reducer';
import { RetrieveAllTopicsAction } from './broker/topics/notifications-broker-topics.actions';
import { NotificationsBrokerSourceObject } from '../core/notifications/broker/models/notifications-broker-source.model';
import { RetrieveAllSourceAction } from './broker/source/notifications-broker-source.actions';
/**
* The service handling the Notifications State.
@@ -113,4 +121,92 @@ export class NotificationsStateService {
public dispatchRetrieveNotificationsBrokerTopics(elementsPerPage: number, currentPage: number): void {
this.store.dispatch(new RetrieveAllTopicsAction(elementsPerPage, currentPage));
}
// Notifications Broker source
// --------------------------------------------------------------------------
/**
* Returns the list of Notifications Broker source from the state.
*
* @return Observable<NotificationsBrokerSourceObject>
* The list of Notifications Broker source.
*/
public getNotificationsBrokerSource(): Observable<NotificationsBrokerSourceObject[]> {
return this.store.pipe(select(notificationsBrokerSourceObjectSelector()));
}
/**
* Returns the information about the loading status of the Notifications Broker source (if it's running or not).
*
* @return Observable<boolean>
* 'true' if the source are loading, 'false' otherwise.
*/
public isNotificationsBrokerSourceLoading(): Observable<boolean> {
return this.store.pipe(
select(isNotificationsBrokerSourceLoadedSelector),
map((loaded: boolean) => !loaded)
);
}
/**
* Returns the information about the loading status of the Notifications Broker source (whether or not they were loaded).
*
* @return Observable<boolean>
* 'true' if the source are loaded, 'false' otherwise.
*/
public isNotificationsBrokerSourceLoaded(): Observable<boolean> {
return this.store.pipe(select(isNotificationsBrokerSourceLoadedSelector));
}
/**
* Returns the information about the processing status of the Notifications Broker source (if it's running or not).
*
* @return Observable<boolean>
* 'true' if there are operations running on the source (ex.: a REST call), 'false' otherwise.
*/
public isNotificationsBrokerSourceProcessing(): Observable<boolean> {
return this.store.pipe(select(isNotificationsBrokerSourceProcessingSelector));
}
/**
* Returns, from the state, the total available pages of the Notifications Broker source.
*
* @return Observable<number>
* The number of the Notifications Broker source pages.
*/
public getNotificationsBrokerSourceTotalPages(): Observable<number> {
return this.store.pipe(select(getNotificationsBrokerSourceTotalPagesSelector));
}
/**
* Returns the current page of the Notifications Broker source, from the state.
*
* @return Observable<number>
* The number of the current Notifications Broker source page.
*/
public getNotificationsBrokerSourceCurrentPage(): Observable<number> {
return this.store.pipe(select(getNotificationsBrokerSourceCurrentPageSelector));
}
/**
* Returns the total number of the Notifications Broker source.
*
* @return Observable<number>
* The number of the Notifications Broker source.
*/
public getNotificationsBrokerSourceTotals(): Observable<number> {
return this.store.pipe(select(getNotificationsBrokerSourceTotalsSelector));
}
/**
* Dispatch a request to change the Notifications Broker source state, retrieving the source from the server.
*
* @param elementsPerPage
* The number of the source per page.
* @param currentPage
* The number of the current page.
*/
public dispatchRetrieveNotificationsBrokerSource(elementsPerPage: number, currentPage: number): void {
this.store.dispatch(new RetrieveAllSourceAction(elementsPerPage, currentPage));
}
}

View File

@@ -1,5 +1,7 @@
import { NotificationsBrokerSourceEffects } from './broker/source/notifications-broker-source.effects';
import { NotificationsBrokerTopicsEffects } from './broker/topics/notifications-broker-topics.effects';
export const notificationsEffects = [
NotificationsBrokerTopicsEffects
NotificationsBrokerTopicsEffects,
NotificationsBrokerSourceEffects
];

View File

@@ -17,6 +17,9 @@ import { NotificationsBrokerEventRestService } from '../core/notifications/broke
import { ProjectEntryImportModalComponent } from './broker/project-entry-import-modal/project-entry-import-modal.component';
import { TranslateModule } from '@ngx-translate/core';
import { SearchModule } from '../shared/search/search.module';
import { NotificationsBrokerSourceComponent } from './broker/source/notifications-broker-source.component';
import { NotificationsBrokerSourceService } from './broker/source/notifications-broker-source.service';
import { NotificationsBrokerSourceRestService } from '../core/notifications/broker/source/notifications-broker-source-rest.service';
const MODULES = [
CommonModule,
@@ -29,7 +32,8 @@ const MODULES = [
const COMPONENTS = [
NotificationsBrokerTopicsComponent,
NotificationsBrokerEventsComponent
NotificationsBrokerEventsComponent,
NotificationsBrokerSourceComponent
];
const DIRECTIVES = [ ];
@@ -41,7 +45,9 @@ const ENTRY_COMPONENTS = [
const PROVIDERS = [
NotificationsStateService,
NotificationsBrokerTopicsService,
NotificationsBrokerSourceService,
NotificationsBrokerTopicRestService,
NotificationsBrokerSourceRestService,
NotificationsBrokerEventRestService
];

View File

@@ -1,5 +1,5 @@
import { ActionReducerMap, createFeatureSelector } from '@ngrx/store';
import { notificationsBrokerSourceReducer, NotificationsBrokerSourceState } from './broker/source/notifications-broker-source.reducer';
import { notificationsBrokerTopicsReducer, NotificationsBrokerTopicState, } from './broker/topics/notifications-broker-topics.reducer';
/**
@@ -7,10 +7,12 @@ import { notificationsBrokerTopicsReducer, NotificationsBrokerTopicState, } from
*/
export interface NotificationsState {
'brokerTopic': NotificationsBrokerTopicState;
'brokerSource': NotificationsBrokerSourceState;
}
export const notificationsReducers: ActionReducerMap<NotificationsState> = {
brokerTopic: notificationsBrokerTopicsReducer,
brokerSource: notificationsBrokerSourceReducer
};
export const notificationsSelector = createFeatureSelector<NotificationsState>('notifications');

View File

@@ -3,6 +3,8 @@ import { subStateSelector } from '../shared/selector.util';
import { notificationsSelector, NotificationsState } from './notifications.reducer';
import { NotificationsBrokerTopicObject } from '../core/notifications/broker/models/notifications-broker-topic.model';
import { NotificationsBrokerTopicState } from './broker/topics/notifications-broker-topics.reducer';
import { NotificationsBrokerSourceState } from './broker/source/notifications-broker-source.reducer';
import { NotificationsBrokerSourceObject } from '../core/notifications/broker/models/notifications-broker-source.model';
/**
* Returns the Notifications state.
@@ -77,3 +79,69 @@ export const getNotificationsBrokerTopicsCurrentPageSelector = createSelector(_g
export const getNotificationsBrokerTopicsTotalsSelector = createSelector(_getNotificationsState,
(state: NotificationsState) => state.brokerTopic.totalElements
);
// Notifications Broker source
// ----------------------------------------------------------------------------
/**
* Returns the Notifications Broker source State.
* @function notificationsBrokerSourceStateSelector
* @return {NotificationsBrokerSourceState}
*/
export function notificationsBrokerSourceStateSelector(): MemoizedSelector<NotificationsState, NotificationsBrokerSourceState> {
return subStateSelector<NotificationsState,NotificationsBrokerSourceState>(notificationsSelector, 'brokerSource');
}
/**
* Returns the Notifications Broker source list.
* @function notificationsBrokerSourceObjectSelector
* @return {NotificationsBrokerSourceObject[]}
*/
export function notificationsBrokerSourceObjectSelector(): MemoizedSelector<NotificationsState, NotificationsBrokerSourceObject[]> {
return subStateSelector<NotificationsState, NotificationsBrokerSourceObject[]>(notificationsBrokerSourceStateSelector(), 'source');
}
/**
* Returns true if the Notifications Broker source are loaded.
* @function isNotificationsBrokerSourceLoadedSelector
* @return {boolean}
*/
export const isNotificationsBrokerSourceLoadedSelector = createSelector(_getNotificationsState,
(state: NotificationsState) => state.brokerSource.loaded
);
/**
* Returns true if the deduplication sets are processing.
* @function isDeduplicationSetsProcessingSelector
* @return {boolean}
*/
export const isNotificationsBrokerSourceProcessingSelector = createSelector(_getNotificationsState,
(state: NotificationsState) => state.brokerSource.processing
);
/**
* Returns the total available pages of Notifications Broker source.
* @function getNotificationsBrokerSourceTotalPagesSelector
* @return {number}
*/
export const getNotificationsBrokerSourceTotalPagesSelector = createSelector(_getNotificationsState,
(state: NotificationsState) => state.brokerSource.totalPages
);
/**
* Returns the current page of Notifications Broker source.
* @function getNotificationsBrokerSourceCurrentPageSelector
* @return {number}
*/
export const getNotificationsBrokerSourceCurrentPageSelector = createSelector(_getNotificationsState,
(state: NotificationsState) => state.brokerSource.currentPage
);
/**
* Returns the total number of Notifications Broker source.
* @function getNotificationsBrokerSourceTotalsSelector
* @return {number}
*/
export const getNotificationsBrokerSourceTotalsSelector = createSelector(_getNotificationsState,
(state: NotificationsState) => state.brokerSource.totalElements
);

View File

@@ -487,6 +487,8 @@
"admin.notifications.broker.page.title": "Notifications Broker",
"admin.notifications.source.breadcrumbs": "Notifications Source",
"admin.search.breadcrumbs": "Administrative Search",
"admin.search.collection.edit": "Edit",
@@ -2713,14 +2715,20 @@
"none.listelement.badge": "Item",
"notifications.broker.title": "{{source}} Broker",
"notifications.broker.title": "Broker Title",
"notifications.broker.topics.description": "Below you can see all the topics received from the subscriptions to {{source}}.",
"notifications.broker.source.description": "Below you can see all the sources.",
"notifications.broker.topics": "Current Topics",
"notifications.broker.source": "Current Sources",
"notifications.broker.table.topic": "Topic",
"notifications.broker.table.source": "Source",
"notifications.broker.table.last-event": "Last Event",
"notifications.broker.table.actions": "Actions",
@@ -2729,10 +2737,14 @@
"notifications.broker.noTopics": "No topics found.",
"notifications.broker.noSource": "No sources found.",
"notifications.events.title": "{{source}} Broker Suggestions",
"notifications.broker.topic.error.service.retrieve": "An error occurred while loading the Notifications Broker topics",
"notifications.broker.source.error.service.retrieve": "An error occurred while loading the Notifications Broker source",
"notifications.broker.events.description": "Below the list of all the suggestions, received from {{source}}, for the selected topic.",
"notifications.broker.loading": "Loading ...",