[CST-5537] Fix issues and refactoring in order to remove nested subscriptions

This commit is contained in:
Giuseppe Digilio
2022-11-11 12:56:57 +01:00
parent 58db3c7b0e
commit 3188374800
11 changed files with 196 additions and 154 deletions

View File

@@ -6,4 +6,4 @@ import { ResourceType } from '../../../shared/resource-type';
* Needs to be in a separate file to prevent circular
* dependencies in webpack.
*/
export const QUALITY_ASSURANCE_EVENT_OBJECT = new ResourceType('qaevent');
export const QUALITY_ASSURANCE_EVENT_OBJECT = new ResourceType('qualityassuranceevent');

View File

@@ -1,21 +1,23 @@
<div class="container">
<div class="row">
<div class="col-12">
<h2 class="border-bottom pb-2">{{'notifications.events.title'| translate}}</h2>
<p>{{'quality-assurance.events.description'| translate}}</p>
<p>
<a class="btn btn-outline-secondary" [routerLink]="['/admin/notifications/quality-assurance']">
<i class="fas fa-angle-double-left"></i>
{{'quality-assurance.events.back' | translate}}
</a>
</p>
<h2 class="border-bottom pb-2">
<div class="d-flex justify-content-between">
{{'notifications.events.title'| translate}}
<a class="btn btn-outline-secondary" [routerLink]="['/admin/notifications/quality-assurance']">
<i class="fas fa-angle-double-left"></i>
{{'quality-assurance.events.back' | translate}}
</a>
</div>
</h2>
<ds-alert [type]="'alert-info'" [content]="'quality-assurance.events.description'"></ds-alert>
</div>
</div>
<div class="row">
<div class="col-12">
<h3 class="border-bottom pb-2">
<h4 class="border-bottom pb-2">
{{'quality-assurance.events.topic' | translate}} {{this.showTopic}}
</h3>
</h4>
<ds-loading class="container" *ngIf="(isEventPageLoading | async)" message="{{'quality-assurance.loading' | translate}}"></ds-loading>
@@ -25,8 +27,7 @@
[sortOptions]="paginationSortConfig"
(paginationChange)="getQualityAssuranceEvents()">
<ds-loading class="container" *ngIf="(isEventLoading | async)" message="{{'quality-assurance.loading' | translate}}"></ds-loading>
<ng-container *ngIf="!(isEventLoading | async)">
<ng-container>
<div *ngIf="(eventsUpdated$|async)?.length == 0" class="alert alert-info w-100 mb-2 mt-2" role="alert">
{{'quality-assurance.noEvents' | translate}}
</div>
@@ -34,11 +35,15 @@
<table id="events" class="table table-striped table-hover table-bordered">
<thead>
<tr>
<th scope="col">{{'quality-assurance.event.table.trust' | translate}}</th>
<th scope="col">{{'quality-assurance.event.table.publication' | translate}}</th>
<th *ngIf="hasDetailColumn() && showTopic.indexOf('/PROJECT') == -1" scope="col">{{'quality-assurance.event.table.details' | translate}}</th>
<th *ngIf="hasDetailColumn() && showTopic.indexOf('/PROJECT') !== -1" scope="col">{{'quality-assurance.event.table.project-details' | translate}}</th>
<th scope="col" class="button-rows">{{'quality-assurance.event.table.actions' | translate}}</th>
<th scope="col" class="trust-col">{{'quality-assurance.event.table.trust' | translate}}</th>
<th scope="col" class="title-col">{{'quality-assurance.event.table.publication' | translate}}</th>
<th *ngIf="hasDetailColumn() && showTopic.indexOf('/PROJECT') == -1" scope="col" class="content-col">
{{'quality-assurance.event.table.details' | translate}}
</th>
<th *ngIf="hasDetailColumn() && showTopic.indexOf('/PROJECT') !== -1" scope="col" class="content-col">
{{'quality-assurance.event.table.project-details' | translate}}
</th>
<th scope="col" class="button-col">{{'quality-assurance.event.table.actions' | translate}}</th>
</tr>
</thead>
<tbody>
@@ -51,7 +56,7 @@
<span *ngIf="!eventElement?.target">{{eventElement.title}}</span>
</td>
<td *ngIf="showTopic.indexOf('/PID') !== -1">
<p><span class="small">{{'quality-assurance.event.table.pidtype' | translate}}</span> <span class="badge badge-info">{{eventElement.event.message.type}}</span></p>
<p><span class="small">{{'quality-assurance.event.table.pidtype' | translate}}</span>&nbsp;<span class="badge badge-info">{{eventElement.event.message.type}}</span></p>
<p><span class="small">{{'quality-assurance.event.table.pidvalue' | translate}}</span><br>
<a *ngIf="hasPIDHref(eventElement.event.message); else noPID" href="{{getPIDHref(eventElement.event.message)}}" target="_blank">
{{eventElement.event.message.value}}
@@ -82,18 +87,19 @@
<a href="https://explore.openaire.eu/search/project?projectId={{ eventElement.event.message.openaireId}}" target="_blank">{{eventElement.event.message.title}}</a>
</p>
<p>
<span *ngIf="eventElement.event.message.acronym"><span class="small">{{'quality-assurance.event.table.acronym' | translate}}</span> <span class="badge badge-info">{{eventElement.event.message.acronym}}</span><br></span>
<span *ngIf="eventElement.event.message.code"><span class="small">{{'quality-assurance.event.table.code' | translate}}</span> <span class="badge badge-info">{{eventElement.event.message.code}}</span><br></span>
<span *ngIf="eventElement.event.message.funder"><span class="small">{{'quality-assurance.event.table.funder' | translate}}</span> <span class="badge badge-info">{{eventElement.event.message.funder}}</span><br></span>
<span *ngIf="eventElement.event.message.fundingProgram"><span class="small">{{'quality-assurance.event.table.fundingProgram' | translate}}</span> <span class="badge badge-info">{{eventElement.event.message.fundingProgram}}</span><br></span>
<span *ngIf="eventElement.event.message.jurisdiction"><span class="small">{{'quality-assurance.event.table.jurisdiction' | translate}}</span> <span class="badge badge-info">{{eventElement.event.message.jurisdiction}}</span></span>
<span *ngIf="eventElement.event.message.acronym"><span class="small">{{'quality-assurance.event.table.acronym' | translate}}</span>&nbsp;<span class="badge badge-info">{{eventElement.event.message.acronym}}</span><br></span>
<span *ngIf="eventElement.event.message.code"><span class="small">{{'quality-assurance.event.table.code' | translate}}</span>&nbsp;<span class="badge badge-info">{{eventElement.event.message.code}}</span><br></span>
<span *ngIf="eventElement.event.message.funder"><span class="small">{{'quality-assurance.event.table.funder' | translate}}</span>&nbsp;<span class="badge badge-info">{{eventElement.event.message.funder}}</span><br></span>
<span *ngIf="eventElement.event.message.fundingProgram"><span class="small">{{'quality-assurance.event.table.fundingProgram' | translate}}</span>&nbsp;<span class="badge badge-info">{{eventElement.event.message.fundingProgram}}</span><br></span>
<span *ngIf="eventElement.event.message.jurisdiction"><span class="small">{{'quality-assurance.event.table.jurisdiction' | translate}}</span>&nbsp;<span class="badge badge-info">{{eventElement.event.message.jurisdiction}}</span></span>
</p>
<hr>
<div>
{{(eventElement.hasProject ? 'quality-assurance.event.project.found' : 'quality-assurance.event.project.notFound') | translate}}
<a target="_blank" *ngIf="eventElement.hasProject" title="{{eventElement.projectTitle}}" [routerLink]="['/items', eventElement.projectId]">{{eventElement.handle}} </a>
<div class="btn-group">
<button class="btn btn-outline-primary btn-sm"
<button *ngIf="!eventElement.hasProject"
class="btn btn-outline-primary btn-sm"
[disabled]="eventElement.isRunning"
(click)="openModalLookup(eventElement); $event.stopPropagation();">
<i class="fas fa-search"></i>
@@ -149,7 +155,7 @@
</ds-pagination>
</div>
</div>
<div class="row">
<div class="row text-right">
<div class="col-md-12">
<a class="btn btn-outline-secondary" [routerLink]="['/admin/notifications/quality-assurance']">
<i class="fas fa-angle-double-left"></i>

