refactor, decouple pagination and configuration

This commit is contained in:
FrancescoMolinaro
2024-01-03 17:27:38 +01:00
parent c4b8eaba72
commit 457c3b351f
26 changed files with 256 additions and 146 deletions

View File

@@ -2,14 +2,16 @@ import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver';
import { I18nBreadcrumbsService } from '../../core/breadcrumbs/i18n-breadcrumbs.service';
import { AuthenticatedGuard } from '../../core/auth/authenticated.guard';
import { AdminNotifyDashboardComponent } from './admin-notify-dashboard.component';
import {
SiteAdministratorGuard
} from '../../core/data/feature-authorization/feature-authorization-guard/site-administrator.guard';
@NgModule({
imports: [
RouterModule.forChild([
{
canActivate: [ AuthenticatedGuard ],
canActivate: [SiteAdministratorGuard],
path: '',
component: AdminNotifyDashboardComponent,
pathMatch: 'full',

View File

@@ -16,7 +16,8 @@
<a ngbNavLink>{{'admin-notify-dashboard.logs' | translate}}</a>
<ng-template ngbNavContent>
<div id="logs">
<ds-admin-notify-logs></ds-admin-notify-logs>
<ds-admin-notify-incoming></ds-admin-notify-incoming>
<ds-admin-notify-outgoing></ds-admin-notify-outgoing>
</div>
</ng-template>
</li>

View File

@@ -3,12 +3,6 @@ import { SearchService } from '../../core/shared/search/search.service';
import { environment } from '../../../environments/environment';
import { PaginatedSearchOptions } from '../../shared/search/models/paginated-search-options.model';
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
import {
listableObjectComponent
} from '../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { ViewMode } from '../../core/shared/view-mode.model';
import { Context } from '../../core/shared/context.model';
import { AdminNotifySearchResult } from './models/admin-notify-message-search-result.model';
import { forkJoin, Observable } from 'rxjs';
import { getFirstCompletedRemoteData } from '../../core/shared/operators';
import { map } from 'rxjs/operators';
@@ -16,8 +10,6 @@ import { SearchObjects } from '../../shared/search/models/search-objects.model';
import { AdminNotifyMetricsBox, AdminNotifyMetricsRow } from './admin-notify-metrics/admin-notify-metrics.model';
import { DSpaceObject } from '../../core/shared/dspace-object.model';
@listableObjectComponent(AdminNotifySearchResult, ViewMode.GridElement, Context.AdminSearch)
@Component({
selector: 'ds-admin-notify-dashboard',
templateUrl: './admin-notify-dashboard.component.html',

View File

@@ -4,10 +4,14 @@ import { RouterModule } from '@angular/router';
import { AdminNotifyDashboardComponent } from './admin-notify-dashboard.component';
import { AdminNotifyDashboardRoutingModule } from './admin-notify-dashboard-routing.module';
import { AdminNotifyMetricsComponent } from './admin-notify-metrics/admin-notify-metrics.component';
import { AdminNotifyLogsComponent } from './admin-notify-logs/admin-notify-logs.component';
import { AdminNotifyIncomingComponent } from './admin-notify-logs/admin-notify-incoming/admin-notify-incoming.component';
import { SharedModule } from '../../shared/shared.module';
import { SearchModule } from "../../shared/search/search.module";
import { SearchPageModule } from "../../search-page/search-page.module";
import { SearchModule } from '../../shared/search/search.module';
import { SearchPageModule } from '../../search-page/search-page.module';
import { AdminNotifySearchResultComponent } from './admin-notify-search-result/admin-notify-search-result.component';
import {
AdminNotifyOutgoingComponent
} from './admin-notify-logs/admin-notify-outgoing/admin-notify-outgoing.component';
@NgModule({
@@ -22,7 +26,9 @@ import { SearchPageModule } from "../../search-page/search-page.module";
declarations: [
AdminNotifyDashboardComponent,
AdminNotifyMetricsComponent,
AdminNotifyLogsComponent
AdminNotifyIncomingComponent,
AdminNotifyOutgoingComponent,
AdminNotifySearchResultComponent
]
})
export class AdminNotifyDashboardModule {

View File

@@ -0,0 +1,9 @@
<ds-themed-search
[configuration]="'NOTIFY.incoming'"
[showViewModes]="false"
[searchEnabled]="false"
[context]="context"
[useUniquePageId]="true"
[routeConfigParam]="'configuration-incoming'"
></ds-themed-search>

View File

@@ -1,20 +1,20 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AdminNotifyLogsComponent } from './admin-notify-logs.component';
import { AdminNotifyIncomingComponent } from './admin-notify-incoming.component';
import { TranslateModule } from '@ngx-translate/core';
describe('AdminNotifyLogsComponent', () => {
let component: AdminNotifyLogsComponent;
let fixture: ComponentFixture<AdminNotifyLogsComponent>;
let component: AdminNotifyIncomingComponent;
let fixture: ComponentFixture<AdminNotifyIncomingComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [TranslateModule.forRoot()],
declarations: [ AdminNotifyLogsComponent ]
declarations: [ AdminNotifyIncomingComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(AdminNotifyLogsComponent);
fixture = TestBed.createComponent(AdminNotifyIncomingComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

View File

@@ -0,0 +1,20 @@
import { Component } from '@angular/core';
import { SEARCH_CONFIG_SERVICE } from '../../../../my-dspace-page/my-dspace-page.component';
import { SearchConfigurationService } from '../../../../core/shared/search/search-configuration.service';
import { Context } from '../../../../core/shared/context.model';
@Component({
selector: 'ds-admin-notify-incoming',
templateUrl: './admin-notify-incoming.component.html',
styleUrls: ['./admin-notify-incoming.component.scss'],
providers: [
{
provide: SEARCH_CONFIG_SERVICE,
useClass: SearchConfigurationService
}
]
})
export class AdminNotifyIncomingComponent {
protected readonly context = Context.CoarNotify;
}

View File

@@ -1,93 +0,0 @@
import { SearchConfigurationService } from "../../../core/shared/search/search-configuration.service";
import { PaginationComponentOptions } from "../../../shared/pagination/pagination-component-options.model";
import { SortDirection, SortOptions } from "../../../core/cache/models/sort-options.model";
import { RouteService } from "../../../core/services/route.service";
import { ActivatedRoute } from "@angular/router";
import { LinkService } from "../../../core/cache/builders/link.service";
import { HALEndpointService } from "../../../core/shared/hal-endpoint.service";
import { RequestService } from "../../../core/data/request.service";
import { RemoteDataBuildService } from "../../../core/cache/builders/remote-data-build.service";
import { PaginationService } from '../../../core/pagination/pagination.service';
import { Injectable } from "@angular/core";
import { PaginatedSearchOptions } from "../../../shared/search/models/paginated-search-options.model";
import { Observable } from "rxjs";
import { map } from "rxjs/operators";
import { hasValue } from "../../../shared/empty.util";
/**
* Service that performs all actions that have to do with the current notify configuration
*/
@Injectable()
export class AdminNotifyLogsConfigurationService extends SearchConfigurationService {
/**
* Endpoint link path for retrieving general search results
*/
private searchLinkPath = 'discover/search/objects';
/**
* Default pagination settings
*/
protected defaultPagination = Object.assign(new PaginationComponentOptions(), {
id: 'notify-logs-page',
pageSize: 10,
currentPage: 1
});
/**
* Default sort settings
*/
protected defaultSort = new SortOptions('dc.date.issued', SortDirection.DESC);
/**
* Default scope setting
*/
protected defaultScope = '';
/**
* Default query setting
*/
protected defaultQuery = '';
/**
* Initialize class
*
* @param {roleService} roleService
* @param {RouteService} routeService
* @param {PaginationService} paginationService
* @param {ActivatedRoute} route
* @param linkService
* @param halService
* @param requestService
* @param rdb
*/
constructor(
protected routeService: RouteService,
protected paginationService: PaginationService,
protected route: ActivatedRoute,
protected linkService: LinkService,
protected halService: HALEndpointService,
protected requestService: RequestService,
protected rdb: RemoteDataBuildService) {
super(routeService, paginationService, route, linkService, halService, requestService, rdb);
// override parent class initialization
this._defaults = null;
this.initDefaults();
}
getEndpoint(searchOptions?: PaginatedSearchOptions): Observable<string> {
return this.halService.getEndpoint(this.searchLinkPath).pipe(
map((url: string) => {
if (hasValue(searchOptions)) {
return (searchOptions as PaginatedSearchOptions).toRestUrl(url);
} else {
return url;
}
})
);
}
}

View File

@@ -1,3 +0,0 @@
<ds-themed-search configuration="NOTIFY.incoming'"></ds-themed-search>
<ds-themed-search configuration="NOTIFY.outgoing'"></ds-themed-search>

View File

@@ -1,23 +0,0 @@
import { Component, Inject } from '@angular/core';
import { SEARCH_CONFIG_SERVICE } from "../../../my-dspace-page/my-dspace-page.component";
import { AdminNotifyLogsConfigurationService } from "./admin-notify-logs-configuration.service";
import { SearchConfigurationService } from "../../../core/shared/search/search-configuration.service";
import { Context } from "../../../core/shared/context.model";
@Component({
selector: 'ds-admin-notify-logs',
templateUrl: './admin-notify-logs.component.html',
styleUrls: ['./admin-notify-logs.component.scss'],
providers: [
{
provide: SEARCH_CONFIG_SERVICE,
useClass: AdminNotifyLogsConfigurationService
}
]
})
export class AdminNotifyLogsComponent {
constructor(@Inject(SEARCH_CONFIG_SERVICE) private searchConfigService: SearchConfigurationService) {
}
protected readonly context = Context.Search;
}

View File

@@ -0,0 +1,9 @@
<ds-themed-search
[configuration]="'NOTIFY.outgoing'"
[showViewModes]="false"
[searchEnabled]="false"
[context]="context"
[routeConfigParam]="'configuration-outgoing'"
[useUniquePageId]="true"
></ds-themed-search>

View File

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AdminNotifyOutgoingComponent } from './admin-notify-outgoing.component';
import { TranslateModule } from '@ngx-translate/core';
describe('AdminNotifyLogsComponent', () => {
let component: AdminNotifyOutgoingComponent;
let fixture: ComponentFixture<AdminNotifyOutgoingComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [TranslateModule.forRoot()],
declarations: [ AdminNotifyOutgoingComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(AdminNotifyOutgoingComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,20 @@
import { Component } from '@angular/core';
import { SEARCH_CONFIG_SERVICE } from '../../../../my-dspace-page/my-dspace-page.component';
import { SearchConfigurationService } from '../../../../core/shared/search/search-configuration.service';
import { Context } from '../../../../core/shared/context.model';
@Component({
selector: 'ds-admin-notify-outgoing',
templateUrl: './admin-notify-outgoing.component.html',
styleUrls: ['./admin-notify-outgoing.component.scss'],
providers: [
{
provide: SEARCH_CONFIG_SERVICE,
useClass: SearchConfigurationService
}
]
})
export class AdminNotifyOutgoingComponent {
protected readonly context = Context.CoarNotify;
}

View File

@@ -0,0 +1,7 @@
<div class="d-flex bg-light w-100 align-items-center">
<div class="p-2">{{indexableObject.queueTimeout}}</div>
<div class="p-2">{{indexableObject.source}}</div>
<div class="p-2">{{indexableObject.target}}</div>
<div class="p-2">{{indexableObject.coarNotifyType}}</div>
<div class="p-2">{{indexableObject.queueStatusLabel}}</div>
</div>

View File

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

View File

@@ -0,0 +1,24 @@
import { Component, OnInit } from '@angular/core';
import {
listableObjectComponent
} from '../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { AdminNotifySearchResult } from '../models/admin-notify-message-search-result.model';
import { ViewMode } from '../../../core/shared/view-mode.model';
import { Context } from '../../../core/shared/context.model';
import {
SearchResultListElementComponent
} from '../../../shared/object-list/search-result-list-element/search-result-list-element.component';
import { AdminNotifyMessage } from '../models/admin-notify-message.model';
@listableObjectComponent(AdminNotifySearchResult, ViewMode.ListElement, Context.CoarNotify)
@Component({
selector: 'ds-admin-notify-search-result',
templateUrl: './admin-notify-search-result.component.html',
styleUrls: ['./admin-notify-search-result.component.scss']
})
export class AdminNotifySearchResultComponent extends SearchResultListElementComponent<AdminNotifySearchResult, AdminNotifyMessage> implements OnInit{
indexableObject: AdminNotifyMessage;
ngOnInit() {
this.indexableObject = this.object.indexableObject;
}
}

View File

@@ -1,8 +1,10 @@
import { deserialize, inheritSerialization } from 'cerialize';
import { autoserialize, deserialize, inheritSerialization } from 'cerialize';
import { typedObject } from '../../../core/cache/builders/build-decorators';
import { ADMIN_NOTIFY_MESSAGE } from './admin-notify-message.resource-type';
import { excludeFromEquals } from '../../../core/utilities/equals.decorators';
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
import { GenericConstructor } from '../../../core/shared/generic-constructor';
import { ListableObject } from '../../../shared/object-collection/shared/listable-object.model';
/**
* A message that includes admin notify info
@@ -13,11 +15,47 @@ export class AdminNotifyMessage extends DSpaceObject {
static type = ADMIN_NOTIFY_MESSAGE;
/**
* The type of the message
* The type of the resource
*/
@excludeFromEquals
type = ADMIN_NOTIFY_MESSAGE;
/**
* The type of the notification
*/
@autoserialize
coarNotifyType: string;
/**
* The type of the activity stream
*/
@autoserialize
source: number;
/**
* The type of the activity stream
*/
@autoserialize
target: number;
/**
* The label for the status of the queue
*/
@autoserialize
queueStatusLabel: string;
/**
* The timeout of the queue
*/
@autoserialize
queueTimeout: string;
/**
* The status of the queue
*/
@autoserialize
queueStatus: number;
@deserialize
_links: {
self: {
@@ -28,4 +66,8 @@ export class AdminNotifyMessage extends DSpaceObject {
get self(): string {
return this._links.self.href;
}
getRenderTypes(): (string | GenericConstructor<ListableObject>)[] {
return [this.constructor as GenericConstructor<ListableObject>];
}
}

View File

@@ -39,4 +39,6 @@ export enum Context {
MyDSpaceValidation = 'mydspaceValidation',
Bitstream = 'bitstream',
CoarNotify = 'coarNotify'
}

View File

@@ -28,6 +28,7 @@ import { FacetConfigResponseParsingService } from '../../data/facet-config-respo
import { ViewMode } from '../view-mode.model';
import { SearchFilterConfig } from '../../../shared/search/models/search-filter-config.model';
import { FacetConfigResponse } from '../../../shared/search/models/facet-config-response.model';
import { DspaceRestResponseParsingService } from '../../data/dspace-rest-response-parsing.service';
/**
* Service that performs all actions that have to do with the current search configuration
@@ -60,6 +61,11 @@ export class SearchConfigurationService implements OnDestroy {
*/
public paginatedSearchOptions: BehaviorSubject<PaginatedSearchOptions>;
/**
* The name of the param to enhance and store the route config
*/
public routeConfigurationName: string;
/**
* Default pagination settings
*/
@@ -105,7 +111,7 @@ export class SearchConfigurationService implements OnDestroy {
protected linkService: LinkService,
protected halService: HALEndpointService,
protected requestService: RequestService,
protected rdb: RemoteDataBuildService,) {
protected rdb: RemoteDataBuildService) {
this.initDefaults();
}
@@ -131,7 +137,7 @@ export class SearchConfigurationService implements OnDestroy {
getCurrentConfiguration(defaultConfiguration: string) {
return observableCombineLatest([
this.routeService.getQueryParameterValue('configuration').pipe(startWith(undefined)),
this.routeService.getRouteParameterValue('configuration').pipe(startWith(undefined))
this.routeService.getRouteParameterValue(this.routeConfigurationName ?? 'configuration').pipe(startWith(undefined))
]).pipe(
map(([queryConfig, routeConfig]) => {
return queryConfig || routeConfig || defaultConfiguration;
@@ -483,7 +489,7 @@ export class SearchConfigurationService implements OnDestroy {
* @param {string} configurationName the name of the configuration
* @returns {Observable<RemoteData<SearchFilterConfig[]>>} The found filter configuration
*/
getConfig(scope?: string, configurationName?: string): Observable<RemoteData<SearchFilterConfig[]>> {
getConfig(scope?: string, configurationName?: string, facetParser?: DspaceRestResponseParsingService): Observable<RemoteData<SearchFilterConfig[]>> {
const href$ = this.halService.getEndpoint(this.facetLinkPathPrefix).pipe(
map((url: string) => this.getConfigUrl(url, scope, configurationName)),
);
@@ -534,4 +540,13 @@ export class SearchConfigurationService implements OnDestroy {
return { view };
}));
}
/**
* Set param to use for multiple configurations on same page and reload search subscriptions
*/
setRouteConfigurationParamName(configName: string): void {
this.routeConfigurationName = configName;
this.unsubscribeFromSearchOptions(this.paginationID);
this.setSearchSubscription(this.paginationID, this.paginatedSearchOptions.value);
}
}

View File

@@ -2,7 +2,13 @@ import { Component, Input } from '@angular/core';
import {
AdminNotifyMetricsBox
} from '../../admin/admin-notify-dashboard/admin-notify-metrics/admin-notify-metrics.model';
import { listableObjectComponent } from '../object-collection/shared/listable-object/listable-object.decorator';
import {
AdminNotifySearchResult
} from '../../admin/admin-notify-dashboard/models/admin-notify-message-search-result.model';
import { ViewMode } from '../../core/shared/view-mode.model';
@listableObjectComponent(AdminNotifySearchResult, ViewMode.ListElement)
@Component({
selector: 'ds-notification-box',
templateUrl: './notification-box.component.html',

View File

@@ -101,6 +101,11 @@ export class SearchComponent implements OnInit {
*/
@Input() searchEnabled = true;
/**
* Config param for route service
*/
@Input() routeConfigParam: string;
/**
* The width of the sidebar (bootstrap columns)
*/
@@ -292,8 +297,12 @@ export class SearchComponent implements OnInit {
this.searchConfigService.setPaginationId(this.paginationId);
if (this.routeConfigParam) {
this.searchConfigService.setRouteConfigurationParamName(this.routeConfigParam);
}
if (hasValue(this.configuration)) {
this.routeService.setParameter('configuration', this.configuration);
this.routeService.setParameter(this.routeConfigParam ?? 'configuration', this.configuration);
}
if (hasValue(this.fixedFilterQuery)) {
this.routeService.setParameter('fixedFilterQuery', this.fixedFilterQuery);

View File

@@ -20,7 +20,7 @@ import { ListableObject } from '../object-collection/shared/listable-object.mode
})
export class ThemedSearchComponent extends ThemedComponent<SearchComponent> {
protected inAndOutputNames: (keyof SearchComponent & keyof this)[] = ['configurationList', 'context', 'configuration', 'fixedFilterQuery', 'useCachedVersionIfAvailable', 'inPlaceSearch', 'linkType', 'paginationId', 'searchEnabled', 'sideBarWidth', 'searchFormPlaceholder', 'selectable', 'selectionConfig', 'showCsvExport', 'showSidebar', 'showThumbnails', 'showViewModes', 'useUniquePageId', 'viewModeList', 'showScopeSelector', 'resultFound', 'deselectObject', 'selectObject', 'trackStatistics', 'query'];
protected inAndOutputNames: (keyof SearchComponent & keyof this)[] = ['configurationList', 'context', 'configuration', 'fixedFilterQuery', 'routeConfigParam', 'useCachedVersionIfAvailable', 'inPlaceSearch', 'linkType', 'paginationId', 'searchEnabled', 'sideBarWidth', 'searchFormPlaceholder', 'selectable', 'selectionConfig', 'showCsvExport', 'showSidebar', 'showThumbnails', 'showViewModes', 'useUniquePageId', 'viewModeList', 'showScopeSelector', 'resultFound', 'deselectObject', 'selectObject', 'trackStatistics', 'query'];
@Input() configurationList: SearchConfigurationOption[];
@@ -30,6 +30,8 @@ export class ThemedSearchComponent extends ThemedComponent<SearchComponent> {
@Input() fixedFilterQuery: string;
@Input() routeConfigParam: string;
@Input() useCachedVersionIfAvailable: boolean;
@Input() inPlaceSearch: boolean;

View File

@@ -3471,6 +3471,22 @@
"admin.notify.dashboard.breadcrumbs": "Dashboard",
"NOTIFY.incoming.search.results.head": "Incoming",
"search.filters.filter.relateditem.head": "Related item",
"search.filters.filter.origin.head": "Origin",
"search.filters.filter.target.head": "Target",
"search.filters.filter.queue_status.head": "Queue status",
"search.filters.filter.activity_stream_type.head": "Activity stream type",
"search.filters.filter.coar_notify_type.head": "Coar notify type",
"search.filters.filter.notification_type.head": "Notification type",
"orgunit.listelement.badge": "Organizational Unit",
"orgunit.listelement.no-title": "Untitled",