View File

@@ -1,5 +1,12 @@
.button-rows {
min-width: 200px;
.button-col, .trust-col {
width: 15%;
}
.title-col {
width: 30%;
}
.content-col {
width: 40%;
}
.button-width {

View File

@@ -5,16 +5,18 @@ import { ComponentFixture, inject, TestBed, waitForAsync } from '@angular/core/t
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { of as observableOf } from 'rxjs';
import { QualityAssuranceEventRestService } from '../../../core/suggestion-notifications/qa/events/quality-assurance-event-rest.service';
import {
QualityAssuranceEventRestService
} from '../../../core/suggestion-notifications/qa/events/quality-assurance-event-rest.service';
import { QualityAssuranceEventsComponent } from './quality-assurance-events.component';
import {
getMockQualityAssuranceEventRestService,
ItemMockPid10,
ItemMockPid8,
ItemMockPid9,
NotificationsMockDspaceObject,
qualityAssuranceEventObjectMissingProjectFound,
qualityAssuranceEventObjectMissingProjectNotFound,
NotificationsMockDspaceObject
qualityAssuranceEventObjectMissingProjectNotFound
} from '../../../shared/mocks/notifications.mock';
import { NotificationsServiceStub } from '../../../shared/testing/notifications-service.stub';
import { NotificationsService } from '../../../shared/notifications/notifications.service';
@@ -22,10 +24,12 @@ import { getMockTranslateService } from '../../../shared/mocks/translate.service
import { createTestComponent } from '../../../shared/testing/utils.test';
import { ActivatedRouteStub } from '../../../shared/testing/active-router.stub';
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
import { QualityAssuranceEventObject } from '../../../core/suggestion-notifications/qa/models/quality-assurance-event.model';
import {
QualityAssuranceEventObject
} from '../../../core/suggestion-notifications/qa/models/quality-assurance-event.model';
import { QualityAssuranceEventData } from '../project-entry-import-modal/project-entry-import-modal.component';
import { TestScheduler } from 'rxjs/testing';
import { getTestScheduler } from 'jasmine-marbles';
import { cold, getTestScheduler } from 'jasmine-marbles';
import { followLink } from '../../../shared/utils/follow-link-config.model';
import { PageInfo } from '../../../core/shared/page-info.model';
import { buildPaginatedList } from '../../../core/data/paginated-list.model';
@@ -37,7 +41,7 @@ import {
import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model';
import { PaginationService } from '../../../core/pagination/pagination.service';
import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub';
import {FindListOptions} from '../../../core/data/find-list-options.model';
import { FindListOptions } from '../../../core/data/find-list-options.model';
describe('QualityAssuranceEventsComponent test suite', () => {
let fixture: ComponentFixture<QualityAssuranceEventsComponent>;
@@ -156,18 +160,16 @@ describe('QualityAssuranceEventsComponent test suite', () => {
compAsAny = null;
});
describe('setEventUpdated', () => {
it('should update events', () => {
const expected = [
getQualityAssuranceEventData1(),
getQualityAssuranceEventData2()
];
scheduler.schedule(() => {
compAsAny.setEventUpdated(events);
describe('fetchEvents', () => {
it('should fetch events', () => {
const result = compAsAny.fetchEvents(events);
const expected = cold('(a|)', {
a: [
getQualityAssuranceEventData1(),
getQualityAssuranceEventData2()
]
});
scheduler.flush();
expect(comp.eventsUpdated$.value).toEqual(expected);
expect(result).toBeObservable(expected);
});
});
@@ -229,7 +231,10 @@ describe('QualityAssuranceEventsComponent test suite', () => {
describe('executeAction', () => {
it('should call getQualityAssuranceEvents on 200 response from REST', () => {
const action = 'ACCEPTED';
spyOn(compAsAny, 'getQualityAssuranceEvents');
spyOn(compAsAny, 'getQualityAssuranceEvents').and.returnValue(observableOf([
getQualityAssuranceEventData1(),
getQualityAssuranceEventData2()
]));
qualityAssuranceEventRestServiceStub.patchEvent.and.returnValue(createSuccessfulRemoteDataObject$({}));
scheduler.schedule(() => {
@@ -279,7 +284,7 @@ describe('QualityAssuranceEventsComponent test suite', () => {
});
describe('getQualityAssuranceEvents', () => {
it('should call the "qualityAssuranceEventRestService.getEventsByTopic" to take data and "setEventUpdated" to populate eventData', () => {
it('should call the "qualityAssuranceEventRestService.getEventsByTopic" to take data and "fetchEvents" to populate eventData', () => {
comp.paginationConfig = new PaginationComponentOptions();
comp.paginationConfig.currentPage = 1;
comp.paginationConfig.pageSize = 20;
@@ -292,7 +297,7 @@ describe('QualityAssuranceEventsComponent test suite', () => {
const pageInfo = new PageInfo({
elementsPerPage: comp.paginationConfig.pageSize,
totalElements: 0,
totalElements: 2,
totalPages: 1,
currentPage: comp.paginationConfig.currentPage
});
@@ -303,10 +308,13 @@ describe('QualityAssuranceEventsComponent test suite', () => {
const paginatedList = buildPaginatedList(pageInfo, array);
const paginatedListRD = createSuccessfulRemoteDataObject(paginatedList);
qualityAssuranceEventRestServiceStub.getEventsByTopic.and.returnValue(observableOf(paginatedListRD));
spyOn(compAsAny, 'setEventUpdated');
spyOn(compAsAny, 'fetchEvents').and.returnValue(observableOf([
getQualityAssuranceEventData1(),
getQualityAssuranceEventData2()
]));
scheduler.schedule(() => {
compAsAny.getQualityAssuranceEvents();
compAsAny.getQualityAssuranceEvents().subscribe();
});
scheduler.flush();
@@ -315,7 +323,7 @@ describe('QualityAssuranceEventsComponent test suite', () => {
options,
followLink('target'),followLink('related')
);
expect(compAsAny.setEventUpdated).toHaveBeenCalled();
expect(compAsAny.fetchEvents).toHaveBeenCalled();
});
});

View File

@@ -3,8 +3,8 @@ import { ActivatedRoute } from '@angular/router';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { TranslateService } from '@ngx-translate/core';
import { BehaviorSubject, from, Observable, of as observableOf, Subscription } from 'rxjs';
import { distinctUntilChanged, map, mergeMap, scan, switchMap, take } from 'rxjs/operators';
import { BehaviorSubject, combineLatest, from, Observable, of, Subscription } from 'rxjs';
import { distinctUntilChanged, last, map, mergeMap, scan, switchMap, take, tap } from 'rxjs/operators';
import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model';
import { PaginatedList } from '../../../core/data/paginated-list.model';
@@ -19,7 +19,7 @@ import {
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
import { Metadata } from '../../../core/shared/metadata.utils';
import { followLink } from '../../../shared/utils/follow-link-config.model';
import { hasValue, isEmpty } from '../../../shared/empty.util';
import { hasValue } from '../../../shared/empty.util';
import { ItemSearchResult } from '../../../shared/object-collection/shared/item-search-result.model';
import { NotificationsService } from '../../../shared/notifications/notifications.service';
import {
@@ -28,7 +28,6 @@ import {
} from '../project-entry-import-modal/project-entry-import-modal.component';
import { getFirstCompletedRemoteData } from '../../../core/shared/operators';
import { PaginationService } from '../../../core/pagination/pagination.service';
import { combineLatest } from 'rxjs/internal/observable/combineLatest';
import { Item } from '../../../core/shared/item.model';
import { FindListOptions } from '../../../core/data/find-list-options.model';
@@ -65,7 +64,7 @@ export class QualityAssuranceEventsComponent implements OnInit, OnDestroy {
* The total number of Quality Assurance events.
* @type {Observable<number>}
*/
public totalElements$: Observable<number>;
public totalElements$: BehaviorSubject<number> = new BehaviorSubject<number>(null);
/**
* The topic of the Quality Assurance events; suitable for displaying.
* @type {string}
@@ -86,11 +85,6 @@ export class QualityAssuranceEventsComponent implements OnInit, OnDestroy {
* @type {Observable<boolean>}
*/
public isEventPageLoading: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
/**
* Contains the information about the loading status of the events inside the pagination component.
* @type {Observable<boolean>}
*/
public isEventLoading: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
/**
* The modal reference.
* @type {any}
@@ -104,7 +98,7 @@ export class QualityAssuranceEventsComponent implements OnInit, OnDestroy {
/**
* The FindListOptions object
*/
protected defaultConfig: FindListOptions = Object.assign(new FindListOptions(), {sort: this.paginationSortConfig});
protected defaultConfig: FindListOptions = Object.assign(new FindListOptions(), { sort: this.paginationSortConfig });
/**
* Array to track all the component subscriptions. Useful to unsubscribe them with 'onDestroy'.
* @type {Array}
@@ -138,13 +132,17 @@ export class QualityAssuranceEventsComponent implements OnInit, OnDestroy {
this.activatedRoute.paramMap.pipe(
map((params) => params.get('topicId')),
take(1)
).subscribe((id: string) => {
const regEx = /!/g;
this.showTopic = id.replace(regEx, '/');
this.topic = id;
take(1),
switchMap((id: string) => {
const regEx = /!/g;
this.showTopic = id.replace(regEx, '/');
this.topic = id;
return this.getQualityAssuranceEvents();
})
).subscribe((events: QualityAssuranceEventData[]) => {
console.log(events);
this.eventsUpdated$.next(events);
this.isEventPageLoading.next(false);
this.getQualityAssuranceEvents();
});
}
@@ -240,20 +238,25 @@ export class QualityAssuranceEventsComponent implements OnInit, OnDestroy {
public executeAction(action: string, eventData: QualityAssuranceEventData): void {
eventData.isRunning = true;
this.subs.push(
this.qualityAssuranceEventRestService.patchEvent(action, eventData.event, eventData.reason).pipe(getFirstCompletedRemoteData())
.subscribe((rd: RemoteData<QualityAssuranceEventObject>) => {
if (rd.isSuccess && rd.statusCode === 200) {
this.qualityAssuranceEventRestService.patchEvent(action, eventData.event, eventData.reason).pipe(
getFirstCompletedRemoteData(),
switchMap((rd: RemoteData<QualityAssuranceEventObject>) => {
if (rd.hasSucceeded) {
this.notificationsService.success(
this.translateService.instant('quality-assurance.event.action.saved')
);
this.getQualityAssuranceEvents();
return this.getQualityAssuranceEvents();
} else {
this.notificationsService.error(
this.translateService.instant('quality-assurance.event.action.error')
);
return of(this.eventsUpdated$.value);
}
eventData.isRunning = false;
})
).subscribe((events: QualityAssuranceEventData[]) => {
this.eventsUpdated$.next(events);
eventData.isRunning = false;
})
);
}
@@ -274,7 +277,7 @@ export class QualityAssuranceEventsComponent implements OnInit, OnDestroy {
this.subs.push(
this.qualityAssuranceEventRestService.boundProject(eventData.id, projectId).pipe(getFirstCompletedRemoteData())
.subscribe((rd: RemoteData<QualityAssuranceEventObject>) => {
if (rd.isSuccess) {
if (rd.hasSucceeded) {
this.notificationsService.success(
this.translateService.instant('quality-assurance.event.project.bounded')
);
@@ -303,7 +306,7 @@ export class QualityAssuranceEventsComponent implements OnInit, OnDestroy {
this.subs.push(
this.qualityAssuranceEventRestService.removeProject(eventData.id).pipe(getFirstCompletedRemoteData())
.subscribe((rd: RemoteData<QualityAssuranceEventObject>) => {
if (rd.isSuccess) {
if (rd.hasSucceeded) {
this.notificationsService.success(
this.translateService.instant('quality-assurance.event.project.removed')
);
@@ -337,12 +340,11 @@ export class QualityAssuranceEventsComponent implements OnInit, OnDestroy {
return event.pidHref;
}
/**
* Dispatch the Quality Assurance events retrival.
*/
public getQualityAssuranceEvents(): void {
this.paginationService.getFindListOptions(this.paginationConfig.id, this.defaultConfig).pipe(
public getQualityAssuranceEvents(): Observable<QualityAssuranceEventData[]> {
return this.paginationService.getFindListOptions(this.paginationConfig.id, this.defaultConfig).pipe(
distinctUntilChanged(),
switchMap((options: FindListOptions) => this.qualityAssuranceEventRestService.getEventsByTopic(
this.topic,
@@ -350,16 +352,24 @@ export class QualityAssuranceEventsComponent implements OnInit, OnDestroy {
followLink('target'), followLink('related')
)),
getFirstCompletedRemoteData(),
).subscribe((rd: RemoteData<PaginatedList<QualityAssuranceEventObject>>) => {
if (rd.hasSucceeded) {
this.isEventLoading.next(false);
this.totalElements$ = observableOf(rd.payload.totalElements);
this.setEventUpdated(rd.payload.page);
} else {
throw new Error('Can\'t retrieve Quality Assurance events from the Broker events REST service');
}
this.qualityAssuranceEventRestService.clearFindByTopicRequests();
});
switchMap((rd: RemoteData<PaginatedList<QualityAssuranceEventObject>>) => {
if (rd.hasSucceeded) {
this.totalElements$.next(rd.payload.totalElements);
if (rd.payload.totalElements > 0) {
console.log(rd.payload.page);
return this.fetchEvents(rd.payload.page);
} else {
return of([]);
}
} else {
throw new Error('Can\'t retrieve Quality Assurance events from the Broker events REST service');
}
}),
take(1),
tap(() => {
this.qualityAssuranceEventRestService.clearFindByTopicRequests();
})
);
}
/**
@@ -372,55 +382,47 @@ export class QualityAssuranceEventsComponent implements OnInit, OnDestroy {
}
/**
* Set the project status for the Quality Assurance events.
* Fetch Quality Assurance events in order to build proper QualityAssuranceEventData object.
*
* @param {QualityAssuranceEventObject[]} events
* the Quality Assurance event item
* @return array of QualityAssuranceEventData
*/
protected setEventUpdated(events: QualityAssuranceEventObject[]): void {
if (isEmpty(events)) {
this.eventsUpdated$.next([]);
} else {
this.subs.push(
from(events).pipe(
mergeMap((event: QualityAssuranceEventObject) => {
const related$ = event.related.pipe(
getFirstCompletedRemoteData(),
);
const target$ = event.target.pipe(
getFirstCompletedRemoteData()
);
return combineLatest([related$, target$]).pipe(
map(([relatedItemRD, targetItemRD]: [RemoteData<Item>, RemoteData<Item>]) => {
const data: QualityAssuranceEventData = {
event: event,
id: event.id,
title: event.title,
hasProject: false,
projectTitle: null,
projectId: null,
handle: null,
reason: null,
isRunning: false,
target: (targetItemRD?.hasSucceeded) ? targetItemRD.payload : null,
};
if (relatedItemRD?.hasSucceeded && relatedItemRD?.payload?.id) {
data.hasProject = true;
data.projectTitle = event.message.title;
data.projectId = relatedItemRD?.payload?.id;
data.handle = relatedItemRD?.payload?.handle;
}
return data;
})
);
}),
scan((acc: any, value: any) => [...acc, value], []),
take(events.length)
).subscribe((eventsReduced) => {
this.eventsUpdated$.next(eventsReduced);
}
)
);
}
protected fetchEvents(events: QualityAssuranceEventObject[]): Observable<QualityAssuranceEventData[]> {
return from(events).pipe(
mergeMap((event: QualityAssuranceEventObject) => {
const related$ = event.related.pipe(
getFirstCompletedRemoteData(),
);
const target$ = event.target.pipe(
getFirstCompletedRemoteData()
);
return combineLatest([related$, target$]).pipe(
map(([relatedItemRD, targetItemRD]: [RemoteData<Item>, RemoteData<Item>]) => {
const data: QualityAssuranceEventData = {
event: event,
id: event.id,
title: event.title,
hasProject: false,
projectTitle: null,
projectId: null,
handle: null,
reason: null,
isRunning: false,
target: (targetItemRD?.hasSucceeded) ? targetItemRD.payload : null,
};
if (relatedItemRD?.hasSucceeded && relatedItemRD?.payload?.id) {
data.hasProject = true;
data.projectTitle = event.message.title;
data.projectId = relatedItemRD?.payload?.id;
data.handle = relatedItemRD?.payload?.handle;
}
return data;
})
);
}),
scan((acc: any, value: any) => [...acc, value], []),
last()
);
}
}

View File

@@ -2,12 +2,12 @@
<div class="row">
<div class="col-12">
<h2 class="border-bottom pb-2">{{'quality-assurance.title'| translate}}</h2>
<p>{{'quality-assurance.source.description'| translate}}</p>
<ds-alert [type]="'alert-info'" [content]="'quality-assurance.source.description'"></ds-alert>
</div>
</div>
<div class="row">
<div class="col-12">
<h3 class="border-bottom pb-2">{{'quality-assurance.source'| translate}}</h3>
<h4 class="border-bottom pb-2">{{'quality-assurance.source'| translate}}</h4>
<ds-loading class="container" *ngIf="(isSourceLoading() | async)" message="{{'quality-assurance.loading' | translate}}"></ds-loading>
<ds-pagination *ngIf="!(isSourceLoading() | async)"

View File

@@ -1,12 +1,19 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { find, map } from 'rxjs/operators';
import { QualityAssuranceSourceRestService } from '../../../core/suggestion-notifications/qa/source/quality-assurance-source-rest.service';
import { map } from 'rxjs/operators';
import {
QualityAssuranceSourceRestService
} from '../../../core/suggestion-notifications/qa/source/quality-assurance-source-rest.service';
import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model';
import { RemoteData } from '../../../core/data/remote-data';
import { PaginatedList } from '../../../core/data/paginated-list.model';
import { QualityAssuranceSourceObject } from '../../../core/suggestion-notifications/qa/models/quality-assurance-source.model';
import {FindListOptions} from '../../../core/data/find-list-options.model';
import {
QualityAssuranceSourceObject
} from '../../../core/suggestion-notifications/qa/models/quality-assurance-source.model';
import { FindListOptions } from '../../../core/data/find-list-options.model';
import { getFirstCompletedRemoteData } from '../../../core/shared/operators';
/**
* The service handling all Quality Assurance source requests to the REST service.
@@ -20,7 +27,8 @@ export class QualityAssuranceSourceService {
*/
constructor(
private qualityAssuranceSourceRestService: QualityAssuranceSourceRestService
) { }
) {
}
/**
* Return the list of Quality Assurance source managing pagination and errors.
@@ -42,7 +50,7 @@ export class QualityAssuranceSourceService {
};
return this.qualityAssuranceSourceRestService.getSources(findListOptions).pipe(
find((rd: RemoteData<PaginatedList<QualityAssuranceSourceObject>>) => !rd.isResponsePending),
getFirstCompletedRemoteData(),
map((rd: RemoteData<PaginatedList<QualityAssuranceSourceObject>>) => {
if (rd.hasSucceeded) {
return rd.payload;

View File

@@ -2,12 +2,12 @@
<div class="row">
<div class="col-12">
<h2 class="border-bottom pb-2">{{'quality-assurance.title'| translate}}</h2>
<p>{{'quality-assurance.topics.description'| translate:{source: sourceId} }}</p>
<ds-alert [type]="'alert-info'">{{'quality-assurance.topics.description'| translate:{source: sourceId} }}</ds-alert>
</div>
</div>
<div class="row">
<div class="col-12">
<h3 class="border-bottom pb-2">{{'quality-assurance.topics'| translate}}</h3>
<h4 class="border-bottom pb-2">{{'quality-assurance.topics'| translate}}</h4>
<ds-loading class="container" *ngIf="(isTopicsLoading() | async)" message="{{'quality-assurance.loading' | translate}}"></ds-loading>
<ds-pagination *ngIf="!(isTopicsLoading() | async)"

View File

@@ -1,14 +1,18 @@
import { Component, OnInit } from '@angular/core';
import { Observable, Subscription } from 'rxjs';
import { distinctUntilChanged, map, take } from 'rxjs/operators';
import { distinctUntilChanged, take } from 'rxjs/operators';
import { SortOptions } from '../../../core/cache/models/sort-options.model';
import { QualityAssuranceTopicObject } from '../../../core/suggestion-notifications/qa/models/quality-assurance-topic.model';
import {
QualityAssuranceTopicObject
} from '../../../core/suggestion-notifications/qa/models/quality-assurance-topic.model';
import { hasValue } from '../../../shared/empty.util';
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
import { SuggestionNotificationsStateService } from '../../suggestion-notifications-state.service';
import { AdminQualityAssuranceTopicsPageParams } from '../../../admin/admin-notifications/admin-quality-assurance-topics-page/admin-quality-assurance-topics-page-resolver.service';
import {
AdminQualityAssuranceTopicsPageParams
} from '../../../admin/admin-notifications/admin-quality-assurance-topics-page/admin-quality-assurance-topics-page-resolver.service';
import { PaginationService } from '../../../core/pagination/pagination.service';
import { ActivatedRoute } from '@angular/router';
import { QualityAssuranceTopicsService } from './quality-assurance-topics.service';
@@ -59,7 +63,9 @@ export class QualityAssuranceTopicsComponent implements OnInit {
/**
* Initialize the component variables.
* @param {PaginationService} paginationService
* @param {ActivatedRoute} activatedRoute
* @param {SuggestionNotificationsStateService} notificationsStateService
* @param {QualityAssuranceTopicsService} qualityAssuranceTopicsService
*/
constructor(
private paginationService: PaginationService,

View File

@@ -1,13 +1,18 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { find, map } from 'rxjs/operators';
import { QualityAssuranceTopicRestService } from '../../../core/suggestion-notifications/qa/topics/quality-assurance-topic-rest.service';
import { map } from 'rxjs/operators';
import {
QualityAssuranceTopicRestService
} from '../../../core/suggestion-notifications/qa/topics/quality-assurance-topic-rest.service';
import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model';
import { RemoteData } from '../../../core/data/remote-data';
import { PaginatedList } from '../../../core/data/paginated-list.model';
import { QualityAssuranceTopicObject } from '../../../core/suggestion-notifications/qa/models/quality-assurance-topic.model';
import {
QualityAssuranceTopicObject
} from '../../../core/suggestion-notifications/qa/models/quality-assurance-topic.model';
import { RequestParam } from '../../../core/cache/models/request-param.model';
import {FindListOptions} from '../../../core/data/find-list-options.model';
import { FindListOptions } from '../../../core/data/find-list-options.model';
import { getFirstCompletedRemoteData } from '../../../core/shared/operators';
/**
* The service handling all Quality Assurance topic requests to the REST service.
@@ -49,7 +54,7 @@ export class QualityAssuranceTopicsService {
};
return this.qualityAssuranceTopicRestService.getTopics(findListOptions).pipe(
find((rd: RemoteData<PaginatedList<QualityAssuranceTopicObject>>) => !rd.isResponsePending),
getFirstCompletedRemoteData(),
map((rd: RemoteData<PaginatedList<QualityAssuranceTopicObject>>) => {
if (rd.hasSucceeded) {
return rd.payload;

View File

@@ -30,6 +30,7 @@ import {
const MODULES = [
CommonModule,
SharedModule,
SearchModule,
CoreModule.forRoot(),
StoreModule.forFeature('suggestionNotifications', suggestionNotificationsReducers, storeModuleConfig as StoreConfig<SuggestionNotificationsState, Action>),
EffectsModule.forFeature(suggestionNotificationsEffects),
@@ -59,8 +60,7 @@ const PROVIDERS = [
@NgModule({
imports: [
...MODULES,
SearchModule
...MODULES
],
declarations: [
...COMPONENTS,