From 5d8d3e35d58f2151e37faff9191159b44c5a33c9 Mon Sep 17 00:00:00 2001 From: Luca Giamminonni Date: Tue, 29 Mar 2022 16:16:22 +0200 Subject: [PATCH 01/34] [CST-5249] Added suggestions from openaire --- .../admin-notifications-routing-paths.ts | 9 + .../admin-notifications-routing.module.ts | 42 + ...uggestion-targets-page-resolver.service.ts | 32 + ...ons-suggestion-targets-page.component.html | 1 + ...ons-suggestion-targets-page.component.scss | 0 ...-suggestion-targets-page.component.spec.ts | 38 + ...tions-suggestion-targets-page.component.ts | 10 + .../admin-notifications.module.ts | 27 + src/app/admin/admin-routing-paths.ts | 5 + src/app/admin/admin-routing.module.ts | 7 +- .../admin-sidebar/admin-sidebar.component.ts | 38 +- src/app/app-routing.module.ts | 6 + src/app/core/core.module.ts | 6 + ...enaire-suggestion-objects.resource-type.ts | 25 + .../openaire-suggestion-source.model.ts | 47 + .../openaire-suggestion-target.model.ts | 60 + .../models/openaire-suggestion.model.ts | 85 + .../openaire-suggestions-data.service.ts | 301 +++ src/app/openaire/openaire.module.ts | 72 + src/app/openaire/openaire.reducer.ts | 19 + .../openaire/reciter-suggestions/selectors.ts | 97 + .../suggestion-actions.component.html | 28 + .../suggestion-actions.component.scss | 1 + .../suggestion-actions.component.ts | 92 + .../suggestion-evidences.component.html | 20 + .../suggestion-evidences.component.scss | 0 .../suggestion-evidences.component.ts | 15 + .../suggestion-list-element.component.html | 44 + .../suggestion-list-element.component.scss | 16 + .../suggestion-list-element.component.ts | 98 + .../suggestion-targets.actions.ts | 154 ++ .../suggestion-targets.component.html | 51 + .../suggestion-targets.component.scss | 0 .../suggestion-targets.component.ts | 150 ++ .../suggestion-targets.effects.ts | 110 + .../suggestion-targets.reducer.ts | 100 + .../suggestion-targets.state.service.ts | 164 ++ .../suggestions-notification.component.html | 8 + .../suggestions-notification.component.scss | 0 .../suggestions-notification.component.ts | 42 + .../suggestions-popup.component.html | 1 + .../suggestions-popup.component.scss | 0 .../suggestions-popup.component.spec.ts | 79 + .../suggestions-popup.component.ts | 67 + .../suggestions.service.ts | 296 +++ src/app/shared/mocks/openaire.mock.ts | 1797 +++++++++++++++++ .../mocks/reciter-suggestion-targets.mock.ts | 42 + .../shared/mocks/reciter-suggestion.mock.ts | 210 ++ .../suggestions-page-routing-paths.ts | 11 + .../suggestions-page-routing.module.ts | 36 + .../suggestions-page.component.html | 48 + .../suggestions-page.component.scss | 0 .../suggestions-page.component.spec.ts | 107 + .../suggestions-page.component.ts | 285 +++ .../suggestions-page.module.ts | 24 + .../suggestions-page.resolver.ts | 32 + src/assets/i18n/en.json5 | 65 + 57 files changed, 5112 insertions(+), 8 deletions(-) create mode 100644 src/app/admin/admin-notifications/admin-notifications-routing-paths.ts create mode 100644 src/app/admin/admin-notifications/admin-notifications-routing.module.ts create mode 100644 src/app/admin/admin-notifications/admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page-resolver.service.ts create mode 100644 src/app/admin/admin-notifications/admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page.component.html create mode 100644 src/app/admin/admin-notifications/admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page.component.scss create mode 100644 src/app/admin/admin-notifications/admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page.component.spec.ts create mode 100644 src/app/admin/admin-notifications/admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page.component.ts create mode 100644 src/app/admin/admin-notifications/admin-notifications.module.ts create mode 100644 src/app/core/openaire/reciter-suggestions/models/openaire-suggestion-objects.resource-type.ts create mode 100644 src/app/core/openaire/reciter-suggestions/models/openaire-suggestion-source.model.ts create mode 100644 src/app/core/openaire/reciter-suggestions/models/openaire-suggestion-target.model.ts create mode 100644 src/app/core/openaire/reciter-suggestions/models/openaire-suggestion.model.ts create mode 100644 src/app/core/openaire/reciter-suggestions/openaire-suggestions-data.service.ts create mode 100644 src/app/openaire/openaire.module.ts create mode 100644 src/app/openaire/openaire.reducer.ts create mode 100644 src/app/openaire/reciter-suggestions/selectors.ts create mode 100644 src/app/openaire/reciter-suggestions/suggestion-actions/suggestion-actions.component.html create mode 100644 src/app/openaire/reciter-suggestions/suggestion-actions/suggestion-actions.component.scss create mode 100644 src/app/openaire/reciter-suggestions/suggestion-actions/suggestion-actions.component.ts create mode 100644 src/app/openaire/reciter-suggestions/suggestion-list-element/suggestion-evidences/suggestion-evidences.component.html create mode 100644 src/app/openaire/reciter-suggestions/suggestion-list-element/suggestion-evidences/suggestion-evidences.component.scss create mode 100644 src/app/openaire/reciter-suggestions/suggestion-list-element/suggestion-evidences/suggestion-evidences.component.ts create mode 100644 src/app/openaire/reciter-suggestions/suggestion-list-element/suggestion-list-element.component.html create mode 100644 src/app/openaire/reciter-suggestions/suggestion-list-element/suggestion-list-element.component.scss create mode 100644 src/app/openaire/reciter-suggestions/suggestion-list-element/suggestion-list-element.component.ts create mode 100644 src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.actions.ts create mode 100644 src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.component.html create mode 100644 src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.component.scss create mode 100644 src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.component.ts create mode 100644 src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.effects.ts create mode 100644 src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.reducer.ts create mode 100644 src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.state.service.ts create mode 100644 src/app/openaire/reciter-suggestions/suggestions-notification/suggestions-notification.component.html create mode 100644 src/app/openaire/reciter-suggestions/suggestions-notification/suggestions-notification.component.scss create mode 100644 src/app/openaire/reciter-suggestions/suggestions-notification/suggestions-notification.component.ts create mode 100644 src/app/openaire/reciter-suggestions/suggestions-popup/suggestions-popup.component.html create mode 100644 src/app/openaire/reciter-suggestions/suggestions-popup/suggestions-popup.component.scss create mode 100644 src/app/openaire/reciter-suggestions/suggestions-popup/suggestions-popup.component.spec.ts create mode 100644 src/app/openaire/reciter-suggestions/suggestions-popup/suggestions-popup.component.ts create mode 100644 src/app/openaire/reciter-suggestions/suggestions.service.ts create mode 100644 src/app/shared/mocks/openaire.mock.ts create mode 100644 src/app/shared/mocks/reciter-suggestion-targets.mock.ts create mode 100644 src/app/shared/mocks/reciter-suggestion.mock.ts create mode 100644 src/app/suggestions-page/suggestions-page-routing-paths.ts create mode 100644 src/app/suggestions-page/suggestions-page-routing.module.ts create mode 100644 src/app/suggestions-page/suggestions-page.component.html create mode 100644 src/app/suggestions-page/suggestions-page.component.scss create mode 100644 src/app/suggestions-page/suggestions-page.component.spec.ts create mode 100644 src/app/suggestions-page/suggestions-page.component.ts create mode 100644 src/app/suggestions-page/suggestions-page.module.ts create mode 100644 src/app/suggestions-page/suggestions-page.resolver.ts diff --git a/src/app/admin/admin-notifications/admin-notifications-routing-paths.ts b/src/app/admin/admin-notifications/admin-notifications-routing-paths.ts new file mode 100644 index 00000000000..614b2ef49b2 --- /dev/null +++ b/src/app/admin/admin-notifications/admin-notifications-routing-paths.ts @@ -0,0 +1,9 @@ +import { URLCombiner } from '../../core/url-combiner/url-combiner'; +import { getNotificationsModuleRoute } from '../admin-routing-paths'; + +export const NOTIFICATIONS_EDIT_PATH = 'openaire-broker'; +export const NOTIFICATIONS_RECITER_SUGGESTION_PATH = 'suggestion-targets'; + +export function getNotificationsOpenairebrokerRoute(id: string) { + return new URLCombiner(getNotificationsModuleRoute(), NOTIFICATIONS_EDIT_PATH, id).toString(); +} diff --git a/src/app/admin/admin-notifications/admin-notifications-routing.module.ts b/src/app/admin/admin-notifications/admin-notifications-routing.module.ts new file mode 100644 index 00000000000..12bc7b9ec73 --- /dev/null +++ b/src/app/admin/admin-notifications/admin-notifications-routing.module.ts @@ -0,0 +1,42 @@ +import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; + +import { AuthenticatedGuard } from '../../core/auth/authenticated.guard'; +import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver'; +import { I18nBreadcrumbsService } from '../../core/breadcrumbs/i18n-breadcrumbs.service'; +import { NOTIFICATIONS_EDIT_PATH, NOTIFICATIONS_RECITER_SUGGESTION_PATH } from './admin-notifications-routing-paths'; +import { AdminNotificationsSuggestionTargetsPageComponent } from './admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page.component'; +import { AdminNotificationsSuggestionTargetsPageResolver } from './admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page-resolver.service'; + +@NgModule({ + imports: [ + RouterModule.forChild([ + { + canActivate: [ AuthenticatedGuard ], + path: `${NOTIFICATIONS_RECITER_SUGGESTION_PATH}`, + component: AdminNotificationsSuggestionTargetsPageComponent, + pathMatch: 'full', + resolve: { + breadcrumb: I18nBreadcrumbResolver, + reciterSuggestionTargetParams: AdminNotificationsSuggestionTargetsPageResolver + }, + data: { + title: 'admin.notifications.recitersuggestion.page.title', + breadcrumbKey: 'admin.notifications.recitersuggestion', + showBreadcrumbsFluid: false + } + }, + ]) + ], + providers: [ + I18nBreadcrumbResolver, + I18nBreadcrumbsService, + AdminNotificationsSuggestionTargetsPageResolver + ] +}) +/** + * Routing module for the Notifications section of the admin sidebar + */ +export class AdminNotificationsRoutingModule { + +} diff --git a/src/app/admin/admin-notifications/admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page-resolver.service.ts b/src/app/admin/admin-notifications/admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page-resolver.service.ts new file mode 100644 index 00000000000..df1f4b81e69 --- /dev/null +++ b/src/app/admin/admin-notifications/admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page-resolver.service.ts @@ -0,0 +1,32 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router'; + +/** + * Interface for the route parameters. + */ +export interface AdminNotificationsSuggestionTargetsPageParams { + pageId?: string; + pageSize?: number; + currentPage?: number; +} + +/** + * This class represents a resolver that retrieve the route data before the route is activated. + */ +@Injectable() +export class AdminNotificationsSuggestionTargetsPageResolver implements Resolve { + + /** + * Method for resolving the parameters in the current route. + * @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot + * @param {RouterStateSnapshot} state The current RouterStateSnapshot + * @returns AdminNotificationsSuggestionTargetsPageParams Emits the route parameters + */ + resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): AdminNotificationsSuggestionTargetsPageParams { + return { + pageId: route.queryParams.pageId, + pageSize: parseInt(route.queryParams.pageSize, 10), + currentPage: parseInt(route.queryParams.page, 10) + }; + } +} diff --git a/src/app/admin/admin-notifications/admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page.component.html b/src/app/admin/admin-notifications/admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page.component.html new file mode 100644 index 00000000000..5d06a1a6bdd --- /dev/null +++ b/src/app/admin/admin-notifications/admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page.component.html @@ -0,0 +1 @@ + diff --git a/src/app/admin/admin-notifications/admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page.component.scss b/src/app/admin/admin-notifications/admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/app/admin/admin-notifications/admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page.component.spec.ts b/src/app/admin/admin-notifications/admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page.component.spec.ts new file mode 100644 index 00000000000..f9e407f4021 --- /dev/null +++ b/src/app/admin/admin-notifications/admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page.component.spec.ts @@ -0,0 +1,38 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AdminNotificationsSuggestionTargetsPageComponent } from './admin-notifications-suggestion-targets-page.component'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { TranslateModule } from '@ngx-translate/core'; + +describe('AdminNotificationsSuggestionTargetsPageComponent', () => { + let component: AdminNotificationsSuggestionTargetsPageComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + CommonModule, + TranslateModule.forRoot() + ], + declarations: [ + AdminNotificationsSuggestionTargetsPageComponent + ], + providers: [ + AdminNotificationsSuggestionTargetsPageComponent + ], + schemas: [NO_ERRORS_SCHEMA] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AdminNotificationsSuggestionTargetsPageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/admin/admin-notifications/admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page.component.ts b/src/app/admin/admin-notifications/admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page.component.ts new file mode 100644 index 00000000000..a9a77f50890 --- /dev/null +++ b/src/app/admin/admin-notifications/admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page.component.ts @@ -0,0 +1,10 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'ds-admin-notifications-reciter-page', + templateUrl: './admin-notifications-suggestion-targets-page.component.html', + styleUrls: ['./admin-notifications-suggestion-targets-page.component.scss'] +}) +export class AdminNotificationsSuggestionTargetsPageComponent { + +} diff --git a/src/app/admin/admin-notifications/admin-notifications.module.ts b/src/app/admin/admin-notifications/admin-notifications.module.ts new file mode 100644 index 00000000000..47125daad64 --- /dev/null +++ b/src/app/admin/admin-notifications/admin-notifications.module.ts @@ -0,0 +1,27 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { CoreModule } from '../../core/core.module'; +import { SharedModule } from '../../shared/shared.module'; +import { AdminNotificationsRoutingModule } from './admin-notifications-routing.module'; +import { OpenaireModule } from '../../openaire/openaire.module'; +import { AdminNotificationsSuggestionTargetsPageComponent } from './admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page.component'; + +@NgModule({ + imports: [ + CommonModule, + SharedModule, + CoreModule.forRoot(), + AdminNotificationsRoutingModule, + OpenaireModule + ], + declarations: [ + AdminNotificationsSuggestionTargetsPageComponent + ], + entryComponents: [] +}) +/** + * This module handles all components related to the notifications pages + */ +export class AdminNotificationsModule { + +} diff --git a/src/app/admin/admin-routing-paths.ts b/src/app/admin/admin-routing-paths.ts index 3168ea93c92..30f801cecb7 100644 --- a/src/app/admin/admin-routing-paths.ts +++ b/src/app/admin/admin-routing-paths.ts @@ -2,7 +2,12 @@ import { URLCombiner } from '../core/url-combiner/url-combiner'; import { getAdminModuleRoute } from '../app-routing-paths'; export const REGISTRIES_MODULE_PATH = 'registries'; +export const NOTIFICATIONS_MODULE_PATH = 'notifications'; export function getRegistriesModuleRoute() { return new URLCombiner(getAdminModuleRoute(), REGISTRIES_MODULE_PATH).toString(); } + +export function getNotificationsModuleRoute() { + return new URLCombiner(getAdminModuleRoute(), NOTIFICATIONS_MODULE_PATH).toString(); +} diff --git a/src/app/admin/admin-routing.module.ts b/src/app/admin/admin-routing.module.ts index ee5cb8737bc..782a7faa380 100644 --- a/src/app/admin/admin-routing.module.ts +++ b/src/app/admin/admin-routing.module.ts @@ -6,11 +6,16 @@ import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.reso import { AdminWorkflowPageComponent } from './admin-workflow-page/admin-workflow-page.component'; import { I18nBreadcrumbsService } from '../core/breadcrumbs/i18n-breadcrumbs.service'; import { AdminCurationTasksComponent } from './admin-curation-tasks/admin-curation-tasks.component'; -import { REGISTRIES_MODULE_PATH } from './admin-routing-paths'; +import { REGISTRIES_MODULE_PATH, NOTIFICATIONS_MODULE_PATH } from './admin-routing-paths'; @NgModule({ imports: [ RouterModule.forChild([ + { + path: NOTIFICATIONS_MODULE_PATH, + loadChildren: () => import('./admin-notifications/admin-notifications.module') + .then((m) => m.AdminNotificationsModule), + }, { path: REGISTRIES_MODULE_PATH, loadChildren: () => import('./admin-registries/admin-registries.module') diff --git a/src/app/admin/admin-sidebar/admin-sidebar.component.ts b/src/app/admin/admin-sidebar/admin-sidebar.component.ts index dc9d2a817ff..dd5d7e507a0 100644 --- a/src/app/admin/admin-sidebar/admin-sidebar.component.ts +++ b/src/app/admin/admin-sidebar/admin-sidebar.component.ts @@ -22,6 +22,7 @@ import { CSSVariableService } from '../../shared/sass-helper/sass-helper.service import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; import { Router, ActivatedRoute } from '@angular/router'; +import {NOTIFICATIONS_RECITER_SUGGESTION_PATH} from "../admin-notifications/admin-notifications-routing-paths"; /** * Component representing the admin sidebar @@ -277,7 +278,7 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit { // link: '' // } as LinkMenuItemModel, // icon: 'chart-bar', - // index: 8 + // index: 9 // }, /* Control Panel */ @@ -292,7 +293,7 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit { // link: '' // } as LinkMenuItemModel, // icon: 'cogs', - // index: 9 + // index: 10 // }, /* Processes */ @@ -306,7 +307,7 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit { link: '/processes' } as LinkMenuItemModel, icon: 'terminal', - index: 10 + index: 12 }, ]; menuList.forEach((menuSection) => this.menuService.addSection(this.menuID, Object.assign(menuSection, { @@ -465,6 +466,29 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit { createSiteAdministratorMenuSections() { this.authorizationService.isAuthorized(FeatureID.AdministratorOf).subscribe((authorized) => { const menuList = [ + /* Notifications */ + { + id: 'notifications', + active: false, + visible: authorized, + model: { + type: MenuItemType.TEXT, + text: 'menu.section.notifications' + } as TextMenuItemModel, + icon: 'bell', + index: 4 + }, + { + id: 'notifications_reciter', + parentID: 'notifications', + active: false, + visible: authorized, + model: { + type: MenuItemType.LINK, + text: 'menu.section.notifications_reciter', + link: '/admin/notifications/' + NOTIFICATIONS_RECITER_SUGGESTION_PATH + } as LinkMenuItemModel, + }, /* Admin Search */ { id: 'admin_search', @@ -476,7 +500,7 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit { link: '/admin/search' } as LinkMenuItemModel, icon: 'search', - index: 5 + index: 6 }, /* Registries */ { @@ -488,7 +512,7 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit { text: 'menu.section.registries' } as TextMenuItemModel, icon: 'list', - index: 6 + index: 7 }, { id: 'registries_metadata', @@ -524,7 +548,7 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit { link: 'admin/curation-tasks' } as LinkMenuItemModel, icon: 'filter', - index: 7 + index: 8 }, /* Workflow */ @@ -601,7 +625,7 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit { text: 'menu.section.access_control' } as TextMenuItemModel, icon: 'key', - index: 4 + index: 5 }, ]; diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 88f7791b1b2..9bf55185584 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -30,6 +30,7 @@ import { ThemedForbiddenComponent } from './forbidden/themed-forbidden.component import { GroupAdministratorGuard } from './core/data/feature-authorization/feature-authorization-guard/group-administrator.guard'; import { ThemedPageInternalServerErrorComponent } from './page-internal-server-error/themed-page-internal-server-error.component'; import { ServerCheckGuard } from './core/server-check/server-check.guard'; +import { SUGGESTION_MODULE_PATH } from './suggestions-page/suggestions-page-routing-paths'; @NgModule({ imports: [ @@ -190,6 +191,11 @@ import { ServerCheckGuard } from './core/server-check/server-check.guard'; .then((m) => m.ProcessPageModule), canActivate: [AuthenticatedGuard, EndUserAgreementCurrentUserGuard] }, + { path: SUGGESTION_MODULE_PATH, + loadChildren: () => import('./suggestions-page/suggestions-page.module') + .then((m) => m.SuggestionsPageModule), + canActivate: [AuthenticatedGuard, EndUserAgreementCurrentUserGuard] + }, { path: INFO_MODULE_PATH, loadChildren: () => import('./info/info.module').then((m) => m.InfoModule) diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index 8d8a614a899..6293fbd1092 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -162,6 +162,9 @@ import { SearchConfig } from './shared/search/search-filters/search-config.model import { SequenceService } from './shared/sequence.service'; import { GroupDataService } from './eperson/group-data.service'; import { SubmissionAccessesModel } from './config/models/config-submission-accesses.model'; +import { OpenaireSuggestionTarget } from './openaire/reciter-suggestions/models/openaire-suggestion-target.model'; +import { OpenaireSuggestion } from './openaire/reciter-suggestions/models/openaire-suggestion.model'; +import { OpenaireSuggestionSource } from './openaire/reciter-suggestions/models/openaire-suggestion-source.model'; /** * When not in production, endpoint responses can be mocked for testing purposes @@ -343,6 +346,9 @@ export const models = ShortLivedToken, Registration, UsageReport, + OpenaireSuggestion, + OpenaireSuggestionTarget, + OpenaireSuggestionSource, Root, SearchConfig, SubmissionAccessesModel diff --git a/src/app/core/openaire/reciter-suggestions/models/openaire-suggestion-objects.resource-type.ts b/src/app/core/openaire/reciter-suggestions/models/openaire-suggestion-objects.resource-type.ts new file mode 100644 index 00000000000..e31006959ff --- /dev/null +++ b/src/app/core/openaire/reciter-suggestions/models/openaire-suggestion-objects.resource-type.ts @@ -0,0 +1,25 @@ +import { ResourceType } from '../../../shared/resource-type'; + +/** + * The resource type for the Suggestion Target object + * + * Needs to be in a separate file to prevent circular + * dependencies in webpack. + */ +export const SUGGESTION_TARGET = new ResourceType('suggestiontarget'); + +/** + * The resource type for the Suggestion Source object + * + * Needs to be in a separate file to prevent circular + * dependencies in webpack. + */ +export const SUGGESTION_SOURCE = new ResourceType('suggestionsource'); + +/** + * The resource type for the Suggestion object + * + * Needs to be in a separate file to prevent circular + * dependencies in webpack. + */ +export const SUGGESTION = new ResourceType('suggestion'); diff --git a/src/app/core/openaire/reciter-suggestions/models/openaire-suggestion-source.model.ts b/src/app/core/openaire/reciter-suggestions/models/openaire-suggestion-source.model.ts new file mode 100644 index 00000000000..6da9fd47b9b --- /dev/null +++ b/src/app/core/openaire/reciter-suggestions/models/openaire-suggestion-source.model.ts @@ -0,0 +1,47 @@ +import { autoserialize, deserialize } from 'cerialize'; + +import { CacheableObject } from '../../../cache/object-cache.reducer'; +import { SUGGESTION_SOURCE } from './openaire-suggestion-objects.resource-type'; +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'; + +/** + * The interface representing the Suggestion Source model + */ +@typedObject +export class OpenaireSuggestionSource implements CacheableObject { + /** + * A string representing the kind of object, e.g. community, item, … + */ + static type = SUGGESTION_SOURCE; + + /** + * The Suggestion Target id + */ + @autoserialize + id: string; + + /** + * The total number of suggestions provided by Suggestion Target for + */ + @autoserialize + total: 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, + suggestiontargets: HALLink + }; +} diff --git a/src/app/core/openaire/reciter-suggestions/models/openaire-suggestion-target.model.ts b/src/app/core/openaire/reciter-suggestions/models/openaire-suggestion-target.model.ts new file mode 100644 index 00000000000..e35972bc798 --- /dev/null +++ b/src/app/core/openaire/reciter-suggestions/models/openaire-suggestion-target.model.ts @@ -0,0 +1,60 @@ +import { autoserialize, deserialize } from 'cerialize'; + +import { CacheableObject } from '../../../cache/object-cache.reducer'; +import { SUGGESTION_TARGET } from './openaire-suggestion-objects.resource-type'; +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'; + +/** + * The interface representing the Suggestion Target model + */ +@typedObject +export class OpenaireSuggestionTarget implements CacheableObject { + /** + * A string representing the kind of object, e.g. community, item, … + */ + static type = SUGGESTION_TARGET; + + /** + * The Suggestion Target id + */ + @autoserialize + id: string; + + /** + * The Suggestion Target name to display + */ + @autoserialize + display: string; + + /** + * The Suggestion Target source to display + */ + @autoserialize + source: string; + + /** + * The total number of suggestions provided by Suggestion Target for + */ + @autoserialize + total: 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, + suggestions: HALLink, + target: HALLink + }; +} diff --git a/src/app/core/openaire/reciter-suggestions/models/openaire-suggestion.model.ts b/src/app/core/openaire/reciter-suggestions/models/openaire-suggestion.model.ts new file mode 100644 index 00000000000..3ff5d7b630c --- /dev/null +++ b/src/app/core/openaire/reciter-suggestions/models/openaire-suggestion.model.ts @@ -0,0 +1,85 @@ +import { autoserialize, autoserializeAs, deserialize } from 'cerialize'; + +import { CacheableObject } from '../../../cache/object-cache.reducer'; +import { SUGGESTION } from './openaire-suggestion-objects.resource-type'; +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 { MetadataMap, MetadataMapSerializer } from '../../../shared/metadata.models'; + +export interface SuggestionEvidences { + [sectionId: string]: { + score: string; + notes: string + }; +} +/** + * The interface representing the Suggestion Source model + */ +@typedObject +export class OpenaireSuggestion implements CacheableObject { + /** + * A string representing the kind of object, e.g. community, item, … + */ + static type = SUGGESTION; + + /** + * The Suggestion id + */ + @autoserialize + id: string; + + /** + * The Suggestion name to display + */ + @autoserialize + display: string; + + /** + * The Suggestion source to display + */ + @autoserialize + source: string; + + /** + * The Suggestion external source uri + */ + @autoserialize + externalSourceUri: string; + + /** + * The Total Score of the suggestion + */ + @autoserialize + score: string; + + /** + * The total number of suggestions provided by Suggestion Target for + */ + @autoserialize + evidences: SuggestionEvidences; + + /** + * All metadata of this suggestion object + */ + @excludeFromEquals + @autoserializeAs(MetadataMapSerializer) + metadata: MetadataMap; + + /** + * The type of this ConfigObject + */ + @excludeFromEquals + @autoserialize + type: ResourceType; + + /** + * The links to all related resources returned by the rest api. + */ + @deserialize + _links: { + self: HALLink, + target: HALLink + }; +} diff --git a/src/app/core/openaire/reciter-suggestions/openaire-suggestions-data.service.ts b/src/app/core/openaire/reciter-suggestions/openaire-suggestions-data.service.ts new file mode 100644 index 00000000000..d961eaf9b52 --- /dev/null +++ b/src/app/core/openaire/reciter-suggestions/openaire-suggestions-data.service.ts @@ -0,0 +1,301 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Store } from '@ngrx/store'; + +import { Observable } from 'rxjs'; + +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 { SUGGESTION_TARGET } from './models/openaire-suggestion-objects.resource-type'; +import { FollowLinkConfig } from '../../../shared/utils/follow-link-config.model'; +import { PaginatedList } from '../../data/paginated-list.model'; +import { OpenaireSuggestionSource } from './models/openaire-suggestion-source.model'; +import { OpenaireSuggestionTarget } from './models/openaire-suggestion-target.model'; +import { OpenaireSuggestion } from './models/openaire-suggestion.model'; +import { RequestParam } from '../../cache/models/request-param.model'; +import { NoContent } from '../../shared/NoContent.model'; + +/* tslint:disable:max-classes-per-file */ + +/** + * A private DataService implementation to delegate specific methods to. + */ +class SuggestionDataServiceImpl extends DataService { + /** + * The REST endpoint. + */ + protected linkPath = 'suggestions'; + + /** + * Initialize service variables + * @param {RequestService} requestService + * @param {RemoteDataBuildService} rdbService + * @param {Store} store + * @param {ObjectCacheService} objectCache + * @param {HALEndpointService} halService + * @param {NotificationsService} notificationsService + * @param {HttpClient} http + * @param {ChangeAnalyzer} comparator + */ + constructor( + protected requestService: RequestService, + protected rdbService: RemoteDataBuildService, + protected store: Store, + protected objectCache: ObjectCacheService, + protected halService: HALEndpointService, + protected notificationsService: NotificationsService, + protected http: HttpClient, + protected comparator: ChangeAnalyzer) { + super(); + } +} + +/** + * A private DataService implementation to delegate specific methods to. + */ +class SuggestionTargetsDataServiceImpl extends DataService { + /** + * The REST endpoint. + */ + protected linkPath = 'suggestiontargets'; + + /** + * Initialize service variables + * @param {RequestService} requestService + * @param {RemoteDataBuildService} rdbService + * @param {Store} store + * @param {ObjectCacheService} objectCache + * @param {HALEndpointService} halService + * @param {NotificationsService} notificationsService + * @param {HttpClient} http + * @param {ChangeAnalyzer} comparator + */ + constructor( + protected requestService: RequestService, + protected rdbService: RemoteDataBuildService, + protected store: Store, + protected objectCache: ObjectCacheService, + protected halService: HALEndpointService, + protected notificationsService: NotificationsService, + protected http: HttpClient, + protected comparator: ChangeAnalyzer) { + super(); + } +} + +/** + * A private DataService implementation to delegate specific methods to. + */ +class SuggestionSourcesDataServiceImpl extends DataService { + /** + * The REST endpoint. + */ + protected linkPath = 'suggestionsources'; + + /** + * Initialize service variables + * @param {RequestService} requestService + * @param {RemoteDataBuildService} rdbService + * @param {Store} store + * @param {ObjectCacheService} objectCache + * @param {HALEndpointService} halService + * @param {NotificationsService} notificationsService + * @param {HttpClient} http + * @param {ChangeAnalyzer} comparator + */ + constructor( + protected requestService: RequestService, + protected rdbService: RemoteDataBuildService, + protected store: Store, + protected objectCache: ObjectCacheService, + protected halService: HALEndpointService, + protected notificationsService: NotificationsService, + protected http: HttpClient, + protected comparator: ChangeAnalyzer) { + super(); + } +} + +/** + * The service handling all Suggestion Target REST requests. + */ +@Injectable() +@dataService(SUGGESTION_TARGET) +export class OpenaireSuggestionsDataService { + protected searchFindBySourceMethod = 'findBySource'; + protected searchFindByTargetMethod = 'findByTarget'; + protected searchFindByTargetAndSourceMethod = 'findByTargetAndSource'; + + /** + * A private DataService implementation to delegate specific methods to. + */ + private suggestionsDataService: SuggestionDataServiceImpl; + + /** + * A private DataService implementation to delegate specific methods to. + */ + private suggestionSourcesDataService: SuggestionSourcesDataServiceImpl; + + /** + * A private DataService implementation to delegate specific methods to. + */ + private suggestionTargetsDataService: SuggestionTargetsDataServiceImpl; + + /** + * Initialize service variables + * @param {RequestService} requestService + * @param {RemoteDataBuildService} rdbService + * @param {ObjectCacheService} objectCache + * @param {HALEndpointService} halService + * @param {NotificationsService} notificationsService + * @param {HttpClient} http + * @param {DefaultChangeAnalyzer} comparatorSuggestions + * @param {DefaultChangeAnalyzer} comparatorSources + * @param {DefaultChangeAnalyzer} comparatorTargets + */ + constructor( + protected requestService: RequestService, + protected rdbService: RemoteDataBuildService, + protected objectCache: ObjectCacheService, + protected halService: HALEndpointService, + protected notificationsService: NotificationsService, + protected http: HttpClient, + protected comparatorSuggestions: DefaultChangeAnalyzer, + protected comparatorSources: DefaultChangeAnalyzer, + protected comparatorTargets: DefaultChangeAnalyzer, + ) { + this.suggestionsDataService = new SuggestionDataServiceImpl(requestService, rdbService, null, objectCache, halService, notificationsService, http, comparatorSuggestions); + this.suggestionSourcesDataService = new SuggestionSourcesDataServiceImpl(requestService, rdbService, null, objectCache, halService, notificationsService, http, comparatorSources); + this.suggestionTargetsDataService = new SuggestionTargetsDataServiceImpl(requestService, rdbService, null, objectCache, halService, notificationsService, http, comparatorTargets); + } + + /** + * Return the list of Suggestion Target + * + * @param options + * Find list options object. + * @return Observable>> + * The list of Suggestion Sources. + */ + public getSources(options: FindListOptions = {}): Observable>> { + return this.suggestionSourcesDataService.findAll(options); + } + + /** + * Return the list of Suggestion Target for a given source + * + * @param source + * The source for which to find targets. + * @param options + * Find list options object. + * @param linksToFollow + * List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved. + * @return Observable>> + * The list of Suggestion Target. + */ + public getTargets( + source: string, + options: FindListOptions = {}, + ...linksToFollow: FollowLinkConfig[] + ): Observable>> { + options.searchParams = [new RequestParam('source', source)]; + + return this.suggestionTargetsDataService.searchBy(this.searchFindBySourceMethod, options, true, true, ...linksToFollow); + } + + /** + * Return the list of Suggestion Target for a given user + * + * @param userId + * The user Id for which to find targets. + * @param options + * Find list options object. + * @param linksToFollow + * List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved. + * @return Observable>> + * The list of Suggestion Target. + */ + public getTargetsByUser( + userId: string, + options: FindListOptions = {}, + ...linksToFollow: FollowLinkConfig[] + ): Observable>> { + options.searchParams = [new RequestParam('target', userId)]; + + return this.suggestionTargetsDataService.searchBy(this.searchFindByTargetMethod, options, true, true, ...linksToFollow); + } + + /** + * Return a Suggestion Target for a given id + * + * @param targetId + * The target id to retrieve. + * + * @return Observable> + * The list of Suggestion Target. + */ + public getTargetById(targetId: string): Observable> { + return this.suggestionTargetsDataService.findById(targetId); + } + + /** + * Used to delete Suggestion + * @suggestionId + */ + public deleteSuggestion(suggestionId: string): Observable> { + return this.suggestionsDataService.delete(suggestionId); + } + + /** + * Used to fetch Suggestion notification for user + * @suggestionId + */ + public getSuggestion(suggestionId: string, ...linksToFollow: FollowLinkConfig[]): Observable> { + return this.suggestionsDataService.findById(suggestionId, true, true, ...linksToFollow); + } + + /** + * Return the list of Suggestion for a given target and source + * + * @param target + * The target for which to find suggestions. + * @param source + * The source for which to find suggestions. + * @param options + * Find list options object. + * @param linksToFollow + * List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved. + * @return Observable>> + * The list of Suggestion. + */ + public getSuggestionsByTargetAndSource( + target: string, + source: string, + options: FindListOptions = {}, + ...linksToFollow: FollowLinkConfig[] + ): Observable>> { + options.searchParams = [ + new RequestParam('target', target), + new RequestParam('source', source) + ]; + + return this.suggestionsDataService.searchBy(this.searchFindByTargetAndSourceMethod, options, true, true, ...linksToFollow); + } + + /** + * Clear findByTargetAndSource suggestions requests from cache + */ + public clearSuggestionRequests() { + this.requestService.setStaleByHrefSubstring(this.searchFindByTargetAndSourceMethod); + } +} diff --git a/src/app/openaire/openaire.module.ts b/src/app/openaire/openaire.module.ts new file mode 100644 index 00000000000..22d04f3002b --- /dev/null +++ b/src/app/openaire/openaire.module.ts @@ -0,0 +1,72 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { Action, StoreConfig, StoreModule } from '@ngrx/store'; +import { EffectsModule } from '@ngrx/effects'; + +import { CoreModule } from '../core/core.module'; +import { SharedModule } from '../shared/shared.module'; +import { storeModuleConfig } from '../app.reducer'; +import { openaireReducers, OpenaireState } from './openaire.reducer'; +import { SuggestionTargetsStateService } from './reciter-suggestions/suggestion-targets/suggestion-targets.state.service'; +import { SuggestionsService } from './reciter-suggestions/suggestions.service'; +import { OpenaireSuggestionsDataService } from '../core/openaire/reciter-suggestions/openaire-suggestions-data.service'; +import { SuggestionTargetsComponent } from './reciter-suggestions/suggestion-targets/suggestion-targets.component'; +import { SuggestionListElementComponent } from './reciter-suggestions/suggestion-list-element/suggestion-list-element.component'; +import { SuggestionEvidencesComponent } from './reciter-suggestions/suggestion-list-element/suggestion-evidences/suggestion-evidences.component'; +import { SuggestionActionsComponent } from './reciter-suggestions/suggestion-actions/suggestion-actions.component'; +import { SuggestionsPopupComponent } from './reciter-suggestions/suggestions-popup/suggestions-popup.component'; +import { SuggestionsNotificationComponent } from './reciter-suggestions/suggestions-notification/suggestions-notification.component'; +import { TranslateModule } from '@ngx-translate/core'; +import { SearchModule } from '../shared/search/search.module'; + +const MODULES = [ + CommonModule, + SharedModule, + CoreModule.forRoot(), + StoreModule.forFeature('openaire', openaireReducers, storeModuleConfig as StoreConfig), + TranslateModule +]; + +const COMPONENTS = [ + SuggestionTargetsComponent, + SuggestionActionsComponent, + SuggestionListElementComponent, + SuggestionEvidencesComponent, + SuggestionsPopupComponent, + SuggestionsNotificationComponent +]; + +const DIRECTIVES = [ ]; + + +const PROVIDERS = [ + SuggestionTargetsStateService, + SuggestionsService, + OpenaireSuggestionsDataService +]; + +@NgModule({ + imports: [ + ...MODULES, + SearchModule + ], + declarations: [ + ...COMPONENTS, + ...DIRECTIVES, + ], + providers: [ + ...PROVIDERS + ], + entryComponents: [ + ], + exports: [ + ...COMPONENTS, + ...DIRECTIVES + ] +}) + +/** + * This module handles all components that are necessary for the OpenAIRE components + */ +export class OpenaireModule { +} diff --git a/src/app/openaire/openaire.reducer.ts b/src/app/openaire/openaire.reducer.ts new file mode 100644 index 00000000000..cacf3c72834 --- /dev/null +++ b/src/app/openaire/openaire.reducer.ts @@ -0,0 +1,19 @@ +import { ActionReducerMap, createFeatureSelector } from '@ngrx/store'; + +import { + SuggestionTargetsReducer, + SuggestionTargetState +} from './reciter-suggestions/suggestion-targets/suggestion-targets.reducer'; + +/** + * The OpenAIRE State + */ +export interface OpenaireState { + 'suggestionTarget': SuggestionTargetState; +} + +export const openaireReducers: ActionReducerMap = { + suggestionTarget: SuggestionTargetsReducer, +}; + +export const openaireSelector = createFeatureSelector('openaire'); diff --git a/src/app/openaire/reciter-suggestions/selectors.ts b/src/app/openaire/reciter-suggestions/selectors.ts new file mode 100644 index 00000000000..e699b27dbae --- /dev/null +++ b/src/app/openaire/reciter-suggestions/selectors.ts @@ -0,0 +1,97 @@ +import { createSelector, MemoizedSelector } from '@ngrx/store'; +import { subStateSelector } from '../../shared/selector.util'; +import { openaireSelector, OpenaireState } from '../openaire.reducer'; +import { OpenaireSuggestionTarget } from '../../core/openaire/reciter-suggestions/models/openaire-suggestion-target.model'; +import { SuggestionTargetState } from './suggestion-targets/suggestion-targets.reducer'; + +/** + * Returns the Reciter Suggestion Target state. + * @function _getReciterSuggestionTargetState + * @param {AppState} state Top level state. + * @return {OpenaireState} + */ +const _getReciterSuggestionTargetState = (state: any) => state.openaire; + +// Reciter Suggestion Targets +// ---------------------------------------------------------------------------- + +/** + * Returns the Reciter Suggestion Targets State. + * @function reciterSuggestionTargetStateSelector + * @return {OpenaireState} + */ +export function reciterSuggestionTargetStateSelector(): MemoizedSelector { + return subStateSelector(openaireSelector, 'suggestionTarget'); +} + +/** + * Returns the Reciter Suggestion Targets list. + * @function reciterSuggestionTargetObjectSelector + * @return {OpenaireReciterSuggestionTarget[]} + */ +export function reciterSuggestionTargetObjectSelector(): MemoizedSelector { + return subStateSelector(reciterSuggestionTargetStateSelector(), 'targets'); +} + +/** + * Returns true if the Reciter Suggestion Targets are loaded. + * @function isReciterSuggestionTargetLoadedSelector + * @return {boolean} + */ +export const isReciterSuggestionTargetLoadedSelector = createSelector(_getReciterSuggestionTargetState, + (state: OpenaireState) => state.suggestionTarget.loaded +); + +/** + * Returns true if the deduplication sets are processing. + * @function isDeduplicationSetsProcessingSelector + * @return {boolean} + */ +export const isreciterSuggestionTargetProcessingSelector = createSelector(_getReciterSuggestionTargetState, + (state: OpenaireState) => state.suggestionTarget.processing +); + +/** + * Returns the total available pages of Reciter Suggestion Targets. + * @function getreciterSuggestionTargetTotalPagesSelector + * @return {number} + */ +export const getreciterSuggestionTargetTotalPagesSelector = createSelector(_getReciterSuggestionTargetState, + (state: OpenaireState) => state.suggestionTarget.totalPages +); + +/** + * Returns the current page of Reciter Suggestion Targets. + * @function getreciterSuggestionTargetCurrentPageSelector + * @return {number} + */ +export const getreciterSuggestionTargetCurrentPageSelector = createSelector(_getReciterSuggestionTargetState, + (state: OpenaireState) => state.suggestionTarget.currentPage +); + +/** + * Returns the total number of Reciter Suggestion Targets. + * @function getreciterSuggestionTargetTotalsSelector + * @return {number} + */ +export const getreciterSuggestionTargetTotalsSelector = createSelector(_getReciterSuggestionTargetState, + (state: OpenaireState) => state.suggestionTarget.totalElements +); + +/** + * Returns Suggestion Targets for the current user. + * @function getCurrentUserReciterSuggestionTargetSelector + * @return {OpenaireSuggestionTarget[]} + */ +export const getCurrentUserSuggestionTargetsSelector = createSelector(_getReciterSuggestionTargetState, + (state: OpenaireState) => state.suggestionTarget.currentUserTargets +); + +/** + * Returns whether or not the user has consulted their suggestions + * @function getCurrentUserReciterSuggestionTargetSelector + * @return {boolean} + */ +export const getCurrentUserSuggestionTargetsVisitedSelector = createSelector(_getReciterSuggestionTargetState, + (state: OpenaireState) => state.suggestionTarget.currentUserTargetsVisited +); diff --git a/src/app/openaire/reciter-suggestions/suggestion-actions/suggestion-actions.component.html b/src/app/openaire/reciter-suggestions/suggestion-actions/suggestion-actions.component.html new file mode 100644 index 00000000000..7ec3e61395e --- /dev/null +++ b/src/app/openaire/reciter-suggestions/suggestion-actions/suggestion-actions.component.html @@ -0,0 +1,28 @@ +
+
+ + + + + + + +
+ + +
diff --git a/src/app/openaire/reciter-suggestions/suggestion-actions/suggestion-actions.component.scss b/src/app/openaire/reciter-suggestions/suggestion-actions/suggestion-actions.component.scss new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/src/app/openaire/reciter-suggestions/suggestion-actions/suggestion-actions.component.scss @@ -0,0 +1 @@ + diff --git a/src/app/openaire/reciter-suggestions/suggestion-actions/suggestion-actions.component.ts b/src/app/openaire/reciter-suggestions/suggestion-actions/suggestion-actions.component.ts new file mode 100644 index 00000000000..cf219011237 --- /dev/null +++ b/src/app/openaire/reciter-suggestions/suggestion-actions/suggestion-actions.component.ts @@ -0,0 +1,92 @@ +import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { ItemType } from '../../../core/shared/item-relationships/item-type.model'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { OpenaireSuggestion } from '../../../core/openaire/reciter-suggestions/models/openaire-suggestion.model'; +import { SuggestionApproveAndImport } from '../suggestion-list-element/suggestion-list-element.component'; +import { Collection } from '../../../core/shared/collection.model'; +import { take } from 'rxjs/operators'; +import { CreateItemParentSelectorComponent } from '../../../shared/dso-selector/modal-wrappers/create-item-parent-selector/create-item-parent-selector.component'; + +@Component({ + selector: 'ds-suggestion-actions', + styleUrls: [ './suggestion-actions.component.scss' ], + templateUrl: './suggestion-actions.component.html' +}) +export class SuggestionActionsComponent { + + @Input() object: OpenaireSuggestion; + + @Input() isBulk = false; + + @Input() hasEvidence = false; + + @Input() seeEvidence = false; + + @Input() isCollectionFixed = false; + + /** + * The component is used to Delete suggestion + */ + @Output() notMineClicked = new EventEmitter(); + + /** + * The component is used to approve & import + */ + @Output() approveAndImport = new EventEmitter(); + + /** + * The component is used to approve & import + */ + @Output() seeEvidences = new EventEmitter(); + + constructor(private modalService: NgbModal) { } + + /** + * Method called on clicking the button "approve & import", It opens a dialog for + * select a collection and it emits an approveAndImport event. + */ + openDialog(entity: ItemType) { + + const modalRef = this.modalService.open(CreateItemParentSelectorComponent); + modalRef.componentInstance.emitOnly = true; + modalRef.componentInstance.entityType = entity.label; + + modalRef.componentInstance.select.pipe(take(1)) + .subscribe((collection: Collection) => { + this.approveAndImport.emit({ + suggestion: this.isBulk ? undefined : this.object, + collectionId: collection.id + }); + }); + } + + approveAndImportCollectionFixed() { + this.approveAndImport.emit({ + suggestion: this.isBulk ? undefined : this.object, + collectionId: null + }); + } + + + /** + * Delete the suggestion + */ + notMine() { + this.notMineClicked.emit(this.isBulk ? undefined : this.object.id); + } + + /** + * Toggle See Evidence + */ + toggleSeeEvidences() { + this.seeEvidences.emit(!this.seeEvidence); + } + + notMineLabel(): string { + return this.isBulk ? 'reciter.suggestion.notMine.bulk' : 'reciter.suggestion.notMine' ; + } + + approveAndImportLabel(): string { + return this.isBulk ? 'reciter.suggestion.approveAndImport.bulk' : 'reciter.suggestion.approveAndImport'; + } +} diff --git a/src/app/openaire/reciter-suggestions/suggestion-list-element/suggestion-evidences/suggestion-evidences.component.html b/src/app/openaire/reciter-suggestions/suggestion-list-element/suggestion-evidences/suggestion-evidences.component.html new file mode 100644 index 00000000000..5ad4f0a9783 --- /dev/null +++ b/src/app/openaire/reciter-suggestions/suggestion-list-element/suggestion-evidences/suggestion-evidences.component.html @@ -0,0 +1,20 @@ +
+
+ + + + + + + + + + + + + + + +
{{'reciter.suggestion.evidence.score' | translate}}{{'reciter.suggestion.evidence.type' | translate}}{{'reciter.suggestion.evidence.notes' | translate}}
{{evidences[evidence].score}}{{evidence | translate}}{{evidences[evidence].notes}}
+
+
diff --git a/src/app/openaire/reciter-suggestions/suggestion-list-element/suggestion-evidences/suggestion-evidences.component.scss b/src/app/openaire/reciter-suggestions/suggestion-list-element/suggestion-evidences/suggestion-evidences.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/app/openaire/reciter-suggestions/suggestion-list-element/suggestion-evidences/suggestion-evidences.component.ts b/src/app/openaire/reciter-suggestions/suggestion-list-element/suggestion-evidences/suggestion-evidences.component.ts new file mode 100644 index 00000000000..e0536ae7234 --- /dev/null +++ b/src/app/openaire/reciter-suggestions/suggestion-list-element/suggestion-evidences/suggestion-evidences.component.ts @@ -0,0 +1,15 @@ +import { Component, Input } from '@angular/core'; +import { fadeIn } from '../../../../shared/animations/fade'; +import { SuggestionEvidences } from '../../../../core/openaire/reciter-suggestions/models/openaire-suggestion.model'; + +@Component({ + selector: 'ds-suggestion-evidences', + styleUrls: [ './suggestion-evidences.component.scss' ], + templateUrl: './suggestion-evidences.component.html', + animations: [fadeIn] +}) +export class SuggestionEvidencesComponent { + + @Input() evidences: SuggestionEvidences; + +} diff --git a/src/app/openaire/reciter-suggestions/suggestion-list-element/suggestion-list-element.component.html b/src/app/openaire/reciter-suggestions/suggestion-list-element/suggestion-list-element.component.html new file mode 100644 index 00000000000..05f9c0ac771 --- /dev/null +++ b/src/app/openaire/reciter-suggestions/suggestion-list-element/suggestion-list-element.component.html @@ -0,0 +1,44 @@ +
+
+ +
+
+ +
+
+ +
+
+
{{'reciter.suggestion.totalScore' | translate}}
+ {{ object.score }} +
+
+ +
+ + + + +
+
+ +
+
+ +
+
+
+
diff --git a/src/app/openaire/reciter-suggestions/suggestion-list-element/suggestion-list-element.component.scss b/src/app/openaire/reciter-suggestions/suggestion-list-element/suggestion-list-element.component.scss new file mode 100644 index 00000000000..1c522095189 --- /dev/null +++ b/src/app/openaire/reciter-suggestions/suggestion-list-element/suggestion-list-element.component.scss @@ -0,0 +1,16 @@ +.issue-date { + color: #c8c8c8; +} + +.parent { + display: flex; + gap:10px; +} + +.import { + flex: initial; +} + +.suggestion-score { + font-size: 1.5rem; +} diff --git a/src/app/openaire/reciter-suggestions/suggestion-list-element/suggestion-list-element.component.ts b/src/app/openaire/reciter-suggestions/suggestion-list-element/suggestion-list-element.component.ts new file mode 100644 index 00000000000..8227dc3213b --- /dev/null +++ b/src/app/openaire/reciter-suggestions/suggestion-list-element/suggestion-list-element.component.ts @@ -0,0 +1,98 @@ +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; + +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; + +import { fadeIn } from '../../../shared/animations/fade'; +import { OpenaireSuggestion } from '../../../core/openaire/reciter-suggestions/models/openaire-suggestion.model'; +import { Item } from '../../../core/shared/item.model'; +import { isNotEmpty } from '../../../shared/empty.util'; + +export interface SuggestionApproveAndImport { + suggestion: OpenaireSuggestion; + collectionId: string; +} + +@Component({ + selector: 'ds-suggestion-list-item', + styleUrls: ['./suggestion-list-element.component.scss'], + templateUrl: './suggestion-list-element.component.html', + animations: [fadeIn] +}) +export class SuggestionListElementComponent implements OnInit { + + @Input() object: OpenaireSuggestion; + + @Input() isSelected = false; + + @Input() isCollectionFixed = false; + + public listableObject: any; + + public seeEvidence = false; + + /** + * The component is used to Delete suggestion + */ + @Output() notMineClicked = new EventEmitter(); + + /** + * The component is used to approve & import + */ + @Output() approveAndImport = new EventEmitter(); + + /** + * New value whether the element is selected + */ + @Output() selected = new EventEmitter(); + + /** + * Initialize instance variables + * + * @param {NgbModal} modalService + */ + constructor(private modalService: NgbModal) { } + + ngOnInit() { + this.listableObject = { + indexableObject: Object.assign(new Item(), {id: this.object.id, metadata: this.object.metadata}), + hitHighlights: {} + }; + } + + /** + * Approve and import the suggestion + */ + onApproveAndImport(event: SuggestionApproveAndImport) { + this.approveAndImport.emit(event); + } + + /** + * Delete the suggestion + */ + onNotMine(suggestionId: string) { + this.notMineClicked.emit(suggestionId); + } + + /** + * Change is selected value. + */ + changeSelected(event) { + this.isSelected = event.target.checked; + this.selected.next(this.isSelected); + } + + /** + * See the Evidence + */ + hasEvidences() { + return isNotEmpty(this.object.evidences); + } + + /** + * Set the see evidence variable. + */ + onSeeEvidences(seeEvidence: boolean) { + this.seeEvidence = seeEvidence; + } + +} diff --git a/src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.actions.ts b/src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.actions.ts new file mode 100644 index 00000000000..6c44d40b915 --- /dev/null +++ b/src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.actions.ts @@ -0,0 +1,154 @@ +import { Action } from '@ngrx/store'; +import { type } from '../../../shared/ngrx/type'; +import { OpenaireSuggestionTarget } from '../../../core/openaire/reciter-suggestions/models/openaire-suggestion-target.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 SuggestionTargetActionTypes = { + ADD_TARGETS: type('dspace/integration/openaire/suggestions/target/ADD_TARGETS'), + CLEAR_TARGETS: type('dspace/integration/openaire/suggestions/target/CLEAR_TARGETS'), + RETRIEVE_TARGETS_BY_SOURCE: type('dspace/integration/openaire/suggestions/target/RETRIEVE_TARGETS_BY_SOURCE'), + RETRIEVE_TARGETS_BY_SOURCE_ERROR: type('dspace/integration/openaire/suggestions/target/RETRIEVE_TARGETS_BY_SOURCE_ERROR'), + ADD_USER_SUGGESTIONS: type('dspace/integration/openaire/suggestions/target/ADD_USER_SUGGESTIONS'), + REFRESH_USER_SUGGESTIONS: type('dspace/integration/openaire/suggestions/target/REFRESH_USER_SUGGESTIONS'), + MARK_USER_SUGGESTIONS_AS_VISITED: type('dspace/integration/openaire/suggestions/target/MARK_USER_SUGGESTIONS_AS_VISITED') +}; + +/* tslint:disable:max-classes-per-file */ + +/** + * An ngrx action to retrieve all the Suggestion Targets. + */ +export class RetrieveTargetsBySourceAction implements Action { + type = SuggestionTargetActionTypes.RETRIEVE_TARGETS_BY_SOURCE; + payload: { + source: string; + elementsPerPage: number; + currentPage: number; + }; + + /** + * Create a new RetrieveTargetsBySourceAction. + * + * @param source + * the source for which to retrieve suggestion targets + * @param elementsPerPage + * the number of targets per page + * @param currentPage + * The page number to retrieve + */ + constructor(source: string, elementsPerPage: number, currentPage: number) { + this.payload = { + source, + elementsPerPage, + currentPage + }; + } +} + +/** + * An ngrx action for retrieving 'all Suggestion Targets' error. + */ +export class RetrieveAllTargetsErrorAction implements Action { + type = SuggestionTargetActionTypes.RETRIEVE_TARGETS_BY_SOURCE_ERROR; +} + +/** + * An ngrx action to load the Suggestion Target objects. + */ +export class AddTargetAction implements Action { + type = SuggestionTargetActionTypes.ADD_TARGETS; + payload: { + targets: OpenaireSuggestionTarget[]; + totalPages: number; + currentPage: number; + totalElements: number; + }; + + /** + * Create a new AddTargetAction. + * + * @param targets + * the list of targets + * @param totalPages + * the total available pages of targets + * @param currentPage + * the current page + * @param totalElements + * the total available Suggestion Targets + */ + constructor(targets: OpenaireSuggestionTarget[], totalPages: number, currentPage: number, totalElements: number) { + this.payload = { + targets, + totalPages, + currentPage, + totalElements + }; + } + +} + +/** + * An ngrx action to load the user Suggestion Target object. + * Called by the ??? effect. + */ +export class AddUserSuggestionsAction implements Action { + type = SuggestionTargetActionTypes.ADD_USER_SUGGESTIONS; + payload: { + suggestionTargets: OpenaireSuggestionTarget[]; + }; + + /** + * Create a new AddUserSuggestionsAction. + * + * @param suggestionTargets + * the user suggestions target + */ + constructor(suggestionTargets: OpenaireSuggestionTarget[]) { + this.payload = { suggestionTargets }; + } + +} + +/** + * An ngrx action to reload the user Suggestion Target object. + * Called by the ??? effect. + */ +export class RefreshUserSuggestionsAction implements Action { + type = SuggestionTargetActionTypes.REFRESH_USER_SUGGESTIONS; +} + +/** + * An ngrx action to Mark User Suggestions As Visited. + * Called by the ??? effect. + */ +export class MarkUserSuggestionsAsVisitedAction implements Action { + type = SuggestionTargetActionTypes.MARK_USER_SUGGESTIONS_AS_VISITED; +} + +/** + * An ngrx action to clear targets state. + */ +export class ClearSuggestionTargetsAction implements Action { + type = SuggestionTargetActionTypes.CLEAR_TARGETS; +} + +/* 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 SuggestionTargetsActions + = AddTargetAction + | AddUserSuggestionsAction + | ClearSuggestionTargetsAction + | MarkUserSuggestionsAsVisitedAction + | RetrieveTargetsBySourceAction + | RetrieveAllTargetsErrorAction; diff --git a/src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.component.html b/src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.component.html new file mode 100644 index 00000000000..791e694ba90 --- /dev/null +++ b/src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.component.html @@ -0,0 +1,51 @@ +
+
+
+ + + + + + + + +
+ + + + + + + + + + + + + +
{{'reciter.suggestion.table.name' | translate}}{{'reciter.suggestion.table.actions' | translate}}
+ {{targetElement.display}} + +
+ +
+
+
+
+
+
+
+
diff --git a/src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.component.scss b/src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.component.ts b/src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.component.ts new file mode 100644 index 00000000000..b9ed6c4e87b --- /dev/null +++ b/src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.component.ts @@ -0,0 +1,150 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; + +import { Observable, Subscription } from 'rxjs'; +import { distinctUntilChanged, take } from 'rxjs/operators'; + +import { OpenaireSuggestionTarget } from '../../../core/openaire/reciter-suggestions/models/openaire-suggestion-target.model'; +import { hasValue } from '../../../shared/empty.util'; +import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; +import { SuggestionTargetsStateService } from './suggestion-targets.state.service'; +import { getSuggestionPageRoute } from '../../../suggestions-page/suggestions-page-routing-paths'; +import { SuggestionsService } from '../suggestions.service'; +import { PaginationService } from '../../../core/pagination/pagination.service'; + +/** + * Component to display the Suggestion Target list. + */ +@Component({ + selector: 'ds-suggestion-target', + templateUrl: './suggestion-targets.component.html', + styleUrls: ['./suggestion-targets.component.scss'], +}) +export class SuggestionTargetsComponent implements OnInit { + + /** + * The source for which to list targets + */ + @Input() source: string; + + /** + * The pagination system configuration for HTML listing. + * @type {PaginationComponentOptions} + */ + public paginationConfig: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { + id: 'stp', + pageSizeOptions: [5, 10, 20, 40, 60] + }); + + /** + * The Suggestion Target list. + */ + public targets$: Observable; + /** + * The total number of Suggestion Targets. + */ + public totalElements$: Observable; + /** + * 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 {SuggestionTargetsStateService} suggestionTargetsStateService + * @param {SuggestionsService} suggestionService + * @param {Router} router + */ + constructor( + private paginationService: PaginationService, + private suggestionTargetsStateService: SuggestionTargetsStateService, + private suggestionService: SuggestionsService, + private router: Router + ) { + } + + /** + * Component initialization. + */ + ngOnInit(): void { + this.targets$ = this.suggestionTargetsStateService.getReciterSuggestionTargets(); + this.totalElements$ = this.suggestionTargetsStateService.getReciterSuggestionTargetsTotals(); + } + + /** + * First Suggestion Targets loading after view initialization. + */ + ngAfterViewInit(): void { + this.subs.push( + this.suggestionTargetsStateService.isReciterSuggestionTargetsLoaded().pipe( + take(1) + ).subscribe(() => { + this.getSuggestionTargets(); + }) + ); + } + + /** + * Returns the information about the loading status of the Suggestion Targets (if it's running or not). + * + * @return Observable + * 'true' if the targets are loading, 'false' otherwise. + */ + public isTargetsLoading(): Observable { + return this.suggestionTargetsStateService.isReciterSuggestionTargetsLoading(); + } + + /** + * Returns the information about the processing status of the Suggestion Targets (if it's running or not). + * + * @return Observable + * 'true' if there are operations running on the targets (ex.: a REST call), 'false' otherwise. + */ + public isTargetsProcessing(): Observable { + return this.suggestionTargetsStateService.isReciterSuggestionTargetsProcessing(); + } + + /** + * Redirect to suggestion page. + * + * @param {string} id + * the id of suggestion target + * @param {string} name + * the name of suggestion target + */ + public redirectToSuggestions(id: string, name: string) { + this.router.navigate([getSuggestionPageRoute(id)]); + } + + /** + * Unsubscribe from all subscriptions. + */ + ngOnDestroy(): void { + this.suggestionTargetsStateService.dispatchClearSuggestionTargetsAction(); + this.subs + .filter((sub) => hasValue(sub)) + .forEach((sub) => sub.unsubscribe()); + } + + /** + * Dispatch the Suggestion Targets retrival. + */ + public getSuggestionTargets(): void { + this.paginationService.getCurrentPagination(this.paginationConfig.id, this.paginationConfig).pipe( + distinctUntilChanged(), + take(1) + ).subscribe((options: PaginationComponentOptions) => { + this.suggestionTargetsStateService.dispatchRetrieveReciterSuggestionTargets( + this.source, + options.pageSize, + options.currentPage + ); + }); + } + + public getTargetUuid(target: OpenaireSuggestionTarget) { + return this.suggestionService.getTargetUuid(target); + } +} diff --git a/src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.effects.ts b/src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.effects.ts new file mode 100644 index 00000000000..85e871403c4 --- /dev/null +++ b/src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.effects.ts @@ -0,0 +1,110 @@ +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 } from 'rxjs/operators'; +import { of } from 'rxjs'; + +import { + AddTargetAction, + AddUserSuggestionsAction, + RefreshUserSuggestionsAction, + RetrieveAllTargetsErrorAction, + RetrieveTargetsBySourceAction, + SuggestionTargetActionTypes, +} from './suggestion-targets.actions'; +import { PaginatedList } from '../../../core/data/paginated-list.model'; +import { SuggestionsService } from '../suggestions.service'; +import { NotificationsService } from '../../../shared/notifications/notifications.service'; +import { AuthActionTypes, RetrieveAuthenticatedEpersonSuccessAction } from '../../../core/auth/auth.actions'; +import { OpenaireSuggestionTarget } from '../../../core/openaire/reciter-suggestions/models/openaire-suggestion-target.model'; +import { EPerson } from '../../../core/eperson/models/eperson.model'; + +/** + * Provides effect methods for the Suggestion Targets actions. + */ +@Injectable() +export class SuggestionTargetsEffects { + + /** + * Retrieve all Suggestion Targets managing pagination and errors. + */ + @Effect() retrieveTargetsBySource$ = this.actions$.pipe( + ofType(SuggestionTargetActionTypes.RETRIEVE_TARGETS_BY_SOURCE), + switchMap((action: RetrieveTargetsBySourceAction) => { + return this.suggestionsService.getTargets( + action.payload.source, + action.payload.elementsPerPage, + action.payload.currentPage + ).pipe( + map((targets: PaginatedList) => + new AddTargetAction(targets.page, targets.totalPages, targets.currentPage, targets.totalElements) + ), + catchError((error: Error) => { + if (error) { + console.error(error.message); + } + return of(new RetrieveAllTargetsErrorAction()); + }) + ); + }) + ); + + /** + * Show a notification on error. + */ + @Effect({ dispatch: false }) retrieveAllTargetsErrorAction$ = this.actions$.pipe( + ofType(SuggestionTargetActionTypes.RETRIEVE_TARGETS_BY_SOURCE_ERROR), + tap(() => { + this.notificationsService.error(null, this.translate.get('reciter.suggestion.target.error.service.retrieve')); + }) + ); + + /** + * Show a notification on error. + */ + @Effect() retrieveUserTargets$ = this.actions$.pipe( + ofType(AuthActionTypes.RETRIEVE_AUTHENTICATED_EPERSON_SUCCESS), + switchMap((action: RetrieveAuthenticatedEpersonSuccessAction) => { + return this.suggestionsService.retrieveCurrentUserSuggestions(action.payload).pipe( + map((suggestionTargets: OpenaireSuggestionTarget[]) => new AddUserSuggestionsAction(suggestionTargets)) + ); + })); + + /** + * Fetch the current user suggestion + */ + @Effect() refreshUserTargets$ = this.actions$.pipe( + ofType(SuggestionTargetActionTypes.REFRESH_USER_SUGGESTIONS), + switchMap((action: RefreshUserSuggestionsAction) => { + return this.store$.select((state: any) => state.core.auth.user) + .pipe( + switchMap((user: EPerson) => { + return this.suggestionsService.retrieveCurrentUserSuggestions(user) + .pipe( + map((suggestionTargets: OpenaireSuggestionTarget[]) => new AddUserSuggestionsAction(suggestionTargets)), + catchError((errors) => of(errors)) + ); + }), + catchError((errors) => of(errors)) + ); + })); + + /** + * Initialize the effect class variables. + * @param {Actions} actions$ + * @param {Store} store$ + * @param {TranslateService} translate + * @param {NotificationsService} notificationsService + * @param {SuggestionsService} suggestionsService + */ + constructor( + private actions$: Actions, + private store$: Store, + private translate: TranslateService, + private notificationsService: NotificationsService, + private suggestionsService: SuggestionsService + ) { + } +} diff --git a/src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.reducer.ts b/src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.reducer.ts new file mode 100644 index 00000000000..f8bd53ec050 --- /dev/null +++ b/src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.reducer.ts @@ -0,0 +1,100 @@ +import { SuggestionTargetActionTypes, SuggestionTargetsActions } from './suggestion-targets.actions'; +import { OpenaireSuggestionTarget } from '../../../core/openaire/reciter-suggestions/models/openaire-suggestion-target.model'; + +/** + * The interface representing the OpenAIRE suggestion targets state. + */ +export interface SuggestionTargetState { + targets: OpenaireSuggestionTarget[]; + processing: boolean; + loaded: boolean; + totalPages: number; + currentPage: number; + totalElements: number; + currentUserTargets: OpenaireSuggestionTarget[]; + currentUserTargetsVisited: boolean; +} + +/** + * Used for the OpenAIRE Suggestion Target state initialization. + */ +const SuggestionTargetInitialState: SuggestionTargetState = { + targets: [], + processing: false, + loaded: false, + totalPages: 0, + currentPage: 0, + totalElements: 0, + currentUserTargets: null, + currentUserTargetsVisited: false +}; + +/** + * The OpenAIRE Broker Topic Reducer + * + * @param state + * the current state initialized with SuggestionTargetInitialState + * @param action + * the action to perform on the state + * @return SuggestionTargetState + * the new state + */ +export function SuggestionTargetsReducer(state = SuggestionTargetInitialState, action: SuggestionTargetsActions): SuggestionTargetState { + switch (action.type) { + case SuggestionTargetActionTypes.RETRIEVE_TARGETS_BY_SOURCE: { + return Object.assign({}, state, { + targets: [], + processing: true + }); + } + + case SuggestionTargetActionTypes.ADD_TARGETS: { + return Object.assign({}, state, { + targets: state.targets.concat(action.payload.targets), + processing: false, + loaded: true, + totalPages: action.payload.totalPages, + currentPage: state.currentPage, + totalElements: action.payload.totalElements + }); + } + + case SuggestionTargetActionTypes.RETRIEVE_TARGETS_BY_SOURCE_ERROR: { + return Object.assign({}, state, { + targets: [], + processing: false, + loaded: true, + totalPages: 0, + currentPage: 0, + totalElements: 0, + }); + } + + case SuggestionTargetActionTypes.ADD_USER_SUGGESTIONS: { + return Object.assign({}, state, { + currentUserTargets: action.payload.suggestionTargets + }); + } + + case SuggestionTargetActionTypes.MARK_USER_SUGGESTIONS_AS_VISITED: { + return Object.assign({}, state, { + currentUserTargetsVisited: true + }); + } + + case SuggestionTargetActionTypes.CLEAR_TARGETS: { + return Object.assign({}, state, { + targets: [], + processing: false, + loaded: false, + totalPages: 0, + currentPage: 0, + totalElements: 0, + }); + } + + default: { + return state; + } + } +} diff --git a/src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.state.service.ts b/src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.state.service.ts new file mode 100644 index 00000000000..2e05bce0a9b --- /dev/null +++ b/src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.state.service.ts @@ -0,0 +1,164 @@ +import { Injectable } from '@angular/core'; + +import { select, Store } from '@ngrx/store'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; + +import { + getCurrentUserSuggestionTargetsSelector, + getCurrentUserSuggestionTargetsVisitedSelector, + getreciterSuggestionTargetCurrentPageSelector, + getreciterSuggestionTargetTotalsSelector, + isReciterSuggestionTargetLoadedSelector, + isreciterSuggestionTargetProcessingSelector, + reciterSuggestionTargetObjectSelector +} from '../selectors'; +import { OpenaireSuggestionTarget } from '../../../core/openaire/reciter-suggestions/models/openaire-suggestion-target.model'; +import { + ClearSuggestionTargetsAction, + MarkUserSuggestionsAsVisitedAction, + RefreshUserSuggestionsAction, + RetrieveTargetsBySourceAction +} from './suggestion-targets.actions'; +import { OpenaireState } from '../../openaire.reducer'; + +/** + * The service handling the Suggestion targets State. + */ +@Injectable() +export class SuggestionTargetsStateService { + + /** + * Initialize the service variables. + * @param {Store} store + */ + constructor(private store: Store) { } + + /** + * Returns the list of Reciter Suggestion Targets from the state. + * + * @return Observable + * The list of Reciter Suggestion Targets. + */ + public getReciterSuggestionTargets(): Observable { + return this.store.pipe(select(reciterSuggestionTargetObjectSelector())); + } + + /** + * Returns the information about the loading status of the Reciter Suggestion Targets (if it's running or not). + * + * @return Observable + * 'true' if the targets are loading, 'false' otherwise. + */ + public isReciterSuggestionTargetsLoading(): Observable { + return this.store.pipe( + select(isReciterSuggestionTargetLoadedSelector), + map((loaded: boolean) => !loaded) + ); + } + + /** + * Returns the information about the loading status of the Reciter Suggestion Targets (whether or not they were loaded). + * + * @return Observable + * 'true' if the targets are loaded, 'false' otherwise. + */ + public isReciterSuggestionTargetsLoaded(): Observable { + return this.store.pipe(select(isReciterSuggestionTargetLoadedSelector)); + } + + /** + * Returns the information about the processing status of the Reciter Suggestion Targets (if it's running or not). + * + * @return Observable + * 'true' if there are operations running on the targets (ex.: a REST call), 'false' otherwise. + */ + public isReciterSuggestionTargetsProcessing(): Observable { + return this.store.pipe(select(isreciterSuggestionTargetProcessingSelector)); + } + + /** + * Returns, from the state, the total available pages of the Reciter Suggestion Targets. + * + * @return Observable + * The number of the Reciter Suggestion Targets pages. + */ + public getReciterSuggestionTargetsTotalPages(): Observable { + return this.store.pipe(select(getreciterSuggestionTargetTotalsSelector)); + } + + /** + * Returns the current page of the Reciter Suggestion Targets, from the state. + * + * @return Observable + * The number of the current Reciter Suggestion Targets page. + */ + public getReciterSuggestionTargetsCurrentPage(): Observable { + return this.store.pipe(select(getreciterSuggestionTargetCurrentPageSelector)); + } + + /** + * Returns the total number of the Reciter Suggestion Targets. + * + * @return Observable + * The number of the Reciter Suggestion Targets. + */ + public getReciterSuggestionTargetsTotals(): Observable { + return this.store.pipe(select(getreciterSuggestionTargetTotalsSelector)); + } + + /** + * Dispatch a request to change the Reciter Suggestion Targets state, retrieving the targets from the server. + * + * @param source + * the source for which to retrieve suggestion targets + * @param elementsPerPage + * The number of the targets per page. + * @param currentPage + * The number of the current page. + */ + public dispatchRetrieveReciterSuggestionTargets(source: string, elementsPerPage: number, currentPage: number): void { + this.store.dispatch(new RetrieveTargetsBySourceAction(source, elementsPerPage, currentPage)); + } + + /** + * Returns, from the state, the reciter suggestion targets for the current user. + * + * @return Observable + * The Reciter Suggestion Targets object. + */ + public getCurrentUserSuggestionTargets(): Observable { + return this.store.pipe(select(getCurrentUserSuggestionTargetsSelector)); + } + + /** + * Returns, from the state, whether or not the user has consulted their suggestion targets. + * + * @return Observable + * True if user already visited, false otherwise. + */ + public hasUserVisitedSuggestions(): Observable { + return this.store.pipe(select(getCurrentUserSuggestionTargetsVisitedSelector)); + } + + /** + * Dispatch a new MarkUserSuggestionsAsVisitedAction + */ + public dispatchMarkUserSuggestionsAsVisitedAction(): void { + this.store.dispatch(new MarkUserSuggestionsAsVisitedAction()); + } + + /** + * Dispatch an action to clear the Reciter Suggestion Targets state. + */ + public dispatchClearSuggestionTargetsAction(): void { + this.store.dispatch(new ClearSuggestionTargetsAction()); + } + + /** + * Dispatch an action to refresh the user suggestions. + */ + public dispatchRefreshUserSuggestionsAction(): void { + this.store.dispatch(new RefreshUserSuggestionsAction()); + } +} diff --git a/src/app/openaire/reciter-suggestions/suggestions-notification/suggestions-notification.component.html b/src/app/openaire/reciter-suggestions/suggestions-notification/suggestions-notification.component.html new file mode 100644 index 00000000000..577aa496b38 --- /dev/null +++ b/src/app/openaire/reciter-suggestions/suggestions-notification/suggestions-notification.component.html @@ -0,0 +1,8 @@ + + +
+
+
+
+
+
diff --git a/src/app/openaire/reciter-suggestions/suggestions-notification/suggestions-notification.component.scss b/src/app/openaire/reciter-suggestions/suggestions-notification/suggestions-notification.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/app/openaire/reciter-suggestions/suggestions-notification/suggestions-notification.component.ts b/src/app/openaire/reciter-suggestions/suggestions-notification/suggestions-notification.component.ts new file mode 100644 index 00000000000..094dfab0174 --- /dev/null +++ b/src/app/openaire/reciter-suggestions/suggestions-notification/suggestions-notification.component.ts @@ -0,0 +1,42 @@ +import { Component, OnInit } from '@angular/core'; +import { OpenaireSuggestionTarget } from '../../../core/openaire/reciter-suggestions/models/openaire-suggestion-target.model'; +import { TranslateService } from '@ngx-translate/core'; +import { SuggestionTargetsStateService } from '../suggestion-targets/suggestion-targets.state.service'; +import { NotificationsService } from '../../../shared/notifications/notifications.service'; +import { SuggestionsService } from '../suggestions.service'; +import { Observable } from 'rxjs'; + +@Component({ + selector: 'ds-suggestions-notification', + templateUrl: './suggestions-notification.component.html', + styleUrls: ['./suggestions-notification.component.scss'] +}) +export class SuggestionsNotificationComponent implements OnInit { + + labelPrefix = 'mydspace.'; + + /** + * The user suggestion targets. + */ + suggestionsRD$: Observable; + + constructor( + private translateService: TranslateService, + private reciterSuggestionStateService: SuggestionTargetsStateService, + private notificationsService: NotificationsService, + private suggestionsService: SuggestionsService + ) { } + + ngOnInit() { + this.suggestionsRD$ = this.reciterSuggestionStateService.getCurrentUserSuggestionTargets(); + } + + /** + * Interpolated params to build the notification suggestions notification. + * @param suggestionTarget + */ + public getNotificationSuggestionInterpolation(suggestionTarget: OpenaireSuggestionTarget): any { + return this.suggestionsService.getNotificationSuggestionInterpolation(suggestionTarget); + } + +} diff --git a/src/app/openaire/reciter-suggestions/suggestions-popup/suggestions-popup.component.html b/src/app/openaire/reciter-suggestions/suggestions-popup/suggestions-popup.component.html new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/src/app/openaire/reciter-suggestions/suggestions-popup/suggestions-popup.component.html @@ -0,0 +1 @@ + diff --git a/src/app/openaire/reciter-suggestions/suggestions-popup/suggestions-popup.component.scss b/src/app/openaire/reciter-suggestions/suggestions-popup/suggestions-popup.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/app/openaire/reciter-suggestions/suggestions-popup/suggestions-popup.component.spec.ts b/src/app/openaire/reciter-suggestions/suggestions-popup/suggestions-popup.component.spec.ts new file mode 100644 index 00000000000..67678354caf --- /dev/null +++ b/src/app/openaire/reciter-suggestions/suggestions-popup/suggestions-popup.component.spec.ts @@ -0,0 +1,79 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SuggestionsPopupComponent } from './suggestions-popup.component'; +import { TranslateModule } from '@ngx-translate/core'; +import { SuggestionTargetsStateService } from '../suggestion-targets/suggestion-targets.state.service'; +import { NotificationsService } from '../../../shared/notifications/notifications.service'; +import { NotificationsServiceStub } from '../../../shared/testing/notifications-service.stub'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { of as observableOf } from 'rxjs'; +import { mockSuggestionTargetsObjectOne } from '../../../shared/mocks/reciter-suggestion-targets.mock'; +import { SuggestionsService } from '../suggestions.service'; + +describe('SuggestionsPopupComponent', () => { + let component: SuggestionsPopupComponent; + let fixture: ComponentFixture; + + const suggestionStateService = jasmine.createSpyObj('SuggestionTargetsStateService', { + hasUserVisitedSuggestions: jasmine.createSpy('hasUserVisitedSuggestions'), + getCurrentUserSuggestionTargets: jasmine.createSpy('getCurrentUserSuggestionTargets'), + dispatchMarkUserSuggestionsAsVisitedAction: jasmine.createSpy('dispatchMarkUserSuggestionsAsVisitedAction') + }); + + const mockNotificationInterpolation = { count: 12, source: 'source', suggestionId: 'id', displayName: 'displayName' }; + const suggestionService = jasmine.createSpyObj('SuggestionService', { + getNotificationSuggestionInterpolation: + jasmine.createSpy('getNotificationSuggestionInterpolation').and.returnValue(mockNotificationInterpolation) + }); + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot()], + declarations: [ SuggestionsPopupComponent ], + providers: [ + { provide: SuggestionTargetsStateService, useValue: suggestionStateService }, + { provide: SuggestionsService, useValue: suggestionService }, + { provide: NotificationsService, useValue: new NotificationsServiceStub() }, + ], + schemas: [NO_ERRORS_SCHEMA] + + }) + .compileComponents(); + })); + + describe('should create', () => { + + beforeEach(() => { + fixture = TestBed.createComponent(SuggestionsPopupComponent); + component = fixture.componentInstance; + spyOn(component, 'initializePopup').and.returnValue(null); + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + expect(component.initializePopup).toHaveBeenCalled(); + }); + + }); + + describe('when there are publication suggestions', () => { + + beforeEach(() => { + + suggestionStateService.hasUserVisitedSuggestions.and.returnValue(observableOf(false)); + suggestionStateService.getCurrentUserSuggestionTargets.and.returnValue(observableOf([mockSuggestionTargetsObjectOne])); + suggestionStateService.dispatchMarkUserSuggestionsAsVisitedAction.and.returnValue(observableOf(null)); + + fixture = TestBed.createComponent(SuggestionsPopupComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should show a notification when new publication suggestions are available', () => { + expect((component as any).notificationsService.success).toHaveBeenCalled(); + expect(suggestionStateService.dispatchMarkUserSuggestionsAsVisitedAction).toHaveBeenCalled(); + }); + + }); +}); diff --git a/src/app/openaire/reciter-suggestions/suggestions-popup/suggestions-popup.component.ts b/src/app/openaire/reciter-suggestions/suggestions-popup/suggestions-popup.component.ts new file mode 100644 index 00000000000..6135cd99eab --- /dev/null +++ b/src/app/openaire/reciter-suggestions/suggestions-popup/suggestions-popup.component.ts @@ -0,0 +1,67 @@ +import { Component, OnDestroy, OnInit } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { SuggestionTargetsStateService } from '../suggestion-targets/suggestion-targets.state.service'; +import { NotificationsService } from '../../../shared/notifications/notifications.service'; +import { SuggestionsService } from '../suggestions.service'; +import { takeUntil } from 'rxjs/operators'; +import { OpenaireSuggestionTarget } from '../../../core/openaire/reciter-suggestions/models/openaire-suggestion-target.model'; +import { isNotEmpty } from '../../../shared/empty.util'; +import { combineLatest, Subject } from 'rxjs'; + +@Component({ + selector: 'ds-suggestions-popup', + templateUrl: './suggestions-popup.component.html', + styleUrls: ['./suggestions-popup.component.scss'] +}) +export class SuggestionsPopupComponent implements OnInit, OnDestroy { + + labelPrefix = 'mydspace.'; + + subscription; + + constructor( + private translateService: TranslateService, + private reciterSuggestionStateService: SuggestionTargetsStateService, + private notificationsService: NotificationsService, + private suggestionsService: SuggestionsService + ) { } + + ngOnInit() { + this.initializePopup(); + } + + public initializePopup() { + const notifier = new Subject(); + this.subscription = combineLatest([ + this.reciterSuggestionStateService.getCurrentUserSuggestionTargets(), + this.reciterSuggestionStateService.hasUserVisitedSuggestions() + ]).pipe(takeUntil(notifier)).subscribe(([suggestions, visited]) => { + if (isNotEmpty(suggestions)) { + if (!visited) { + suggestions.forEach((suggestionTarget: OpenaireSuggestionTarget) => this.showNotificationForNewSuggestions(suggestionTarget)); + this.reciterSuggestionStateService.dispatchMarkUserSuggestionsAsVisitedAction(); + notifier.next(); + notifier.complete(); + } + } + }); + } + + /** + * Show a notification to user for a new suggestions detected + * @param suggestionTarget + * @private + */ + private showNotificationForNewSuggestions(suggestionTarget: OpenaireSuggestionTarget): void { + const content = this.translateService.instant(this.labelPrefix + 'notification.suggestion', + this.suggestionsService.getNotificationSuggestionInterpolation(suggestionTarget)); + this.notificationsService.success('', content, {timeOut:0}, true); + } + + ngOnDestroy() { + if (this.subscription) { + this.subscription.unsubscribe(); + } + } + +} diff --git a/src/app/openaire/reciter-suggestions/suggestions.service.ts b/src/app/openaire/reciter-suggestions/suggestions.service.ts new file mode 100644 index 00000000000..de743d95e34 --- /dev/null +++ b/src/app/openaire/reciter-suggestions/suggestions.service.ts @@ -0,0 +1,296 @@ +import { Injectable } from '@angular/core'; + +import { of, forkJoin, Observable } from 'rxjs'; +import { catchError, map, mergeMap, take } from 'rxjs/operators'; + +import { OpenaireSuggestionsDataService } from '../../core/openaire/reciter-suggestions/openaire-suggestions-data.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 { OpenaireSuggestionTarget } from '../../core/openaire/reciter-suggestions/models/openaire-suggestion-target.model'; +import { ResearcherProfileService } from '../../core/profile/researcher-profile.service'; +import { AuthService } from '../../core/auth/auth.service'; +import { EPerson } from '../../core/eperson/models/eperson.model'; +import { hasValue, isNotEmpty } from '../../shared/empty.util'; +import { ResearcherProfile } from '../../core/profile/model/researcher-profile.model'; +import { + getAllSucceededRemoteDataPayload, + getFinishedRemoteData, + getFirstSucceededRemoteDataPayload, + getFirstSucceededRemoteListPayload +} from '../../core/shared/operators'; +import { OpenaireSuggestion } from '../../core/openaire/reciter-suggestions/models/openaire-suggestion.model'; +import { WorkspaceitemDataService } from '../../core/submission/workspaceitem-data.service'; +import { TranslateService } from '@ngx-translate/core'; +import { NoContent } from '../../core/shared/NoContent.model'; +import { environment } from '../../../environments/environment'; +import { SuggestionConfig } from '../../../config/layout-config.interfaces'; +import { WorkspaceItem } from '../../core/submission/models/workspaceitem.model'; + +export interface SuggestionBulkResult { + success: number; + fails: number; +} + +/** + * The service handling all Suggestion Target requests to the REST service. + */ +@Injectable() +export class SuggestionsService { + + /** + * Initialize the service variables. + * @param {AuthService} authService + * @param {ResearcherProfileService} researcherProfileService + * @param {OpenaireSuggestionsDataService} suggestionsDataService + */ + constructor( + private authService: AuthService, + private researcherProfileService: ResearcherProfileService, + private suggestionsDataService: OpenaireSuggestionsDataService, + private translateService: TranslateService + ) { + } + + /** + * Return the list of Suggestion Target managing pagination and errors. + * + * @param source + * The source for which to retrieve targets + * @param elementsPerPage + * The number of the target per page + * @param currentPage + * The page number to retrieve + * @return Observable> + * The list of Suggestion Targets. + */ + public getTargets(source, elementsPerPage, currentPage): Observable> { + const sortOptions = new SortOptions('display', SortDirection.ASC); + + const findListOptions: FindListOptions = { + elementsPerPage: elementsPerPage, + currentPage: currentPage, + sort: sortOptions + }; + + return this.suggestionsDataService.getTargets(source, findListOptions).pipe( + getFinishedRemoteData(), + take(1), + map((rd: RemoteData>) => { + if (rd.hasSucceeded) { + return rd.payload; + } else { + throw new Error('Can\'t retrieve Suggestion Target from the Search Target REST service'); + } + }) + ); + } + + /** + * Return the list of review suggestions Target managing pagination and errors. + * + * @param targetId + * The target id for which to find suggestions. + * @param elementsPerPage + * The number of the target per page + * @param currentPage + * The page number to retrieve + * @param sortOptions + * The sort options + * @return Observable>> + * The list of Suggestion. + */ + public getSuggestions(targetId: string, elementsPerPage, currentPage, sortOptions: SortOptions): Observable> { + const [source, target] = targetId.split(':'); + + const findListOptions: FindListOptions = { + elementsPerPage: elementsPerPage, + currentPage: currentPage, + sort: sortOptions + }; + + return this.suggestionsDataService.getSuggestionsByTargetAndSource(target, source, findListOptions).pipe( + getAllSucceededRemoteDataPayload() + ); + } + + /** + * Clear suggestions requests from cache + */ + public clearSuggestionRequests() { + this.suggestionsDataService.clearSuggestionRequests(); + } + + /** + * Used to delete Suggestion + * @suggestionId + */ + public deleteReviewedSuggestion(suggestionId: string): Observable> { + return this.suggestionsDataService.deleteSuggestion(suggestionId).pipe( + map((response: RemoteData) => { + if (response.isSuccess) { + return response; + } else { + throw new Error('Can\'t delete Suggestion from the Search Target REST service'); + } + }), + take(1) + ); + } + + /** + * Retrieve suggestion targets for the given user + * + * @param user + * The EPerson object for which to retrieve suggestion targets + */ + public retrieveCurrentUserSuggestions(user: EPerson): Observable { + return this.researcherProfileService.findById(user.uuid).pipe( + mergeMap((profile: ResearcherProfile) => { + if (isNotEmpty(profile)) { + return this.researcherProfileService.findRelatedItemId(profile).pipe( + mergeMap((itemId: string) => { + return this.suggestionsDataService.getTargetsByUser(itemId).pipe( + getFirstSucceededRemoteListPayload() + ); + }) + ); + } else { + return of([]); + } + }), + take(1) + ); + } + + /** + * Perform the approve and import operation over a single suggestion + * @param suggestion target suggestion + * @param collectionId the collectionId + * @param workspaceitemService injected dependency + * @private + */ + public approveAndImport(workspaceitemService: WorkspaceitemDataService, + suggestion: OpenaireSuggestion, + collectionId: string): Observable { + + const resolvedCollectionId = this.resolveCollectionId(suggestion, collectionId); + return workspaceitemService.importExternalSourceEntry(suggestion.externalSourceUri, resolvedCollectionId) + .pipe( + getFirstSucceededRemoteDataPayload(), + catchError((error) => of(null)) + ); + } + + /** + * Perform the delete operation over a single suggestion. + * @param suggestionId + */ + public notMine(suggestionId): Observable> { + return this.deleteReviewedSuggestion(suggestionId).pipe( + catchError((error) => of(null)) + ); + } + + /** + * Perform a bulk approve and import operation. + * @param workspaceitemService injected dependency + * @param suggestions the array containing the suggestions + * @param collectionId the collectionId + */ + public approveAndImportMultiple(workspaceitemService: WorkspaceitemDataService, + suggestions: OpenaireSuggestion[], + collectionId: string): Observable { + + return forkJoin(suggestions.map((suggestion: OpenaireSuggestion) => + this.approveAndImport(workspaceitemService, suggestion, collectionId))) + .pipe(map((results: WorkspaceItem[]) => { + return { + success: results.filter((result) => result != null).length, + fails: results.filter((result) => result == null).length + }; + }), take(1)); + } + + /** + * Perform a bulk notMine operation. + * @param suggestions the array containing the suggestions + */ + public notMineMultiple(suggestions: OpenaireSuggestion[]): Observable { + return forkJoin(suggestions.map((suggestion: OpenaireSuggestion) => this.notMine(suggestion.id))) + .pipe(map((results: RemoteData[]) => { + return { + success: results.filter((result) => result != null).length, + fails: results.filter((result) => result == null).length + }; + }), take(1)); + } + + /** + * Get the researcher uuid (for navigation purpose) from a target instance. + * TODO Find a better way + * @param target + * @return the researchUuid + */ + public getTargetUuid(target: OpenaireSuggestionTarget): string { + const tokens = target.id.split(':'); + return tokens.length === 2 ? tokens[1] : null; + } + + /** + * Interpolated params to build the notification suggestions notification. + * @param suggestionTarget + */ + public getNotificationSuggestionInterpolation(suggestionTarget: OpenaireSuggestionTarget): any { + return { + count: suggestionTarget.total, + source: this.translateService.instant(this.translateSuggestionSource(suggestionTarget.source)), + type: this.translateService.instant(this.translateSuggestionType(suggestionTarget.source)), + suggestionId: suggestionTarget.id, + displayName: suggestionTarget.display + }; + } + + public translateSuggestionType(source: string): string { + return 'reciter.suggestion.type.' + source; + } + + public translateSuggestionSource(source: string): string { + return 'reciter.suggestion.source.' + source; + } + + /** + * If the provided collectionId ha no value, tries to resolve it by suggestion source. + * @param suggestion + * @param collectionId + */ + public resolveCollectionId(suggestion: OpenaireSuggestion, collectionId): string { + if (hasValue(collectionId)) { + return collectionId; + } + return environment.suggestion + .find((suggestionConf: SuggestionConfig) => suggestionConf.source === suggestion.source) + .collectionId; + } + + /** + * Return true if all the suggestion are configured with the same fixed collection + * in the configuration. + * @param suggestions + */ + public isCollectionFixed(suggestions: OpenaireSuggestion[]): boolean { + return this.getFixedCollectionIds(suggestions).length === 1; + } + + private getFixedCollectionIds(suggestions: OpenaireSuggestion[]): string[] { + const collectionIds = {}; + suggestions.forEach((suggestion: OpenaireSuggestion) => { + const conf = environment.suggestion.find((suggestionConf: SuggestionConfig) => suggestionConf.source === suggestion.source); + if (hasValue(conf)) { + collectionIds[conf.collectionId] = true; + } + }); + return Object.keys(collectionIds); + } +} diff --git a/src/app/shared/mocks/openaire.mock.ts b/src/app/shared/mocks/openaire.mock.ts new file mode 100644 index 00000000000..95ea8717274 --- /dev/null +++ b/src/app/shared/mocks/openaire.mock.ts @@ -0,0 +1,1797 @@ +import { of as observableOf } from 'rxjs'; +import { ResourceType } from '../../core/shared/resource-type'; +import { OpenaireBrokerTopicObject } from '../../core/openaire/broker/models/openaire-broker-topic.model'; +import { OpenaireBrokerEventObject } from '../../core/openaire/broker/models/openaire-broker-event.model'; +import { OpenaireBrokerTopicRestService } from '../../core/openaire/broker/topics/openaire-broker-topic-rest.service'; +import { OpenaireBrokerEventRestService } from '../../core/openaire/broker/events/openaire-broker-event-rest.service'; +import { DSpaceObject } from '../../core/shared/dspace-object.model'; +import { OpenaireStateService } from '../../openaire/openaire-state.service'; +import { Item } from '../../core/shared/item.model'; +import { + createNoContentRemoteDataObject$, + createSuccessfulRemoteDataObject, + createSuccessfulRemoteDataObject$ +} from '../remote-data.utils'; +import { SearchResult } from '../search/models/search-result.model'; +import { SuggestionsService } from '../../openaire/reciter-suggestions/suggestions.service'; + +// REST Mock --------------------------------------------------------------------- +// ------------------------------------------------------------------------------- + +// Items +// ------------------------------------------------------------------------------- + +const ItemMockPid1: Item = Object.assign( + new Item(), + { + handle: '10077/21486', + lastModified: '2017-04-24T19:44:08.178+0000', + isArchived: true, + isDiscoverable: true, + isWithdrawn: false, + _links:{ + self: { + href: 'https://rest.api/rest/api/core/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357' + } + }, + id: 'ITEM4567-e89b-12d3-a456-426614174001', + uuid: 'ITEM4567-e89b-12d3-a456-426614174001', + type: 'item', + metadata: { + 'dc.creator': [ + { + language: 'en_US', + value: 'Doe, Jane' + } + ], + 'dc.date.accessioned': [ + { + language: null, + value: '1650-06-26T19:58:25Z' + } + ], + 'dc.date.available': [ + { + language: null, + value: '1650-06-26T19:58:25Z' + } + ], + 'dc.date.issued': [ + { + language: null, + value: '1650-06-26' + } + ], + 'dc.identifier.issn': [ + { + language: 'en_US', + value: '123456789' + } + ], + 'dc.identifier.uri': [ + { + language: null, + value: 'http://dspace7.4science.it/xmlui/handle/10673/6' + } + ], + 'dc.description.abstract': [ + { + language: 'en_US', + value: 'This is really just a sample abstract. If it was a real abstract it would contain useful information about this test document. Sorry though, nothing useful in this paragraph. You probably shouldn\'t have even bothered to read it!' + } + ], + 'dc.description.provenance': [ + { + language: 'en', + value: 'Made available in DSpace on 2012-06-26T19:58:25Z (GMT). No. of bitstreams: 2\r\ntest_ppt.ppt: 12707328 bytes, checksum: a353fc7d29b3c558c986f7463a41efd3 (MD5)\r\ntest_ppt.pptx: 12468572 bytes, checksum: 599305edb4ebee329667f2c35b14d1d6 (MD5)' + }, + { + language: 'en', + value: 'Restored into DSpace on 2013-06-13T09:17:34Z (GMT).' + }, + { + language: 'en', + value: 'Restored into DSpace on 2013-06-13T11:04:16Z (GMT).' + }, + { + language: 'en', + value: 'Restored into DSpace on 2017-04-24T19:44:08Z (GMT).' + } + ], + 'dc.language': [ + { + language: 'en_US', + value: 'en' + } + ], + 'dc.rights': [ + { + language: 'en_US', + value: '© Jane Doe' + } + ], + 'dc.subject': [ + { + language: 'en_US', + value: 'keyword1' + }, + { + language: 'en_US', + value: 'keyword2' + }, + { + language: 'en_US', + value: 'keyword3' + } + ], + 'dc.title': [ + { + language: 'en_US', + value: 'Index nominum et rerum' + } + ], + 'dc.type': [ + { + language: 'en_US', + value: 'text' + } + ] + } + } +); + +const ItemMockPid2: Item = Object.assign( + new Item(), + { + handle: '10077/21486', + lastModified: '2017-04-24T19:44:08.178+0000', + isArchived: true, + isDiscoverable: true, + isWithdrawn: false, + _links:{ + self: { + href: 'https://rest.api/rest/api/core/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357' + } + }, + id: 'ITEM4567-e89b-12d3-a456-426614174004', + uuid: 'ITEM4567-e89b-12d3-a456-426614174004', + type: 'item', + metadata: { + 'dc.creator': [ + { + language: 'en_US', + value: 'Doe, Jane' + } + ], + 'dc.date.accessioned': [ + { + language: null, + value: '1650-06-26T19:58:25Z' + } + ], + 'dc.date.available': [ + { + language: null, + value: '1650-06-26T19:58:25Z' + } + ], + 'dc.date.issued': [ + { + language: null, + value: '1650-06-26' + } + ], + 'dc.identifier.issn': [ + { + language: 'en_US', + value: '123456789' + } + ], + 'dc.identifier.uri': [ + { + language: null, + value: 'http://dspace7.4science.it/xmlui/handle/10673/6' + } + ], + 'dc.description.abstract': [ + { + language: 'en_US', + value: 'This is really just a sample abstract. If it was a real abstract it would contain useful information about this test document. Sorry though, nothing useful in this paragraph. You probably shouldn\'t have even bothered to read it!' + } + ], + 'dc.description.provenance': [ + { + language: 'en', + value: 'Made available in DSpace on 2012-06-26T19:58:25Z (GMT). No. of bitstreams: 2\r\ntest_ppt.ppt: 12707328 bytes, checksum: a353fc7d29b3c558c986f7463a41efd3 (MD5)\r\ntest_ppt.pptx: 12468572 bytes, checksum: 599305edb4ebee329667f2c35b14d1d6 (MD5)' + }, + { + language: 'en', + value: 'Restored into DSpace on 2013-06-13T09:17:34Z (GMT).' + }, + { + language: 'en', + value: 'Restored into DSpace on 2013-06-13T11:04:16Z (GMT).' + }, + { + language: 'en', + value: 'Restored into DSpace on 2017-04-24T19:44:08Z (GMT).' + } + ], + 'dc.language': [ + { + language: 'en_US', + value: 'en' + } + ], + 'dc.rights': [ + { + language: 'en_US', + value: '© Jane Doe' + } + ], + 'dc.subject': [ + { + language: 'en_US', + value: 'keyword1' + }, + { + language: 'en_US', + value: 'keyword2' + }, + { + language: 'en_US', + value: 'keyword3' + } + ], + 'dc.title': [ + { + language: 'en_US', + value: 'UNA NUOVA RILETTURA DELL\u0027 ARISTOTELE DI FRANZ BRENTANO ALLA LUCE DI ALCUNI INEDITI' + } + ], + 'dc.type': [ + { + language: 'en_US', + value: 'text' + } + ] + } + } +); + +const ItemMockPid3: Item = Object.assign( + new Item(), + { + handle: '10077/21486', + lastModified: '2017-04-24T19:44:08.178+0000', + isArchived: true, + isDiscoverable: true, + isWithdrawn: false, + _links:{ + self: { + href: 'https://rest.api/rest/api/core/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357' + } + }, + id: 'ITEM4567-e89b-12d3-a456-426614174005', + uuid: 'ITEM4567-e89b-12d3-a456-426614174005', + type: 'item', + metadata: { + 'dc.creator': [ + { + language: 'en_US', + value: 'Doe, Jane' + } + ], + 'dc.date.accessioned': [ + { + language: null, + value: '1650-06-26T19:58:25Z' + } + ], + 'dc.date.available': [ + { + language: null, + value: '1650-06-26T19:58:25Z' + } + ], + 'dc.date.issued': [ + { + language: null, + value: '1650-06-26' + } + ], + 'dc.identifier.issn': [ + { + language: 'en_US', + value: '123456789' + } + ], + 'dc.identifier.uri': [ + { + language: null, + value: 'http://dspace7.4science.it/xmlui/handle/10673/6' + } + ], + 'dc.description.abstract': [ + { + language: 'en_US', + value: 'This is really just a sample abstract. If it was a real abstract it would contain useful information about this test document. Sorry though, nothing useful in this paragraph. You probably shouldn\'t have even bothered to read it!' + } + ], + 'dc.description.provenance': [ + { + language: 'en', + value: 'Made available in DSpace on 2012-06-26T19:58:25Z (GMT). No. of bitstreams: 2\r\ntest_ppt.ppt: 12707328 bytes, checksum: a353fc7d29b3c558c986f7463a41efd3 (MD5)\r\ntest_ppt.pptx: 12468572 bytes, checksum: 599305edb4ebee329667f2c35b14d1d6 (MD5)' + }, + { + language: 'en', + value: 'Restored into DSpace on 2013-06-13T09:17:34Z (GMT).' + }, + { + language: 'en', + value: 'Restored into DSpace on 2013-06-13T11:04:16Z (GMT).' + }, + { + language: 'en', + value: 'Restored into DSpace on 2017-04-24T19:44:08Z (GMT).' + } + ], + 'dc.language': [ + { + language: 'en_US', + value: 'en' + } + ], + 'dc.rights': [ + { + language: 'en_US', + value: '© Jane Doe' + } + ], + 'dc.subject': [ + { + language: 'en_US', + value: 'keyword1' + }, + { + language: 'en_US', + value: 'keyword2' + }, + { + language: 'en_US', + value: 'keyword3' + } + ], + 'dc.title': [ + { + language: 'en_US', + value: 'Sustainable development' + } + ], + 'dc.type': [ + { + language: 'en_US', + value: 'text' + } + ] + } + } +); + +const ItemMockPid4: Item = Object.assign( + new Item(), + { + handle: '10077/21486', + lastModified: '2017-04-24T19:44:08.178+0000', + isArchived: true, + isDiscoverable: true, + isWithdrawn: false, + _links:{ + self: { + href: 'https://rest.api/rest/api/core/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357' + } + }, + id: 'ITEM4567-e89b-12d3-a456-426614174006', + uuid: 'ITEM4567-e89b-12d3-a456-426614174006', + type: 'item', + metadata: { + 'dc.creator': [ + { + language: 'en_US', + value: 'Doe, Jane' + } + ], + 'dc.date.accessioned': [ + { + language: null, + value: '1650-06-26T19:58:25Z' + } + ], + 'dc.date.available': [ + { + language: null, + value: '1650-06-26T19:58:25Z' + } + ], + 'dc.date.issued': [ + { + language: null, + value: '1650-06-26' + } + ], + 'dc.identifier.issn': [ + { + language: 'en_US', + value: '123456789' + } + ], + 'dc.identifier.uri': [ + { + language: null, + value: 'http://dspace7.4science.it/xmlui/handle/10673/6' + } + ], + 'dc.description.abstract': [ + { + language: 'en_US', + value: 'This is really just a sample abstract. If it was a real abstract it would contain useful information about this test document. Sorry though, nothing useful in this paragraph. You probably shouldn\'t have even bothered to read it!' + } + ], + 'dc.description.provenance': [ + { + language: 'en', + value: 'Made available in DSpace on 2012-06-26T19:58:25Z (GMT). No. of bitstreams: 2\r\ntest_ppt.ppt: 12707328 bytes, checksum: a353fc7d29b3c558c986f7463a41efd3 (MD5)\r\ntest_ppt.pptx: 12468572 bytes, checksum: 599305edb4ebee329667f2c35b14d1d6 (MD5)' + }, + { + language: 'en', + value: 'Restored into DSpace on 2013-06-13T09:17:34Z (GMT).' + }, + { + language: 'en', + value: 'Restored into DSpace on 2013-06-13T11:04:16Z (GMT).' + }, + { + language: 'en', + value: 'Restored into DSpace on 2017-04-24T19:44:08Z (GMT).' + } + ], + 'dc.language': [ + { + language: 'en_US', + value: 'en' + } + ], + 'dc.rights': [ + { + language: 'en_US', + value: '© Jane Doe' + } + ], + 'dc.subject': [ + { + language: 'en_US', + value: 'keyword1' + }, + { + language: 'en_US', + value: 'keyword2' + }, + { + language: 'en_US', + value: 'keyword3' + } + ], + 'dc.title': [ + { + language: 'en_US', + value: 'Reply to Critics' + } + ], + 'dc.type': [ + { + language: 'en_US', + value: 'text' + } + ] + } + } +); + +const ItemMockPid5: Item = Object.assign( + new Item(), + { + handle: '10077/21486', + lastModified: '2017-04-24T19:44:08.178+0000', + isArchived: true, + isDiscoverable: true, + isWithdrawn: false, + _links:{ + self: { + href: 'https://rest.api/rest/api/core/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357' + } + }, + id: 'ITEM4567-e89b-12d3-a456-426614174007', + uuid: 'ITEM4567-e89b-12d3-a456-426614174007', + type: 'item', + metadata: { + 'dc.creator': [ + { + language: 'en_US', + value: 'Doe, Jane' + } + ], + 'dc.date.accessioned': [ + { + language: null, + value: '1650-06-26T19:58:25Z' + } + ], + 'dc.date.available': [ + { + language: null, + value: '1650-06-26T19:58:25Z' + } + ], + 'dc.date.issued': [ + { + language: null, + value: '1650-06-26' + } + ], + 'dc.identifier.issn': [ + { + language: 'en_US', + value: '123456789' + } + ], + 'dc.identifier.uri': [ + { + language: null, + value: 'http://dspace7.4science.it/xmlui/handle/10673/6' + } + ], + 'dc.description.abstract': [ + { + language: 'en_US', + value: 'This is really just a sample abstract. If it was a real abstract it would contain useful information about this test document. Sorry though, nothing useful in this paragraph. You probably shouldn\'t have even bothered to read it!' + } + ], + 'dc.description.provenance': [ + { + language: 'en', + value: 'Made available in DSpace on 2012-06-26T19:58:25Z (GMT). No. of bitstreams: 2\r\ntest_ppt.ppt: 12707328 bytes, checksum: a353fc7d29b3c558c986f7463a41efd3 (MD5)\r\ntest_ppt.pptx: 12468572 bytes, checksum: 599305edb4ebee329667f2c35b14d1d6 (MD5)' + }, + { + language: 'en', + value: 'Restored into DSpace on 2013-06-13T09:17:34Z (GMT).' + }, + { + language: 'en', + value: 'Restored into DSpace on 2013-06-13T11:04:16Z (GMT).' + }, + { + language: 'en', + value: 'Restored into DSpace on 2017-04-24T19:44:08Z (GMT).' + } + ], + 'dc.language': [ + { + language: 'en_US', + value: 'en' + } + ], + 'dc.rights': [ + { + language: 'en_US', + value: '© Jane Doe' + } + ], + 'dc.subject': [ + { + language: 'en_US', + value: 'keyword1' + }, + { + language: 'en_US', + value: 'keyword2' + }, + { + language: 'en_US', + value: 'keyword3' + } + ], + 'dc.title': [ + { + language: 'en_US', + value: 'PROGETTAZIONE, SINTESI E VALUTAZIONE DELL\u0027ATTIVITA\u0027 ANTIMICOBATTERICA ED ANTIFUNGINA DI NUOVI DERIVATI ETEROCICLICI' + } + ], + 'dc.type': [ + { + language: 'en_US', + value: 'text' + } + ] + } + } +); + +const ItemMockPid6: Item = Object.assign( + new Item(), + { + handle: '10077/21486', + lastModified: '2017-04-24T19:44:08.178+0000', + isArchived: true, + isDiscoverable: true, + isWithdrawn: false, + _links:{ + self: { + href: 'https://rest.api/rest/api/core/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357' + } + }, + id: 'ITEM4567-e89b-12d3-a456-426614174008', + uuid: 'ITEM4567-e89b-12d3-a456-426614174008', + type: 'item', + metadata: { + 'dc.creator': [ + { + language: 'en_US', + value: 'Doe, Jane' + } + ], + 'dc.date.accessioned': [ + { + language: null, + value: '1650-06-26T19:58:25Z' + } + ], + 'dc.date.available': [ + { + language: null, + value: '1650-06-26T19:58:25Z' + } + ], + 'dc.date.issued': [ + { + language: null, + value: '1650-06-26' + } + ], + 'dc.identifier.issn': [ + { + language: 'en_US', + value: '123456789' + } + ], + 'dc.identifier.uri': [ + { + language: null, + value: 'http://dspace7.4science.it/xmlui/handle/10673/6' + } + ], + 'dc.description.abstract': [ + { + language: 'en_US', + value: 'This is really just a sample abstract. If it was a real abstract it would contain useful information about this test document. Sorry though, nothing useful in this paragraph. You probably shouldn\'t have even bothered to read it!' + } + ], + 'dc.description.provenance': [ + { + language: 'en', + value: 'Made available in DSpace on 2012-06-26T19:58:25Z (GMT). No. of bitstreams: 2\r\ntest_ppt.ppt: 12707328 bytes, checksum: a353fc7d29b3c558c986f7463a41efd3 (MD5)\r\ntest_ppt.pptx: 12468572 bytes, checksum: 599305edb4ebee329667f2c35b14d1d6 (MD5)' + }, + { + language: 'en', + value: 'Restored into DSpace on 2013-06-13T09:17:34Z (GMT).' + }, + { + language: 'en', + value: 'Restored into DSpace on 2013-06-13T11:04:16Z (GMT).' + }, + { + language: 'en', + value: 'Restored into DSpace on 2017-04-24T19:44:08Z (GMT).' + } + ], + 'dc.language': [ + { + language: 'en_US', + value: 'en' + } + ], + 'dc.rights': [ + { + language: 'en_US', + value: '© Jane Doe' + } + ], + 'dc.subject': [ + { + language: 'en_US', + value: 'keyword1' + }, + { + language: 'en_US', + value: 'keyword2' + }, + { + language: 'en_US', + value: 'keyword3' + } + ], + 'dc.title': [ + { + language: 'en_US', + value: 'Donald Davidson' + } + ], + 'dc.type': [ + { + language: 'en_US', + value: 'text' + } + ] + } + } +); + +const ItemMockPid7: Item = Object.assign( + new Item(), + { + handle: '10077/21486', + lastModified: '2017-04-24T19:44:08.178+0000', + isArchived: true, + isDiscoverable: true, + isWithdrawn: false, + _links:{ + self: { + href: 'https://rest.api/rest/api/core/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357' + } + }, + id: 'ITEM4567-e89b-12d3-a456-426614174009', + uuid: 'ITEM4567-e89b-12d3-a456-426614174009', + type: 'item', + metadata: { + 'dc.creator': [ + { + language: 'en_US', + value: 'Doe, Jane' + } + ], + 'dc.date.accessioned': [ + { + language: null, + value: '1650-06-26T19:58:25Z' + } + ], + 'dc.date.available': [ + { + language: null, + value: '1650-06-26T19:58:25Z' + } + ], + 'dc.date.issued': [ + { + language: null, + value: '1650-06-26' + } + ], + 'dc.identifier.issn': [ + { + language: 'en_US', + value: '123456789' + } + ], + 'dc.identifier.uri': [ + { + language: null, + value: 'http://dspace7.4science.it/xmlui/handle/10673/6' + } + ], + 'dc.description.abstract': [ + { + language: 'en_US', + value: 'This is really just a sample abstract. If it was a real abstract it would contain useful information about this test document. Sorry though, nothing useful in this paragraph. You probably shouldn\'t have even bothered to read it!' + } + ], + 'dc.description.provenance': [ + { + language: 'en', + value: 'Made available in DSpace on 2012-06-26T19:58:25Z (GMT). No. of bitstreams: 2\r\ntest_ppt.ppt: 12707328 bytes, checksum: a353fc7d29b3c558c986f7463a41efd3 (MD5)\r\ntest_ppt.pptx: 12468572 bytes, checksum: 599305edb4ebee329667f2c35b14d1d6 (MD5)' + }, + { + language: 'en', + value: 'Restored into DSpace on 2013-06-13T09:17:34Z (GMT).' + }, + { + language: 'en', + value: 'Restored into DSpace on 2013-06-13T11:04:16Z (GMT).' + }, + { + language: 'en', + value: 'Restored into DSpace on 2017-04-24T19:44:08Z (GMT).' + } + ], + 'dc.language': [ + { + language: 'en_US', + value: 'en' + } + ], + 'dc.rights': [ + { + language: 'en_US', + value: '© Jane Doe' + } + ], + 'dc.subject': [ + { + language: 'en_US', + value: 'keyword1' + }, + { + language: 'en_US', + value: 'keyword2' + }, + { + language: 'en_US', + value: 'keyword3' + } + ], + 'dc.title': [ + { + language: 'en_US', + value: 'Missing abstract article' + } + ], + 'dc.type': [ + { + language: 'en_US', + value: 'text' + } + ] + } + } +); + +export const ItemMockPid8: Item = Object.assign( + new Item(), + { + handle: '10077/21486', + lastModified: '2017-04-24T19:44:08.178+0000', + isArchived: true, + isDiscoverable: true, + isWithdrawn: false, + _links:{ + self: { + href: 'https://rest.api/rest/api/core/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357' + } + }, + id: 'ITEM4567-e89b-12d3-a456-426614174002', + uuid: 'ITEM4567-e89b-12d3-a456-426614174002', + type: 'item', + metadata: { + 'dc.creator': [ + { + language: 'en_US', + value: 'Doe, Jane' + } + ], + 'dc.date.accessioned': [ + { + language: null, + value: '1650-06-26T19:58:25Z' + } + ], + 'dc.date.available': [ + { + language: null, + value: '1650-06-26T19:58:25Z' + } + ], + 'dc.date.issued': [ + { + language: null, + value: '1650-06-26' + } + ], + 'dc.identifier.issn': [ + { + language: 'en_US', + value: '123456789' + } + ], + 'dc.identifier.uri': [ + { + language: null, + value: 'http://dspace7.4science.it/xmlui/handle/10673/6' + } + ], + 'dc.description.abstract': [ + { + language: 'en_US', + value: 'This is really just a sample abstract. If it was a real abstract it would contain useful information about this test document. Sorry though, nothing useful in this paragraph. You probably shouldn\'t have even bothered to read it!' + } + ], + 'dc.description.provenance': [ + { + language: 'en', + value: 'Made available in DSpace on 2012-06-26T19:58:25Z (GMT). No. of bitstreams: 2\r\ntest_ppt.ppt: 12707328 bytes, checksum: a353fc7d29b3c558c986f7463a41efd3 (MD5)\r\ntest_ppt.pptx: 12468572 bytes, checksum: 599305edb4ebee329667f2c35b14d1d6 (MD5)' + }, + { + language: 'en', + value: 'Restored into DSpace on 2013-06-13T09:17:34Z (GMT).' + }, + { + language: 'en', + value: 'Restored into DSpace on 2013-06-13T11:04:16Z (GMT).' + }, + { + language: 'en', + value: 'Restored into DSpace on 2017-04-24T19:44:08Z (GMT).' + } + ], + 'dc.language': [ + { + language: 'en_US', + value: 'en' + } + ], + 'dc.rights': [ + { + language: 'en_US', + value: '© Jane Doe' + } + ], + 'dc.subject': [ + { + language: 'en_US', + value: 'keyword1' + }, + { + language: 'en_US', + value: 'keyword2' + }, + { + language: 'en_US', + value: 'keyword3' + } + ], + 'dc.title': [ + { + language: 'en_US', + value: 'Egypt, crossroad of translations and literary interweavings (3rd-6th centuries). A reconsideration of earlier Coptic literature' + } + ], + 'dc.type': [ + { + language: 'en_US', + value: 'text' + } + ] + } + } +); + +export const ItemMockPid9: Item = Object.assign( + new Item(), + { + handle: '10077/21486', + lastModified: '2017-04-24T19:44:08.178+0000', + isArchived: true, + isDiscoverable: true, + isWithdrawn: false, + _links:{ + self: { + href: 'https://rest.api/rest/api/core/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357' + } + }, + id: 'ITEM4567-e89b-12d3-a456-426614174003', + uuid: 'ITEM4567-e89b-12d3-a456-426614174003', + type: 'item', + metadata: { + 'dc.creator': [ + { + language: 'en_US', + value: 'Doe, Jane' + } + ], + 'dc.date.accessioned': [ + { + language: null, + value: '1650-06-26T19:58:25Z' + } + ], + 'dc.date.available': [ + { + language: null, + value: '1650-06-26T19:58:25Z' + } + ], + 'dc.date.issued': [ + { + language: null, + value: '1650-06-26' + } + ], + 'dc.identifier.issn': [ + { + language: 'en_US', + value: '123456789' + } + ], + 'dc.identifier.uri': [ + { + language: null, + value: 'http://dspace7.4science.it/xmlui/handle/10673/6' + } + ], + 'dc.description.abstract': [ + { + language: 'en_US', + value: 'This is really just a sample abstract. If it was a real abstract it would contain useful information about this test document. Sorry though, nothing useful in this paragraph. You probably shouldn\'t have even bothered to read it!' + } + ], + 'dc.description.provenance': [ + { + language: 'en', + value: 'Made available in DSpace on 2012-06-26T19:58:25Z (GMT). No. of bitstreams: 2\r\ntest_ppt.ppt: 12707328 bytes, checksum: a353fc7d29b3c558c986f7463a41efd3 (MD5)\r\ntest_ppt.pptx: 12468572 bytes, checksum: 599305edb4ebee329667f2c35b14d1d6 (MD5)' + }, + { + language: 'en', + value: 'Restored into DSpace on 2013-06-13T09:17:34Z (GMT).' + }, + { + language: 'en', + value: 'Restored into DSpace on 2013-06-13T11:04:16Z (GMT).' + }, + { + language: 'en', + value: 'Restored into DSpace on 2017-04-24T19:44:08Z (GMT).' + } + ], + 'dc.language': [ + { + language: 'en_US', + value: 'en' + } + ], + 'dc.rights': [ + { + language: 'en_US', + value: '© Jane Doe' + } + ], + 'dc.subject': [ + { + language: 'en_US', + value: 'keyword1' + }, + { + language: 'en_US', + value: 'keyword2' + }, + { + language: 'en_US', + value: 'keyword3' + } + ], + 'dc.title': [ + { + language: 'en_US', + value: 'Morocco, crossroad of translations and literary interweavings (3rd-6th centuries). A reconsideration of earlier Coptic literature' + } + ], + 'dc.type': [ + { + language: 'en_US', + value: 'text' + } + ] + } + } +); + +export const ItemMockPid10: Item = Object.assign( + new Item(), + { + handle: '10713/29832', + lastModified: '2017-04-24T19:44:08.178+0000', + isArchived: true, + isDiscoverable: true, + isWithdrawn: false, + _links:{ + self: { + href: 'https://rest.api/rest/api/core/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357' + } + }, + id: 'P23e4567-e89b-12d3-a456-426614174002', + uuid: 'P23e4567-e89b-12d3-a456-426614174002', + type: 'item', + metadata: { + 'dc.creator': [ + { + language: 'en_US', + value: 'Doe, Jane' + } + ], + 'dc.date.accessioned': [ + { + language: null, + value: '1650-06-26T19:58:25Z' + } + ], + 'dc.date.available': [ + { + language: null, + value: '1650-06-26T19:58:25Z' + } + ], + 'dc.date.issued': [ + { + language: null, + value: '1650-06-26' + } + ], + 'dc.identifier.issn': [ + { + language: 'en_US', + value: '123456789' + } + ], + 'dc.identifier.uri': [ + { + language: null, + value: 'http://dspace7.4science.it/xmlui/handle/10673/6' + } + ], + 'dc.description.abstract': [ + { + language: 'en_US', + value: 'This is really just a sample abstract. If it was a real abstract it would contain useful information about this test document. Sorry though, nothing useful in this paragraph. You probably shouldn\'t have even bothered to read it!' + } + ], + 'dc.description.provenance': [ + { + language: 'en', + value: 'Made available in DSpace on 2012-06-26T19:58:25Z (GMT). No. of bitstreams: 2\r\ntest_ppt.ppt: 12707328 bytes, checksum: a353fc7d29b3c558c986f7463a41efd3 (MD5)\r\ntest_ppt.pptx: 12468572 bytes, checksum: 599305edb4ebee329667f2c35b14d1d6 (MD5)' + }, + { + language: 'en', + value: 'Restored into DSpace on 2013-06-13T09:17:34Z (GMT).' + }, + { + language: 'en', + value: 'Restored into DSpace on 2013-06-13T11:04:16Z (GMT).' + }, + { + language: 'en', + value: 'Restored into DSpace on 2017-04-24T19:44:08Z (GMT).' + } + ], + 'dc.language': [ + { + language: 'en_US', + value: 'en' + } + ], + 'dc.rights': [ + { + language: 'en_US', + value: '© Jane Doe' + } + ], + 'dc.subject': [ + { + language: 'en_US', + value: 'keyword1' + }, + { + language: 'en_US', + value: 'keyword2' + }, + { + language: 'en_US', + value: 'keyword3' + } + ], + 'dc.title': [ + { + language: 'en_US', + value: 'Tracking Papyrus and Parchment Paths: An Archaeological Atlas of Coptic Literature.\nLiterary Texts in their Geographical Context: Production, Copying, Usage, Dissemination and Storage' + } + ], + 'dc.type': [ + { + language: 'en_US', + value: 'text' + } + ] + } + } +); + +export const OpenaireMockDspaceObject: SearchResult = Object.assign( + new SearchResult(), + { + handle: '10713/29832', + lastModified: '2017-04-24T19:44:08.178+0000', + isArchived: true, + isDiscoverable: true, + isWithdrawn: false, + _links:{ + self: { + href: 'https://rest.api/rest/api/core/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357' + } + }, + id: 'P23e4567-e89b-12d3-a456-426614174002', + uuid: 'P23e4567-e89b-12d3-a456-426614174002', + type: 'item', + metadata: { + 'dc.creator': [ + { + language: 'en_US', + value: 'Doe, Jane' + } + ], + 'dc.date.accessioned': [ + { + language: null, + value: '1650-06-26T19:58:25Z' + } + ], + 'dc.date.available': [ + { + language: null, + value: '1650-06-26T19:58:25Z' + } + ], + 'dc.date.issued': [ + { + language: null, + value: '1650-06-26' + } + ], + 'dc.identifier.issn': [ + { + language: 'en_US', + value: '123456789' + } + ], + 'dc.identifier.uri': [ + { + language: null, + value: 'http://dspace7.4science.it/xmlui/handle/10673/6' + } + ], + 'dc.description.abstract': [ + { + language: 'en_US', + value: 'This is really just a sample abstract. If it was a real abstract it would contain useful information about this test document. Sorry though, nothing useful in this paragraph. You probably shouldn\'t have even bothered to read it!' + } + ], + 'dc.description.provenance': [ + { + language: 'en', + value: 'Made available in DSpace on 2012-06-26T19:58:25Z (GMT). No. of bitstreams: 2\r\ntest_ppt.ppt: 12707328 bytes, checksum: a353fc7d29b3c558c986f7463a41efd3 (MD5)\r\ntest_ppt.pptx: 12468572 bytes, checksum: 599305edb4ebee329667f2c35b14d1d6 (MD5)' + }, + { + language: 'en', + value: 'Restored into DSpace on 2013-06-13T09:17:34Z (GMT).' + }, + { + language: 'en', + value: 'Restored into DSpace on 2013-06-13T11:04:16Z (GMT).' + }, + { + language: 'en', + value: 'Restored into DSpace on 2017-04-24T19:44:08Z (GMT).' + } + ], + 'dc.language': [ + { + language: 'en_US', + value: 'en' + } + ], + 'dc.rights': [ + { + language: 'en_US', + value: '© Jane Doe' + } + ], + 'dc.subject': [ + { + language: 'en_US', + value: 'keyword1' + }, + { + language: 'en_US', + value: 'keyword2' + }, + { + language: 'en_US', + value: 'keyword3' + } + ], + 'dc.title': [ + { + language: 'en_US', + value: 'Tracking Papyrus and Parchment Paths: An Archaeological Atlas of Coptic Literature.\nLiterary Texts in their Geographical Context: Production, Copying, Usage, Dissemination and Storage' + } + ], + 'dc.type': [ + { + language: 'en_US', + value: 'text' + } + ] + } + } +); + +// Topics +// ------------------------------------------------------------------------------- + +export const openaireBrokerTopicObjectMorePid: OpenaireBrokerTopicObject = { + type: new ResourceType('nbtopic'), + id: 'ENRICH!MORE!PID', + name: 'ENRICH/MORE/PID', + lastEvent: '2020/10/09 10:11 UTC', + totalEvents: 33, + _links: { + self: { + href: 'https://rest.api/rest/api/integration/nbtopics/ENRICH!MORE!PID' + } + } +}; + +export const openaireBrokerTopicObjectMoreAbstract: OpenaireBrokerTopicObject = { + type: new ResourceType('nbtopic'), + id: 'ENRICH!MORE!ABSTRACT', + name: 'ENRICH/MORE/ABSTRACT', + lastEvent: '2020/09/08 21:14 UTC', + totalEvents: 5, + _links: { + self: { + href: 'https://rest.api/rest/api/integration/nbtopics/ENRICH!MORE!ABSTRACT' + } + } +}; + +export const openaireBrokerTopicObjectMissingPid: OpenaireBrokerTopicObject = { + type: new ResourceType('nbtopic'), + id: 'ENRICH!MISSING!PID', + name: 'ENRICH/MISSING/PID', + lastEvent: '2020/10/01 07:36 UTC', + totalEvents: 4, + _links: { + self: { + href: 'https://rest.api/rest/api/integration/nbtopics/ENRICH!MISSING!PID' + } + } +}; + +export const openaireBrokerTopicObjectMissingAbstract: OpenaireBrokerTopicObject = { + type: new ResourceType('nbtopic'), + id: 'ENRICH!MISSING!ABSTRACT', + name: 'ENRICH/MISSING/ABSTRACT', + lastEvent: '2020/10/08 16:14 UTC', + totalEvents: 71, + _links: { + self: { + href: 'https://rest.api/rest/api/integration/nbtopics/ENRICH!MISSING!ABSTRACT' + } + } +}; + +export const openaireBrokerTopicObjectMissingAcm: OpenaireBrokerTopicObject = { + type: new ResourceType('nbtopic'), + id: 'ENRICH!MISSING!SUBJECT!ACM', + name: 'ENRICH/MISSING/SUBJECT/ACM', + lastEvent: '2020/09/21 17:51 UTC', + totalEvents: 18, + _links: { + self: { + href: 'https://rest.api/rest/api/integration/nbtopics/ENRICH!MISSING!SUBJECT!ACM' + } + } +}; + +export const openaireBrokerTopicObjectMissingProject: OpenaireBrokerTopicObject = { + type: new ResourceType('nbtopic'), + id: 'ENRICH!MISSING!PROJECT', + name: 'ENRICH/MISSING/PROJECT', + lastEvent: '2020/09/17 10:28 UTC', + totalEvents: 6, + _links: { + self: { + href: 'https://rest.api/rest/api/integration/nbtopics/ENRICH!MISSING!PROJECT' + } + } +}; + +// Events +// ------------------------------------------------------------------------------- + +export const openaireBrokerEventObjectMissingPid: OpenaireBrokerEventObject = { + id: '123e4567-e89b-12d3-a456-426614174001', + uuid: '123e4567-e89b-12d3-a456-426614174001', + type: new ResourceType('nbevent'), + originalId: 'oai:www.openstarts.units.it:10077/21486', + title: 'Index nominum et rerum', + trust: 0.375, + eventDate: '2020/10/09 10:11 UTC', + status: 'PENDING', + message: { + type: 'doi', + value: '10.18848/1447-9494/cgp/v15i09/45934', + abstract: null, + openaireId: null, + acronym: null, + code: null, + funder: null, + fundingProgram: null, + jurisdiction: null, + title: null + }, + _links: { + self: { + href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174001', + }, + target: { + href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174001/target' + }, + related: { + href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174001/related' + } + }, + target: observableOf(createSuccessfulRemoteDataObject(ItemMockPid1)), + related: observableOf(createSuccessfulRemoteDataObject(ItemMockPid10)) +}; + +export const openaireBrokerEventObjectMissingPid2: OpenaireBrokerEventObject = { + id: '123e4567-e89b-12d3-a456-426614174004', + uuid: '123e4567-e89b-12d3-a456-426614174004', + type: new ResourceType('openaireBrokerEvent'), + originalId: 'oai:www.openstarts.units.it:10077/21486', + title: 'UNA NUOVA RILETTURA DELL\u0027 ARISTOTELE DI FRANZ BRENTANO ALLA LUCE DI ALCUNI INEDITI', + trust: 1.0, + eventDate: '2020/10/09 10:11 UTC', + status: 'PENDING', + message: { + type: 'urn', + value: 'http://thesis2.sba.units.it/store/handle/item/12238', + abstract: null, + openaireId: null, + acronym: null, + code: null, + funder: null, + fundingProgram: null, + jurisdiction: null, + title: null + }, + _links: { + self: { + href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174004' + }, + target: { + href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174004/target' + }, + related: { + href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174004/related' + } + }, + target: observableOf(createSuccessfulRemoteDataObject(ItemMockPid2)), + related: observableOf(createSuccessfulRemoteDataObject(ItemMockPid10)) +}; + +export const openaireBrokerEventObjectMissingPid3: OpenaireBrokerEventObject = { + id: '123e4567-e89b-12d3-a456-426614174005', + uuid: '123e4567-e89b-12d3-a456-426614174005', + type: new ResourceType('openaireBrokerEvent'), + originalId: 'oai:www.openstarts.units.it:10077/554', + title: 'Sustainable development', + trust: 0.375, + eventDate: '2020/10/09 10:11 UTC', + status: 'PENDING', + message: { + type: 'doi', + value: '10.4324/9780203408889', + abstract: null, + openaireId: null, + acronym: null, + code: null, + funder: null, + fundingProgram: null, + jurisdiction: null, + title: null + }, + _links: { + self: { + href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174005' + }, + target: { + href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174005/target' + }, + related: { + href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174005/related' + } + }, + target: observableOf(createSuccessfulRemoteDataObject(ItemMockPid3)), + related: observableOf(createSuccessfulRemoteDataObject(ItemMockPid10)) +}; + +export const openaireBrokerEventObjectMissingPid4: OpenaireBrokerEventObject = { + id: '123e4567-e89b-12d3-a456-426614174006', + uuid: '123e4567-e89b-12d3-a456-426614174006', + type: new ResourceType('openaireBrokerEvent'), + originalId: 'oai:www.openstarts.units.it:10077/10787', + title: 'Reply to Critics', + trust: 1.0, + eventDate: '2020/10/09 10:11 UTC', + status: 'PENDING', + message: { + type: 'doi', + value: '10.1080/13698230.2018.1430104', + abstract: null, + openaireId: null, + acronym: null, + code: null, + funder: null, + fundingProgram: null, + jurisdiction: null, + title: null + }, + _links: { + self: { + href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174006' + }, + target: { + href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174006/target' + }, + related: { + href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174006/related' + } + }, + target: observableOf(createSuccessfulRemoteDataObject(ItemMockPid4)), + related: observableOf(createSuccessfulRemoteDataObject(ItemMockPid10)) +}; + +export const openaireBrokerEventObjectMissingPid5: OpenaireBrokerEventObject = { + id: '123e4567-e89b-12d3-a456-426614174007', + uuid: '123e4567-e89b-12d3-a456-426614174007', + type: new ResourceType('openaireBrokerEvent'), + originalId: 'oai:www.openstarts.units.it:10077/11339', + title: 'PROGETTAZIONE, SINTESI E VALUTAZIONE DELL\u0027ATTIVITA\u0027 ANTIMICOBATTERICA ED ANTIFUNGINA DI NUOVI DERIVATI ETEROCICLICI', + trust: 0.375, + eventDate: '2020/10/09 10:11 UTC', + status: 'PENDING', + message: { + type: 'urn', + value: 'http://thesis2.sba.units.it/store/handle/item/12477', + abstract: null, + openaireId: null, + acronym: null, + code: null, + funder: null, + fundingProgram: null, + jurisdiction: null, + title: null + }, + _links: { + self: { + href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174007' + }, + target: { + href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174007/target' + }, + related: { + href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174007/related' + } + }, + target: observableOf(createSuccessfulRemoteDataObject(ItemMockPid5)), + related: observableOf(createSuccessfulRemoteDataObject(ItemMockPid10)) +}; + +export const openaireBrokerEventObjectMissingPid6: OpenaireBrokerEventObject = { + id: '123e4567-e89b-12d3-a456-426614174008', + uuid: '123e4567-e89b-12d3-a456-426614174008', + type: new ResourceType('openaireBrokerEvent'), + originalId: 'oai:www.openstarts.units.it:10077/29860', + title: 'Donald Davidson', + trust: 0.375, + eventDate: '2020/10/09 10:11 UTC', + status: 'PENDING', + message: { + type: 'doi', + value: '10.1111/j.1475-4975.2004.00098.x', + abstract: null, + openaireId: null, + acronym: null, + code: null, + funder: null, + fundingProgram: null, + jurisdiction: null, + title: null + }, + _links: { + self: { + href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174008' + }, + target: { + href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174008/target' + }, + related: { + href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174008/related' + } + }, + target: observableOf(createSuccessfulRemoteDataObject(ItemMockPid6)), + related: observableOf(createSuccessfulRemoteDataObject(ItemMockPid10)) +}; + +export const openaireBrokerEventObjectMissingAbstract: OpenaireBrokerEventObject = { + id: '123e4567-e89b-12d3-a456-426614174009', + uuid: '123e4567-e89b-12d3-a456-426614174009', + type: new ResourceType('openaireBrokerEvent'), + originalId: 'oai:www.openstarts.units.it:10077/21110', + title: 'Missing abstract article', + trust: 0.751, + eventDate: '2020/10/09 10:11 UTC', + status: 'PENDING', + message: { + type: null, + value: null, + abstract: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla scelerisque vestibulum tellus sed lacinia. Aenean vitae sapien a quam congue ultrices. Sed vehicula sollicitudin ligula, vitae lacinia velit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla scelerisque vestibulum tellus sed lacinia. Aenean vitae sapien a quam congue ultrices. Sed vehicula sollicitudin ligula, vitae lacinia velit.', + openaireId: null, + acronym: null, + code: null, + funder: null, + fundingProgram: null, + jurisdiction: null, + title: null + }, + _links: { + self: { + href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174009' + }, + target: { + href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174009/target' + }, + related: { + href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174009/related' + } + }, + target: observableOf(createSuccessfulRemoteDataObject(ItemMockPid7)), + related: observableOf(createSuccessfulRemoteDataObject(ItemMockPid10)) +}; + +export const openaireBrokerEventObjectMissingProjectFound: OpenaireBrokerEventObject = { + id: '123e4567-e89b-12d3-a456-426614174002', + uuid: '123e4567-e89b-12d3-a456-426614174002', + type: new ResourceType('openaireBrokerEvent'), + originalId: 'oai:www.openstarts.units.it:10077/21838', + title: 'Egypt, crossroad of translations and literary interweavings (3rd-6th centuries). A reconsideration of earlier Coptic literature', + trust: 1.0, + eventDate: '2020/10/09 10:11 UTC', + status: 'PENDING', + message: { + type: null, + value: null, + abstract: null, + openaireId: null, + acronym: 'PAThs', + code: '687567', + funder: 'EC', + fundingProgram: 'H2020', + jurisdiction: 'EU', + title: 'Tracking Papyrus and Parchment Paths: An Archaeological Atlas of Coptic Literature.\nLiterary Texts in their Geographical Context: Production, Copying, Usage, Dissemination and Storage' + }, + _links: { + self: { + href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174002' + }, + target: { + href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174002/target' + }, + related: { + href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174002/related' + } + }, + target: createSuccessfulRemoteDataObject$(ItemMockPid8), + related: createSuccessfulRemoteDataObject$(ItemMockPid10) +}; + +export const openaireBrokerEventObjectMissingProjectNotFound: OpenaireBrokerEventObject = { + id: '123e4567-e89b-12d3-a456-426614174003', + uuid: '123e4567-e89b-12d3-a456-426614174003', + type: new ResourceType('openaireBrokerEvent'), + originalId: 'oai:www.openstarts.units.it:10077/21838', + title: 'Morocco, crossroad of translations and literary interweavings (3rd-6th centuries). A reconsideration of earlier Coptic literature', + trust: 1.0, + eventDate: '2020/10/09 10:11 UTC', + status: 'PENDING', + message: { + type: null, + value: null, + abstract: null, + openaireId: null, + acronym: 'PAThs', + code: '687567B', + funder: 'EC', + fundingProgram: 'H2021', + jurisdiction: 'EU', + title: 'Tracking Unknown Papyrus and Parchment Paths: An Archaeological Atlas of Coptic Literature.\nLiterary Texts in their Geographical Context: Production, Copying, Usage, Dissemination and Storage' + }, + _links: { + self: { + href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174003' + }, + target: { + href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174003/target' + }, + related: { + href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174003/related' + } + }, + target: createSuccessfulRemoteDataObject$(ItemMockPid9), + related: createNoContentRemoteDataObject$() +}; + +// Classes +// ------------------------------------------------------------------------------- + +/** + * Mock for [[OpenaireStateService]] + */ +export function getMockOpenaireStateService(): any { + return jasmine.createSpyObj('OpenaireStateService', { + getOpenaireBrokerTopics: jasmine.createSpy('getOpenaireBrokerTopics'), + isOpenaireBrokerTopicsLoading: jasmine.createSpy('isOpenaireBrokerTopicsLoading'), + isOpenaireBrokerTopicsLoaded: jasmine.createSpy('isOpenaireBrokerTopicsLoaded'), + isOpenaireBrokerTopicsProcessing: jasmine.createSpy('isOpenaireBrokerTopicsProcessing'), + getOpenaireBrokerTopicsTotalPages: jasmine.createSpy('getOpenaireBrokerTopicsTotalPages'), + getOpenaireBrokerTopicsCurrentPage: jasmine.createSpy('getOpenaireBrokerTopicsCurrentPage'), + getOpenaireBrokerTopicsTotals: jasmine.createSpy('getOpenaireBrokerTopicsTotals'), + dispatchRetrieveOpenaireBrokerTopics: jasmine.createSpy('dispatchRetrieveOpenaireBrokerTopics'), + dispatchMarkUserSuggestionsAsVisitedAction: jasmine.createSpy('dispatchMarkUserSuggestionsAsVisitedAction') + }); +} + +/** + * Mock for [[OpenaireBrokerTopicRestService]] + */ +export function getMockOpenaireBrokerTopicRestService(): OpenaireBrokerTopicRestService { + return jasmine.createSpyObj('OpenaireBrokerTopicRestService', { + getTopics: jasmine.createSpy('getTopics'), + getTopic: jasmine.createSpy('getTopic'), + }); +} + +/** + * Mock for [[OpenaireBrokerEventRestService]] + */ +export function getMockOpenaireBrokerEventRestService(): OpenaireBrokerEventRestService { + return jasmine.createSpyObj('OpenaireBrokerEventRestService', { + getEventsByTopic: jasmine.createSpy('getEventsByTopic'), + getEvent: jasmine.createSpy('getEvent'), + patchEvent: jasmine.createSpy('patchEvent'), + boundProject: jasmine.createSpy('boundProject'), + removeProject: jasmine.createSpy('removeProject'), + clearFindByTopicRequests: jasmine.createSpy('.clearFindByTopicRequests') + }); +} + +/** + * Mock for [[OpenaireBrokerEventRestService]] + */ +export function getMockSuggestionsService(): any { + return jasmine.createSpyObj('SuggestionsService', { + getTargets: jasmine.createSpy('getTargets'), + getSuggestions: jasmine.createSpy('getSuggestions'), + clearSuggestionRequests: jasmine.createSpy('clearSuggestionRequests'), + deleteReviewedSuggestion: jasmine.createSpy('deleteReviewedSuggestion'), + retrieveCurrentUserSuggestions: jasmine.createSpy('retrieveCurrentUserSuggestions'), + getTargetUuid: jasmine.createSpy('getTargetUuid'), + }); +} diff --git a/src/app/shared/mocks/reciter-suggestion-targets.mock.ts b/src/app/shared/mocks/reciter-suggestion-targets.mock.ts new file mode 100644 index 00000000000..42e293e6031 --- /dev/null +++ b/src/app/shared/mocks/reciter-suggestion-targets.mock.ts @@ -0,0 +1,42 @@ +import { ResourceType } from '../../core/shared/resource-type'; +import { OpenaireSuggestionTarget } from '../../core/openaire/reciter-suggestions/models/openaire-suggestion-target.model'; + +// REST Mock --------------------------------------------------------------------- +// ------------------------------------------------------------------------------- +export const mockSuggestionTargetsObjectOne: OpenaireSuggestionTarget = { + type: new ResourceType('suggestiontarget'), + id: 'reciter:gf3d657-9d6d-4a87-b905-fef0f8cae26', + display: 'Bollini, Andrea', + source: 'reciter', + total: 31, + _links: { + target: { + href: 'https://rest.api/rest/api/core/items/gf3d657-9d6d-4a87-b905-fef0f8cae26' + }, + suggestions: { + href: 'https://rest.api/rest/api/integration/suggestions/search/findByTargetAndSource?target=gf3d657-9d6d-4a87-b905-fef0f8cae26c&source=reciter' + }, + self: { + href: 'https://rest.api/rest/api/integration/suggestiontargets/reciter:gf3d657-9d6d-4a87-b905-fef0f8cae26' + } + } +}; + +export const mockSuggestionTargetsObjectTwo: OpenaireSuggestionTarget = { + type: new ResourceType('suggestiontarget'), + id: 'reciter:nhy567-9d6d-ty67-b905-fef0f8cae26', + display: 'Digilio, Andrea', + source: 'reciter', + total: 12, + _links: { + target: { + href: 'https://rest.api/rest/api/core/items/nhy567-9d6d-ty67-b905-fef0f8cae26' + }, + suggestions: { + href: 'https://rest.api/rest/api/integration/suggestions/search/findByTargetAndSource?target=nhy567-9d6d-ty67-b905-fef0f8cae26&source=reciter' + }, + self: { + href: 'https://rest.api/rest/api/integration/suggestiontargets/reciter:nhy567-9d6d-ty67-b905-fef0f8cae26' + } + } +}; diff --git a/src/app/shared/mocks/reciter-suggestion.mock.ts b/src/app/shared/mocks/reciter-suggestion.mock.ts new file mode 100644 index 00000000000..2ddb49868f1 --- /dev/null +++ b/src/app/shared/mocks/reciter-suggestion.mock.ts @@ -0,0 +1,210 @@ + +// REST Mock --------------------------------------------------------------------- +// ------------------------------------------------------------------------------- + +import { OpenaireSuggestion } from '../../core/openaire/reciter-suggestions/models/openaire-suggestion.model'; +import { SUGGESTION } from '../../core/openaire/reciter-suggestions/models/openaire-suggestion-objects.resource-type'; + +export const mockSuggestionPublicationOne: OpenaireSuggestion = { + id: '24694772', + display: 'publication one', + source: 'reciter', + externalSourceUri: 'https://dspace7.4science.cloud/server/api/integration/reciterSourcesEntry/pubmed/entryValues/24694772', + score: '48', + evidences: { + acceptedRejectedEvidence: { + score: '2.7', + notes: 'some notes, eventually empty or null' + }, + authorNameEvidence: { + score: '0', + notes: 'some notes, eventually empty or null' + }, + journalCategoryEvidence: { + score: '6', + notes: 'some notes, eventually empty or null' + }, + affiliationEvidence: { + score: 'xxx', + notes: 'some notes, eventually empty or null' + }, + relationshipEvidence: { + score: '9', + notes: 'some notes, eventually empty or null' + }, + educationYearEvidence: { + score: '3.6', + notes: 'some notes, eventually empty or null' + }, + personTypeEvidence: { + score: '4', + notes: 'some notes, eventually empty or null' + }, + articleCountEvidence: { + score: '6.7', + notes: 'some notes, eventually empty or null' + }, + averageClusteringEvidence: { + score: '7', + notes: 'some notes, eventually empty or null' + } + }, + metadata: { + 'dc.identifier.uri': [ + { + value: 'https://publication/0000-0003-3681-2038', + language: null, + authority: null, + confidence: -1, + place: -1 + } as any + ], + 'dc.title': [ + { + value: 'publication one', + language: null, + authority: null, + confidence: -1 + } as any + ], + 'dc.date.issued': [ + { + value: '2010-11-03', + language: null, + authority: null, + confidence: -1 + } as any + ], + 'dspace.entity.type': [ + { + uuid: '95f21fe6-ce38-43d6-96d4-60ae66385a06', + language: null, + value: 'OrgUnit', + place: 0, + authority: null, + confidence: -1 + } as any + ], + 'dc.description': [ + { + uuid: '95f21fe6-ce38-43d6-96d4-60ae66385a06', + language: null, + value: "It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).", + place: 0, + authority: null, + confidence: -1 + } as any + ] + }, + type: SUGGESTION, + _links: { + target: { + href: 'https://dspace7.4science.cloud/server/api/core/items/gf3d657-9d6d-4a87-b905-fef0f8cae26' + }, + self: { + href: 'https://dspace7.4science.cloud/server/api/integration/suggestions/reciter:gf3d657-9d6d-4a87-b905-fef0f8cae26c:24694772' + } + } +}; + +export const mockSuggestionPublicationTwo: OpenaireSuggestion = { + id: '24694772', + display: 'publication two', + source: 'reciter', + externalSourceUri: 'https://dspace7.4science.cloud/server/api/integration/reciterSourcesEntry/pubmed/entryValues/24694772', + score: '48', + evidences: { + acceptedRejectedEvidence: { + score: '2.7', + notes: 'some notes, eventually empty or null' + }, + authorNameEvidence: { + score: '0', + notes: 'some notes, eventually empty or null' + }, + journalCategoryEvidence: { + score: '6', + notes: 'some notes, eventually empty or null' + }, + affiliationEvidence: { + score: 'xxx', + notes: 'some notes, eventually empty or null' + }, + relationshipEvidence: { + score: '9', + notes: 'some notes, eventually empty or null' + }, + educationYearEvidence: { + score: '3.6', + notes: 'some notes, eventually empty or null' + }, + personTypeEvidence: { + score: '4', + notes: 'some notes, eventually empty or null' + }, + articleCountEvidence: { + score: '6.7', + notes: 'some notes, eventually empty or null' + }, + averageClusteringEvidence: { + score: '7', + notes: 'some notes, eventually empty or null' + } + }, + metadata: { + 'dc.identifier.uri': [ + { + value: 'https://publication/0000-0003-3681-2038', + language: null, + authority: null, + confidence: -1, + place: -1 + } as any + ], + 'dc.title': [ + { + value: 'publication one', + language: null, + authority: null, + confidence: -1 + } as any + ], + 'dc.date.issued': [ + { + value: '2010-11-03', + language: null, + authority: null, + confidence: -1 + } as any + ], + 'dspace.entity.type': [ + { + uuid: '95f21fe6-ce38-43d6-96d4-60ae66385a06', + language: null, + value: 'OrgUnit', + place: 0, + authority: null, + confidence: -1 + } as any + ], + 'dc.description': [ + { + uuid: '95f21fe6-ce38-43d6-96d4-60ae66385a06', + language: null, + value: "It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).", + place: 0, + authority: null, + confidence: -1 + } as any + ] + }, + type: SUGGESTION, + _links: { + target: { + href: 'https://dspace7.4science.cloud/server/api/core/items/gf3d657-9d6d-4a87-b905-fef0f8cae26' + }, + self: { + href: 'https://dspace7.4science.cloud/server/api/integration/suggestions/reciter:gf3d657-9d6d-4a87-b905-fef0f8cae26c:24694772' + } + } +}; diff --git a/src/app/suggestions-page/suggestions-page-routing-paths.ts b/src/app/suggestions-page/suggestions-page-routing-paths.ts new file mode 100644 index 00000000000..0f3aa782d21 --- /dev/null +++ b/src/app/suggestions-page/suggestions-page-routing-paths.ts @@ -0,0 +1,11 @@ +import { URLCombiner } from '../core/url-combiner/url-combiner'; + +export const SUGGESTION_MODULE_PATH = 'suggestions'; + +export function getSuggestionModuleRoute() { + return `/${SUGGESTION_MODULE_PATH}`; +} + +export function getSuggestionPageRoute(SuggestionId: string) { + return new URLCombiner(getSuggestionModuleRoute(), SuggestionId).toString(); +} diff --git a/src/app/suggestions-page/suggestions-page-routing.module.ts b/src/app/suggestions-page/suggestions-page-routing.module.ts new file mode 100644 index 00000000000..20ed658707e --- /dev/null +++ b/src/app/suggestions-page/suggestions-page-routing.module.ts @@ -0,0 +1,36 @@ +import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; + +import { SuggestionsPageResolver } from './suggestions-page.resolver'; +import { SuggestionsPageComponent } from './suggestions-page.component'; +import { AuthenticatedGuard } from '../core/auth/authenticated.guard'; +import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver'; +import { SiteAdministratorGuard } from '../core/data/feature-authorization/feature-authorization-guard/site-administrator.guard'; + +@NgModule({ + imports: [ + RouterModule.forChild([ + { + path: ':targetId', + resolve: { + suggestionTargets: SuggestionsPageResolver, + breadcrumb: I18nBreadcrumbResolver + }, + data: { + title: 'admin.notifications.recitersuggestion.page.title', + breadcrumbKey: 'admin.notifications.recitersuggestion', + showBreadcrumbsFluid: false + }, + canActivate: [AuthenticatedGuard], + runGuardsAndResolvers: 'always', + component: SuggestionsPageComponent, + }, + ]) + ], + providers: [ + SuggestionsPageResolver + ] +}) +export class SuggestionsPageRoutingModule { + +} diff --git a/src/app/suggestions-page/suggestions-page.component.html b/src/app/suggestions-page/suggestions-page.component.html new file mode 100644 index 00000000000..fb5f08b6a4c --- /dev/null +++ b/src/app/suggestions-page/suggestions-page.component.html @@ -0,0 +1,48 @@ +
+
+
+ +
+ +

+ {{ translateSuggestionType() | translate }} + {{'reciter.suggestion.suggestionFor' | translate}} + {{researcherName}} + {{'reciter.suggestion.from.source' | translate}} {{ translateSuggestionSource() | translate }} +

+ +
+ + ({{ getSelectedSuggestionsCount() }}) + + +
+ + +
    +
  • + +
  • +
+
+
+
+
+
+
diff --git a/src/app/suggestions-page/suggestions-page.component.scss b/src/app/suggestions-page/suggestions-page.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/app/suggestions-page/suggestions-page.component.spec.ts b/src/app/suggestions-page/suggestions-page.component.spec.ts new file mode 100644 index 00000000000..ad518930d98 --- /dev/null +++ b/src/app/suggestions-page/suggestions-page.component.spec.ts @@ -0,0 +1,107 @@ +import { CommonModule } from '@angular/common'; +import { BrowserModule } from '@angular/platform-browser'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { TranslateModule, TranslateService } from '@ngx-translate/core'; +import { of as observableOf } from 'rxjs'; + +import { SuggestionsPageComponent } from './suggestions-page.component'; +import { SuggestionListElementComponent } from '../openaire/reciter-suggestions/suggestion-list-element/suggestion-list-element.component'; +import { SuggestionsService } from '../openaire/reciter-suggestions/suggestions.service'; +import { getMockOpenaireStateService, getMockSuggestionsService } from '../shared/mocks/openaire.mock'; +import { buildPaginatedList, PaginatedList } from '../core/data/paginated-list.model'; +import { OpenaireSuggestion } from '../core/openaire/reciter-suggestions/models/openaire-suggestion.model'; +import { mockSuggestionPublicationOne, mockSuggestionPublicationTwo } from '../shared/mocks/reciter-suggestion.mock'; +import { SuggestionEvidencesComponent } from '../openaire/reciter-suggestions/suggestion-list-element/suggestion-evidences/suggestion-evidences.component'; +import { ObjectKeysPipe } from '../shared/utils/object-keys-pipe'; +import { VarDirective } from '../shared/utils/var.directive'; +import { ActivatedRoute, Router } from '@angular/router'; +import { RouterStub } from '../shared/testing/router.stub'; +import { mockSuggestionTargetsObjectOne } from '../shared/mocks/reciter-suggestion-targets.mock'; +import { AuthService } from '../core/auth/auth.service'; +import { NotificationsService } from '../shared/notifications/notifications.service'; +import { NotificationsServiceStub } from '../shared/testing/notifications-service.stub'; +import { getMockTranslateService } from '../shared/mocks/translate.service.mock'; +import { SuggestionTargetsStateService } from '../openaire/reciter-suggestions/suggestion-targets/suggestion-targets.state.service'; +import { WorkspaceitemDataService } from '../core/submission/workspaceitem-data.service'; +import { createSuccessfulRemoteDataObject } from '../shared/remote-data.utils'; +import { PageInfo } from '../core/shared/page-info.model'; +import { TestScheduler } from 'rxjs/testing'; +import { getTestScheduler } from 'jasmine-marbles'; +import { PaginationServiceStub } from '../shared/testing/pagination-service.stub'; +import { PaginationService } from '../core/pagination/pagination.service'; + +describe('SuggestionPageComponent', () => { + let component: SuggestionsPageComponent; + let fixture: ComponentFixture; + let scheduler: TestScheduler; + const mockSuggestionsService = getMockSuggestionsService(); + const mockSuggestionsTargetStateService = getMockOpenaireStateService(); + const suggestionTargetsList: PaginatedList = buildPaginatedList(new PageInfo(), [mockSuggestionPublicationOne, mockSuggestionPublicationTwo]); + const router = new RouterStub(); + const routeStub = { + data: observableOf({ + suggestionTargets: createSuccessfulRemoteDataObject(mockSuggestionTargetsObjectOne) + }), + queryParams: observableOf({}) + }; + const workspaceitemServiceMock = jasmine.createSpyObj('WorkspaceitemDataService', { + importExternalSourceEntry: jasmine.createSpy('importExternalSourceEntry') + }); + + const authService = jasmine.createSpyObj('authService', { + isAuthenticated: observableOf(true), + setRedirectUrl: {} + }); + const paginationService = new PaginationServiceStub(); + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + BrowserModule, + CommonModule, + TranslateModule.forRoot() + ], + declarations: [ + SuggestionEvidencesComponent, + SuggestionListElementComponent, + SuggestionsPageComponent, + ObjectKeysPipe, + VarDirective + ], + providers: [ + { provide: AuthService, useValue: authService }, + { provide: ActivatedRoute, useValue: routeStub }, + { provide: WorkspaceitemDataService, useValue: workspaceitemServiceMock }, + { provide: Router, useValue: router }, + { provide: SuggestionsService, useValue: mockSuggestionsService }, + { provide: SuggestionTargetsStateService, useValue: mockSuggestionsTargetStateService }, + { provide: NotificationsService, useValue: new NotificationsServiceStub() }, + { provide: TranslateService, useValue: getMockTranslateService() }, + { provide: PaginationService, useValue: paginationService }, + SuggestionsPageComponent + ], + schemas: [NO_ERRORS_SCHEMA] + }) + .compileComponents().then(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SuggestionsPageComponent); + component = fixture.componentInstance; + scheduler = getTestScheduler(); + }); + + it('should create', () => { + spyOn(component, 'updatePage').and.stub(); + + scheduler.schedule(() => fixture.detectChanges()); + scheduler.flush(); + + expect(component).toBeTruthy(); + expect(component.suggestionId).toBe(mockSuggestionTargetsObjectOne.id); + expect(component.researcherName).toBe(mockSuggestionTargetsObjectOne.display); + expect(component.updatePage).toHaveBeenCalled(); + }); +}); diff --git a/src/app/suggestions-page/suggestions-page.component.ts b/src/app/suggestions-page/suggestions-page.component.ts new file mode 100644 index 00000000000..911f6167de5 --- /dev/null +++ b/src/app/suggestions-page/suggestions-page.component.ts @@ -0,0 +1,285 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute, Data, Router } from '@angular/router'; + +import { BehaviorSubject, combineLatest, Observable } from 'rxjs'; +import { distinctUntilChanged, map, switchMap, take } from 'rxjs/operators'; +import { TranslateService } from '@ngx-translate/core'; + +import { SortDirection, SortOptions, } from '../core/cache/models/sort-options.model'; +import { PaginatedList } from '../core/data/paginated-list.model'; +import { RemoteData } from '../core/data/remote-data'; +import { getFirstSucceededRemoteDataPayload, redirectOn4xx } from '../core/shared/operators'; +import { SuggestionBulkResult, SuggestionsService } from '../openaire/reciter-suggestions/suggestions.service'; +import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model'; +import { OpenaireSuggestion } from '../core/openaire/reciter-suggestions/models/openaire-suggestion.model'; +import { OpenaireSuggestionTarget } from '../core/openaire/reciter-suggestions/models/openaire-suggestion-target.model'; +import { AuthService } from '../core/auth/auth.service'; +import { SuggestionApproveAndImport } from '../openaire/reciter-suggestions/suggestion-list-element/suggestion-list-element.component'; +import { NotificationsService } from '../shared/notifications/notifications.service'; +import { SuggestionTargetsStateService } from '../openaire/reciter-suggestions/suggestion-targets/suggestion-targets.state.service'; +import { WorkspaceitemDataService } from '../core/submission/workspaceitem-data.service'; +import { PaginationService } from '../core/pagination/pagination.service'; +import { FindListOptions } from '../core/data/request.models'; +import { WorkspaceItem } from '../core/submission/models/workspaceitem.model'; + +@Component({ + selector: 'ds-suggestion-page', + templateUrl: './suggestions-page.component.html', + styleUrls: ['./suggestions-page.component.scss'], +}) +export class SuggestionsPageComponent implements OnInit { + + /** + * The pagination configuration + */ + paginationOptions: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { + id: 'sp', + pageSizeOptions: [5, 10, 20, 40, 60] + }); + + /** + * The sorting configuration + */ + paginationSortConfig: SortOptions = new SortOptions('trust', SortDirection.DESC); + + /** + * The FindListOptions object + */ + defaultConfig: FindListOptions = Object.assign(new FindListOptions(), {sort: this.paginationSortConfig}); + + /** + * A boolean representing if results are loading + */ + public processing$ = new BehaviorSubject(false); + + /** + * A list of remote data objects of suggestions + */ + suggestionsRD$: BehaviorSubject> = new BehaviorSubject>({} as any); + + targetRD$: Observable>; + targetId$: Observable; + + suggestionTarget: OpenaireSuggestionTarget; + suggestionId: any; + suggestionSource: any; + researcherName: any; + researcherUuid: any; + + selectedSuggestions: { [id: string]: OpenaireSuggestion } = {}; + isBulkOperationPending = false; + + constructor( + private authService: AuthService, + private notificationService: NotificationsService, + private paginationService: PaginationService, + private route: ActivatedRoute, + private router: Router, + private suggestionService: SuggestionsService, + private suggestionTargetsStateService: SuggestionTargetsStateService, + private translateService: TranslateService, + private workspaceItemService: WorkspaceitemDataService + ) { + } + + ngOnInit(): void { + this.targetRD$ = this.route.data.pipe( + map((data: Data) => data.suggestionTargets as RemoteData), + redirectOn4xx(this.router, this.authService) + ); + + this.targetId$ = this.targetRD$.pipe( + getFirstSucceededRemoteDataPayload(), + map((target: OpenaireSuggestionTarget) => target.id) + ); + this.targetRD$.pipe( + getFirstSucceededRemoteDataPayload() + ).subscribe((suggestionTarget: OpenaireSuggestionTarget) => { + this.suggestionTarget = suggestionTarget; + this.suggestionId = suggestionTarget.id; + this.researcherName = suggestionTarget.display; + this.suggestionSource = suggestionTarget.source; + this.researcherUuid = this.suggestionService.getTargetUuid(suggestionTarget); + this.updatePage(); + }); + + this.suggestionTargetsStateService.dispatchMarkUserSuggestionsAsVisitedAction(); + } + + /** + * Called when one of the pagination settings is changed + */ + onPaginationChange() { + this.updatePage(); + } + + /** + * Update the list of suggestions + */ + updatePage() { + this.processing$.next(true); + const pageConfig$: Observable = this.paginationService.getFindListOptions( + this.paginationOptions.id, + this.defaultConfig, + ).pipe( + distinctUntilChanged() + ); + combineLatest([this.targetId$, pageConfig$]).pipe( + switchMap(([targetId, config]: [string, FindListOptions]) => { + return this.suggestionService.getSuggestions( + targetId, + config.elementsPerPage, + config.currentPage, + config.sort + ); + }), + take(1) + ).subscribe((results: PaginatedList) => { + this.processing$.next(false); + this.suggestionsRD$.next(results); + this.suggestionService.clearSuggestionRequests(); + // navigate to the mydspace if no suggestions remains + + // if (results.totalElements === 0) { + // const content = this.translateService.instant('reciter.suggestion.empty', + // this.suggestionService.getNotificationSuggestionInterpolation(this.suggestionTarget)); + // this.notificationService.success('', content, {timeOut:0}, true); + // TODO if the target is not the current use route to the suggestion target page + // this.router.navigate(['/mydspace']); + // } + }); + } + + /** + * Used to delete a suggestion. + * @suggestionId + */ + notMine(suggestionId) { + this.suggestionService.notMine(suggestionId).subscribe((res) => { + this.suggestionTargetsStateService.dispatchRefreshUserSuggestionsAction(); + this.updatePage(); + }); + } + + /** + * Used to delete all selected suggestions. + */ + notMineAllSelected() { + this.isBulkOperationPending = true; + this.suggestionService + .notMineMultiple(Object.values(this.selectedSuggestions)) + .subscribe((results: SuggestionBulkResult) => { + this.suggestionTargetsStateService.dispatchRefreshUserSuggestionsAction(); + this.updatePage(); + this.isBulkOperationPending = false; + this.selectedSuggestions = {}; + if (results.success > 0) { + this.notificationService.success( + this.translateService.get('reciter.suggestion.notMine.bulk.success', + {count: results.success})); + } + if (results.fails > 0) { + this.notificationService.error( + this.translateService.get('reciter.suggestion.notMine.bulk.error', + {count: results.fails})); + } + }); + } + + /** + * Used to approve & import. + * @param event contains the suggestion and the target collection + */ + approveAndImport(event: SuggestionApproveAndImport) { + this.suggestionService.approveAndImport(this.workspaceItemService, event.suggestion, event.collectionId) + .subscribe((workspaceitem: WorkspaceItem) => { + const content = this.translateService.instant('reciter.suggestion.approveAndImport.success', { workspaceItemId: workspaceitem.id }); + this.notificationService.success('', content, {timeOut:0}, true); + this.suggestionTargetsStateService.dispatchRefreshUserSuggestionsAction(); + this.updatePage(); + }); + } + + /** + * Used to approve & import all selected suggestions. + * @param event contains the target collection + */ + approveAndImportAllSelected(event: SuggestionApproveAndImport) { + this.isBulkOperationPending = true; + this.suggestionService + .approveAndImportMultiple(this.workspaceItemService, Object.values(this.selectedSuggestions), event.collectionId) + .subscribe((results: SuggestionBulkResult) => { + this.suggestionTargetsStateService.dispatchRefreshUserSuggestionsAction(); + this.updatePage(); + this.isBulkOperationPending = false; + this.selectedSuggestions = {}; + if (results.success > 0) { + this.notificationService.success( + this.translateService.get('reciter.suggestion.approveAndImport.bulk.success', + {count: results.success})); + } + if (results.fails > 0) { + this.notificationService.error( + this.translateService.get('reciter.suggestion.approveAndImport.bulk.error', + {count: results.fails})); + } + }); + } + + /** + * When a specific suggestion is selected. + * @param object the suggestions + * @param selected the new selected value for the suggestion + */ + onSelected(object: OpenaireSuggestion, selected: boolean) { + if (selected) { + this.selectedSuggestions[object.id] = object; + } else { + delete this.selectedSuggestions[object.id]; + } + } + + /** + * When Toggle Select All occurs. + * @param suggestions all the visible suggestions inside the page + */ + onToggleSelectAll(suggestions: OpenaireSuggestion[]) { + if ( this.getSelectedSuggestionsCount() > 0) { + this.selectedSuggestions = {}; + } else { + suggestions.forEach((suggestion) => { + this.selectedSuggestions[suggestion.id] = suggestion; + }); + } + } + + /** + * The current number of selected suggestions. + */ + getSelectedSuggestionsCount(): number { + return Object.keys(this.selectedSuggestions).length; + } + + /** + * Return true if all the suggestion are configured with the same fixed collection in the configuration. + * @param suggestions + */ + isCollectionFixed(suggestions: OpenaireSuggestion[]): boolean { + return this.suggestionService.isCollectionFixed(suggestions); + } + + /** + * Label to be used to translate the suggestion source. + */ + translateSuggestionSource() { + return this.suggestionService.translateSuggestionSource(this.suggestionSource); + } + + /** + * Label to be used to translate the suggestion type. + */ + translateSuggestionType() { + return this.suggestionService.translateSuggestionType(this.suggestionSource); + } + +} diff --git a/src/app/suggestions-page/suggestions-page.module.ts b/src/app/suggestions-page/suggestions-page.module.ts new file mode 100644 index 00000000000..3fad2d0c19e --- /dev/null +++ b/src/app/suggestions-page/suggestions-page.module.ts @@ -0,0 +1,24 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { SuggestionsPageComponent } from './suggestions-page.component'; +import { SharedModule } from '../shared/shared.module'; +import { SuggestionsPageRoutingModule } from './suggestions-page-routing.module'; +import { SuggestionsService } from '../openaire/reciter-suggestions/suggestions.service'; +import { OpenaireSuggestionsDataService } from '../core/openaire/reciter-suggestions/openaire-suggestions-data.service'; +import { OpenaireModule } from '../openaire/openaire.module'; + +@NgModule({ + declarations: [SuggestionsPageComponent], + imports: [ + CommonModule, + SharedModule, + OpenaireModule, + SuggestionsPageRoutingModule + ], + providers: [ + OpenaireSuggestionsDataService, + SuggestionsService + ] +}) +export class SuggestionsPageModule { } diff --git a/src/app/suggestions-page/suggestions-page.resolver.ts b/src/app/suggestions-page/suggestions-page.resolver.ts new file mode 100644 index 00000000000..0027ae2e779 --- /dev/null +++ b/src/app/suggestions-page/suggestions-page.resolver.ts @@ -0,0 +1,32 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router'; + +import { Observable } from 'rxjs'; +import { find } from 'rxjs/operators'; + +import { RemoteData } from '../core/data/remote-data'; +import { hasValue } from '../shared/empty.util'; +import { OpenaireSuggestionsDataService } from '../core/openaire/reciter-suggestions/openaire-suggestions-data.service'; +import { OpenaireSuggestionTarget } from '../core/openaire/reciter-suggestions/models/openaire-suggestion-target.model'; + +/** + * This class represents a resolver that requests a specific collection before the route is activated + */ +@Injectable() +export class SuggestionsPageResolver implements Resolve> { + constructor(private suggestionsDataService: OpenaireSuggestionsDataService) { + } + + /** + * Method for resolving a suggestion target based on the parameters in the current route + * @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot + * @param {RouterStateSnapshot} state The current RouterStateSnapshot + * @returns Observable<> Emits the found collection based on the parameters in the current route, + * or an error if something went wrong + */ + resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable> { + return this.suggestionsDataService.getTargetById(route.params.targetId).pipe( + find((RD) => hasValue(RD.hasFailed) || RD.hasSucceeded), + ); + } +} diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 2af9d16b0c3..e2919097d6c 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -27,6 +27,10 @@ "404.page-not-found": "page not found", + "admin.notifications.recitersuggestion.breadcrumbs": "Suggestions", + + "admin.notifications.recitersuggestion.page.title": "Suggestions", + "admin.curation-tasks.breadcrumbs": "System curation tasks", "admin.curation-tasks.title": "System curation tasks", @@ -2511,6 +2515,7 @@ "menu.section.icon.unpin": "Unpin sidebar", + "menu.section.icon.notifications": "Notifictions menu section", "menu.section.import": "Import", @@ -2533,6 +2538,10 @@ "menu.section.new_process": "Process", + "menu.section.notifications": "Notifications", + + "menu.section.notifications_reciter": "Publication Claim", + "menu.section.pin": "Pin sidebar", @@ -2960,6 +2969,62 @@ "media-viewer.playlist": "Playlist", + "reciter.suggestion.loading": "Loading ...", + + "reciter.suggestion.title": "Suggestions", + + "reciter.suggestion.targets.description": "Below you can see all the suggestions ", + + "reciter.suggestion.targets": "Current Suggestions", + + "reciter.suggestion.table.name": "Researcher Name", + + "reciter.suggestion.table.actions": "Actions", + + "reciter.suggestion.button.review": "Review {{ total }} suggestion(s)", + + "reciter.suggestion.noTargets": "No target found.", + + "reciter.suggestion.target.error.service.retrieve": "An error occurred while loading the Suggestion targets", + + "reciter.suggestion.evidence.type": "Type", + + "reciter.suggestion.evidence.score": "Score", + + "reciter.suggestion.evidence.notes": "Notes", + + "reciter.suggestion.approveAndImport": "Approve & import", + + "reciter.suggestion.approveAndImport.success": "The suggestion has been imported successfully", + + "reciter.suggestion.approveAndImport.bulk": "Approve & import Selected", + + "reciter.suggestion.approveAndImport.bulk.success": "{{ count }} suggestions have been imported successfully ", + + "reciter.suggestion.approveAndImport.bulk.error": "{{ count }} suggestions haven't been imported due to unexpected server errors", + + "reciter.suggestion.notMine": "Not mine", + + "reciter.suggestion.notMine.success": "The suggestion has been discarded", + + "reciter.suggestion.notMine.bulk": "Not mine Selected", + + "reciter.suggestion.notMine.bulk.success": "{{ count }} suggestions have been discarded ", + + "reciter.suggestion.notMine.bulk.error": "{{ count }} suggestions haven't been discarded due to unexpected server errors", + + "reciter.suggestion.seeEvidence": "See evidence", + + "reciter.suggestion.hideEvidence": "Hide evidence", + + "reciter.suggestion.suggestionFor": "Suggestion for", + + "reciter.suggestion.source.oaire": "OpenAIRE Graph", + + "reciter.suggestion.from.source": "from the ", + + "reciter.suggestion.totalScore": "Total Score", + "register-email.title": "New user registration", From b4d6fbc390282a381e1f7cc340615c40f39e936a Mon Sep 17 00:00:00 2001 From: Luca Giamminonni Date: Mon, 2 May 2022 17:40:51 +0200 Subject: [PATCH 02/34] [CST-5249] Fixed compilation issues --- .../admin-sidebar/admin-sidebar.component.ts | 1 - .../openaire-suggestion-source.model.ts | 2 +- .../openaire-suggestion-target.model.ts | 2 +- .../models/openaire-suggestion.model.ts | 2 +- .../openaire-suggestions-data.service.ts | 4 +- .../submission/workspaceitem-data.service.ts | 31 ++++++++++++- src/app/openaire/openaire.effects.ts | 5 ++ src/app/openaire/openaire.module.ts | 2 + .../openaire/reciter-suggestions/selectors.ts | 6 +-- .../suggestion-list-element.component.html | 1 - .../suggestion-targets.component.html | 1 - .../suggestion-targets.effects.ts | 2 +- .../suggestions.service.ts | 12 ++--- .../suggestions-page.component.ts | 5 +- src/config/app-config.interface.ts | 2 + src/config/default-app-config.ts | 9 ++++ src/config/layout-config.interfaces.ts | 46 +++++++++++++++++++ 17 files changed, 112 insertions(+), 21 deletions(-) create mode 100644 src/app/openaire/openaire.effects.ts create mode 100644 src/config/layout-config.interfaces.ts diff --git a/src/app/admin/admin-sidebar/admin-sidebar.component.ts b/src/app/admin/admin-sidebar/admin-sidebar.component.ts index a8c5ef800c7..b770e3d6753 100644 --- a/src/app/admin/admin-sidebar/admin-sidebar.component.ts +++ b/src/app/admin/admin-sidebar/admin-sidebar.component.ts @@ -42,7 +42,6 @@ import { Router, ActivatedRoute } from '@angular/router'; import {NOTIFICATIONS_RECITER_SUGGESTION_PATH} from "../admin-notifications/admin-notifications-routing-paths"; import { MenuID } from '../../shared/menu/menu-id.model'; import { MenuItemType } from '../../shared/menu/menu-item-type.model'; -import { ActivatedRoute } from '@angular/router'; /** * Component representing the admin sidebar diff --git a/src/app/core/openaire/reciter-suggestions/models/openaire-suggestion-source.model.ts b/src/app/core/openaire/reciter-suggestions/models/openaire-suggestion-source.model.ts index 6da9fd47b9b..d71b49b913f 100644 --- a/src/app/core/openaire/reciter-suggestions/models/openaire-suggestion-source.model.ts +++ b/src/app/core/openaire/reciter-suggestions/models/openaire-suggestion-source.model.ts @@ -1,11 +1,11 @@ import { autoserialize, deserialize } from 'cerialize'; -import { CacheableObject } from '../../../cache/object-cache.reducer'; import { SUGGESTION_SOURCE } from './openaire-suggestion-objects.resource-type'; 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 {CacheableObject} from "../../../cache/cacheable-object.model"; /** * The interface representing the Suggestion Source model diff --git a/src/app/core/openaire/reciter-suggestions/models/openaire-suggestion-target.model.ts b/src/app/core/openaire/reciter-suggestions/models/openaire-suggestion-target.model.ts index e35972bc798..06f85ea5b3c 100644 --- a/src/app/core/openaire/reciter-suggestions/models/openaire-suggestion-target.model.ts +++ b/src/app/core/openaire/reciter-suggestions/models/openaire-suggestion-target.model.ts @@ -1,11 +1,11 @@ import { autoserialize, deserialize } from 'cerialize'; -import { CacheableObject } from '../../../cache/object-cache.reducer'; import { SUGGESTION_TARGET } from './openaire-suggestion-objects.resource-type'; 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 {CacheableObject} from "../../../cache/cacheable-object.model"; /** * The interface representing the Suggestion Target model diff --git a/src/app/core/openaire/reciter-suggestions/models/openaire-suggestion.model.ts b/src/app/core/openaire/reciter-suggestions/models/openaire-suggestion.model.ts index 3ff5d7b630c..2d28ccf9bc3 100644 --- a/src/app/core/openaire/reciter-suggestions/models/openaire-suggestion.model.ts +++ b/src/app/core/openaire/reciter-suggestions/models/openaire-suggestion.model.ts @@ -1,12 +1,12 @@ import { autoserialize, autoserializeAs, deserialize } from 'cerialize'; -import { CacheableObject } from '../../../cache/object-cache.reducer'; import { SUGGESTION } from './openaire-suggestion-objects.resource-type'; 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 { MetadataMap, MetadataMapSerializer } from '../../../shared/metadata.models'; +import {CacheableObject} from "../../../cache/cacheable-object.model"; export interface SuggestionEvidences { [sectionId: string]: { diff --git a/src/app/core/openaire/reciter-suggestions/openaire-suggestions-data.service.ts b/src/app/core/openaire/reciter-suggestions/openaire-suggestions-data.service.ts index d961eaf9b52..7454caa1d93 100644 --- a/src/app/core/openaire/reciter-suggestions/openaire-suggestions-data.service.ts +++ b/src/app/core/openaire/reciter-suggestions/openaire-suggestions-data.service.ts @@ -4,14 +4,12 @@ import { Store } from '@ngrx/store'; import { Observable } from 'rxjs'; -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'; @@ -24,6 +22,8 @@ import { OpenaireSuggestionTarget } from './models/openaire-suggestion-target.mo import { OpenaireSuggestion } from './models/openaire-suggestion.model'; import { RequestParam } from '../../cache/models/request-param.model'; import { NoContent } from '../../shared/NoContent.model'; +import {CoreState} from "../../core-state.model"; +import {FindListOptions} from "../../data/find-list-options.model"; /* tslint:disable:max-classes-per-file */ diff --git a/src/app/core/submission/workspaceitem-data.service.ts b/src/app/core/submission/workspaceitem-data.service.ts index 1b9782834c0..a4dbaab6ca5 100644 --- a/src/app/core/submission/workspaceitem-data.service.ts +++ b/src/app/core/submission/workspaceitem-data.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { HttpClient } from '@angular/common/http'; +import {HttpClient, HttpHeaders} from '@angular/common/http'; import { Store } from '@ngrx/store'; import { dataService } from '../cache/builders/build-decorators'; @@ -17,6 +17,10 @@ import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { RequestParam } from '../cache/models/request-param.model'; import { CoreState } from '../core-state.model'; import { FindListOptions } from '../data/find-list-options.model'; +import {HttpOptions} from "../dspace-rest/dspace-rest.service"; +import {find, map} from "rxjs/operators"; +import {PostRequest} from "../data/request.models"; +import {hasValue} from "../../shared/empty.util"; /** * A service that provides methods to make REST requests with workspaceitems endpoint. @@ -57,4 +61,29 @@ export class WorkspaceitemDataService extends DataService { return this.findByHref(href$, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow); } + /** + * Import an external source entry into a collection + * @param externalSourceEntryHref + * @param collectionId + */ + public importExternalSourceEntry(externalSourceEntryHref: string, collectionId: string): Observable> { + const options: HttpOptions = Object.create({}); + let headers = new HttpHeaders(); + headers = headers.append('Content-Type', 'text/uri-list'); + options.headers = headers; + + const requestId = this.requestService.generateRequestId(); + const href$ = this.halService.getEndpoint(this.linkPath).pipe(map((href) => `${href}?owningCollection=${collectionId}`)); + + href$.pipe( + find((href: string) => hasValue(href)), + map((href: string) => { + const request = new PostRequest(requestId, href, externalSourceEntryHref, options); + this.requestService.send(request); + }) + ).subscribe(); + + return this.rdbService.buildFromRequestUUID(requestId); + } + } diff --git a/src/app/openaire/openaire.effects.ts b/src/app/openaire/openaire.effects.ts new file mode 100644 index 00000000000..d771089f92b --- /dev/null +++ b/src/app/openaire/openaire.effects.ts @@ -0,0 +1,5 @@ +import { SuggestionTargetsEffects } from './reciter-suggestions/suggestion-targets/suggestion-targets.effects'; + +export const openaireEffects = [ + SuggestionTargetsEffects +]; diff --git a/src/app/openaire/openaire.module.ts b/src/app/openaire/openaire.module.ts index 22d04f3002b..ace4622190f 100644 --- a/src/app/openaire/openaire.module.ts +++ b/src/app/openaire/openaire.module.ts @@ -18,12 +18,14 @@ import { SuggestionsPopupComponent } from './reciter-suggestions/suggestions-pop import { SuggestionsNotificationComponent } from './reciter-suggestions/suggestions-notification/suggestions-notification.component'; import { TranslateModule } from '@ngx-translate/core'; import { SearchModule } from '../shared/search/search.module'; +import {openaireEffects} from "./openaire.effects"; const MODULES = [ CommonModule, SharedModule, CoreModule.forRoot(), StoreModule.forFeature('openaire', openaireReducers, storeModuleConfig as StoreConfig), + EffectsModule.forFeature(openaireEffects), TranslateModule ]; diff --git a/src/app/openaire/reciter-suggestions/selectors.ts b/src/app/openaire/reciter-suggestions/selectors.ts index e699b27dbae..292e63472a7 100644 --- a/src/app/openaire/reciter-suggestions/selectors.ts +++ b/src/app/openaire/reciter-suggestions/selectors.ts @@ -1,8 +1,8 @@ -import { createSelector, MemoizedSelector } from '@ngrx/store'; -import { subStateSelector } from '../../shared/selector.util'; +import {createFeatureSelector, createSelector, MemoizedSelector} from '@ngrx/store'; import { openaireSelector, OpenaireState } from '../openaire.reducer'; import { OpenaireSuggestionTarget } from '../../core/openaire/reciter-suggestions/models/openaire-suggestion-target.model'; import { SuggestionTargetState } from './suggestion-targets/suggestion-targets.reducer'; +import {subStateSelector} from "../../submission/selectors"; /** * Returns the Reciter Suggestion Target state. @@ -10,7 +10,7 @@ import { SuggestionTargetState } from './suggestion-targets/suggestion-targets.r * @param {AppState} state Top level state. * @return {OpenaireState} */ -const _getReciterSuggestionTargetState = (state: any) => state.openaire; +const _getReciterSuggestionTargetState = createFeatureSelector('openaire'); // Reciter Suggestion Targets // ---------------------------------------------------------------------------- diff --git a/src/app/openaire/reciter-suggestions/suggestion-list-element/suggestion-list-element.component.html b/src/app/openaire/reciter-suggestions/suggestion-list-element/suggestion-list-element.component.html index 05f9c0ac771..f37d595c45b 100644 --- a/src/app/openaire/reciter-suggestions/suggestion-list-element/suggestion-list-element.component.html +++ b/src/app/openaire/reciter-suggestions/suggestion-list-element/suggestion-list-element.component.html @@ -21,7 +21,6 @@ [showLabel]="false" [object]="listableObject" [linkType]="0" - [hideMetrics]="true" > {{'reciter.suggestion.title'| transla [paginationOptions]="paginationConfig" [collectionSize]="(totalElements$ | async)" [hideGear]="false" - [hideSortOptions]="true" [retainScrollPosition]="false" (paginationChange)="getSuggestionTargets()"> diff --git a/src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.effects.ts b/src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.effects.ts index 85e871403c4..9a007fab214 100644 --- a/src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.effects.ts +++ b/src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.effects.ts @@ -81,7 +81,7 @@ export class SuggestionTargetsEffects { return this.store$.select((state: any) => state.core.auth.user) .pipe( switchMap((user: EPerson) => { - return this.suggestionsService.retrieveCurrentUserSuggestions(user) + return this.suggestionsService.retrieveCurrentUserSuggestions(user.uuid) .pipe( map((suggestionTargets: OpenaireSuggestionTarget[]) => new AddUserSuggestionsAction(suggestionTargets)), catchError((errors) => of(errors)) diff --git a/src/app/openaire/reciter-suggestions/suggestions.service.ts b/src/app/openaire/reciter-suggestions/suggestions.service.ts index de743d95e34..5908c8c6bff 100644 --- a/src/app/openaire/reciter-suggestions/suggestions.service.ts +++ b/src/app/openaire/reciter-suggestions/suggestions.service.ts @@ -5,7 +5,6 @@ import { catchError, map, mergeMap, take } from 'rxjs/operators'; import { OpenaireSuggestionsDataService } from '../../core/openaire/reciter-suggestions/openaire-suggestions-data.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 { OpenaireSuggestionTarget } from '../../core/openaire/reciter-suggestions/models/openaire-suggestion-target.model'; @@ -25,8 +24,9 @@ import { WorkspaceitemDataService } from '../../core/submission/workspaceitem-da import { TranslateService } from '@ngx-translate/core'; import { NoContent } from '../../core/shared/NoContent.model'; import { environment } from '../../../environments/environment'; -import { SuggestionConfig } from '../../../config/layout-config.interfaces'; import { WorkspaceItem } from '../../core/submission/models/workspaceitem.model'; +import {FindListOptions} from "../../core/data/find-list-options.model"; +import {SuggestionConfig} from "../../../config/layout-config.interfaces"; export interface SuggestionBulkResult { success: number; @@ -142,11 +142,11 @@ export class SuggestionsService { /** * Retrieve suggestion targets for the given user * - * @param user - * The EPerson object for which to retrieve suggestion targets + * @param userUuid + * The EPerson id for which to retrieve suggestion targets */ - public retrieveCurrentUserSuggestions(user: EPerson): Observable { - return this.researcherProfileService.findById(user.uuid).pipe( + public retrieveCurrentUserSuggestions(userUuid: string): Observable { + return this.researcherProfileService.findById(userUuid).pipe( mergeMap((profile: ResearcherProfile) => { if (isNotEmpty(profile)) { return this.researcherProfileService.findRelatedItemId(profile).pipe( diff --git a/src/app/suggestions-page/suggestions-page.component.ts b/src/app/suggestions-page/suggestions-page.component.ts index 911f6167de5..850a30da221 100644 --- a/src/app/suggestions-page/suggestions-page.component.ts +++ b/src/app/suggestions-page/suggestions-page.component.ts @@ -8,7 +8,7 @@ import { TranslateService } from '@ngx-translate/core'; import { SortDirection, SortOptions, } from '../core/cache/models/sort-options.model'; import { PaginatedList } from '../core/data/paginated-list.model'; import { RemoteData } from '../core/data/remote-data'; -import { getFirstSucceededRemoteDataPayload, redirectOn4xx } from '../core/shared/operators'; +import { getFirstSucceededRemoteDataPayload } from '../core/shared/operators'; import { SuggestionBulkResult, SuggestionsService } from '../openaire/reciter-suggestions/suggestions.service'; import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model'; import { OpenaireSuggestion } from '../core/openaire/reciter-suggestions/models/openaire-suggestion.model'; @@ -19,8 +19,9 @@ import { NotificationsService } from '../shared/notifications/notifications.serv import { SuggestionTargetsStateService } from '../openaire/reciter-suggestions/suggestion-targets/suggestion-targets.state.service'; import { WorkspaceitemDataService } from '../core/submission/workspaceitem-data.service'; import { PaginationService } from '../core/pagination/pagination.service'; -import { FindListOptions } from '../core/data/request.models'; import { WorkspaceItem } from '../core/submission/models/workspaceitem.model'; +import {FindListOptions} from "../core/data/find-list-options.model"; +import {redirectOn4xx} from "../core/shared/authorized.operators"; @Component({ selector: 'ds-suggestion-page', diff --git a/src/config/app-config.interface.ts b/src/config/app-config.interface.ts index 121e80cd74b..6446e7f127f 100644 --- a/src/config/app-config.interface.ts +++ b/src/config/app-config.interface.ts @@ -14,6 +14,7 @@ import { AuthConfig } from './auth-config.interfaces'; import { UIServerConfig } from './ui-server-config.interface'; import { MediaViewerConfig } from './media-viewer-config.interface'; import { BrowseByConfig } from './browse-by-config.interface'; +import {SuggestionConfig} from "./layout-config.interfaces"; interface AppConfig extends Config { ui: UIServerConfig; @@ -32,6 +33,7 @@ interface AppConfig extends Config { collection: CollectionPageConfig; themes: ThemeConfig[]; mediaViewer: MediaViewerConfig; + suggestion: SuggestionConfig[]; } const APP_CONFIG = new InjectionToken('APP_CONFIG'); diff --git a/src/config/default-app-config.ts b/src/config/default-app-config.ts index dc54c2fcb0f..aea29fc819e 100644 --- a/src/config/default-app-config.ts +++ b/src/config/default-app-config.ts @@ -14,6 +14,7 @@ import { ServerConfig } from './server-config.interface'; import { SubmissionConfig } from './submission-config.interface'; import { ThemeConfig } from './theme.model'; import { UIServerConfig } from './ui-server-config.interface'; +import {SuggestionConfig} from "./layout-config.interfaces"; export class DefaultAppConfig implements AppConfig { production = false; @@ -210,6 +211,14 @@ export class DefaultAppConfig implements AppConfig { } }; + suggestion: SuggestionConfig[] = [ + // { + // // Use this configuration to map a suggestion import to a specific collection based on the suggestion type. + // source: 'suggestionSource', + // collectionId: 'collectionUUID' + // } + ]; + // Theme Config themes: ThemeConfig[] = [ // Add additional themes here. In the case where multiple themes match a route, the first one diff --git a/src/config/layout-config.interfaces.ts b/src/config/layout-config.interfaces.ts new file mode 100644 index 00000000000..0b15a06aa92 --- /dev/null +++ b/src/config/layout-config.interfaces.ts @@ -0,0 +1,46 @@ +import { Config } from './config.interface'; + +export interface UrnConfig extends Config { + name: string; + baseUrl: string; +} + +export interface CrisRefConfig extends Config { + entityType: string; + icon: string; +} + +export interface CrisLayoutMetadataBoxConfig extends Config { + defaultMetadataLabelColStyle: string; + defaultMetadataValueColStyle: string; +} + +export interface CrisLayoutTypeConfig { + orientation: string; +} + +export interface NavbarConfig extends Config { + showCommunityCollection: boolean; +} + +export interface CrisItemPageConfig extends Config { + [entity: string]: CrisLayoutTypeConfig; + default: CrisLayoutTypeConfig; +} + + +export interface CrisLayoutConfig extends Config { + urn: UrnConfig[]; + crisRef: CrisRefConfig[]; + itemPage: CrisItemPageConfig; + metadataBox: CrisLayoutMetadataBoxConfig; +} + +export interface LayoutConfig extends Config { + navbar: NavbarConfig; +} + +export interface SuggestionConfig extends Config { + source: string; + collectionId: string; +} From 11d25e66997d99bd1cb932c057feff3f8dc30e70 Mon Sep 17 00:00:00 2001 From: Luca Giamminonni Date: Wed, 4 May 2022 13:16:13 +0200 Subject: [PATCH 03/34] [CST-5249] Fixed suggestion page --- .../admin-sidebar/admin-sidebar.component.ts | 2 +- src/app/core/core.module.ts | 2 +- .../openaire-suggestion-source.model.ts | 2 +- .../openaire-suggestion-target.model.ts | 2 +- .../models/openaire-suggestion.model.ts | 2 +- .../openaire-suggestions-data.service.ts | 5 +- .../submission/workspaceitem-data.service.ts | 8 +- src/app/openaire/openaire.module.ts | 2 +- .../openaire/reciter-suggestions/selectors.ts | 2 +- .../suggestion-targets.actions.ts | 1 + .../suggestion-targets.effects.ts | 6 +- .../suggestions.service.ts | 4 +- .../dso-selector-modal-wrapper.component.ts | 24 +- src/app/shared/mocks/openaire.mock.ts | 445 ------------------ .../suggestions-page.component.ts | 4 +- src/assets/i18n/en.json5 | 1 + src/config/app-config.interface.ts | 2 +- src/config/default-app-config.ts | 2 +- src/environments/environment.test.ts | 8 + 19 files changed, 55 insertions(+), 469 deletions(-) diff --git a/src/app/admin/admin-sidebar/admin-sidebar.component.ts b/src/app/admin/admin-sidebar/admin-sidebar.component.ts index b770e3d6753..c13599be9da 100644 --- a/src/app/admin/admin-sidebar/admin-sidebar.component.ts +++ b/src/app/admin/admin-sidebar/admin-sidebar.component.ts @@ -39,7 +39,7 @@ import { CSSVariableService } from '../../shared/sass-helper/sass-helper.service import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; import { Router, ActivatedRoute } from '@angular/router'; -import {NOTIFICATIONS_RECITER_SUGGESTION_PATH} from "../admin-notifications/admin-notifications-routing-paths"; +import {NOTIFICATIONS_RECITER_SUGGESTION_PATH} from '../admin-notifications/admin-notifications-routing-paths'; import { MenuID } from '../../shared/menu/menu-id.model'; import { MenuItemType } from '../../shared/menu/menu-item-type.model'; diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index 7273996401c..879ba76c7df 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -168,7 +168,7 @@ import { OpenaireSuggestionSource } from './openaire/reciter-suggestions/models/ import { ResearcherProfileService } from './profile/researcher-profile.service'; import { ProfileClaimService } from '../profile-page/profile-claim/profile-claim.service'; import { ResearcherProfile } from './profile/model/researcher-profile.model'; -import {SubmissionAccessesModel} from "./config/models/config-submission-accesses.model"; +import {SubmissionAccessesModel} from './config/models/config-submission-accesses.model'; /** * When not in production, endpoint responses can be mocked for testing purposes diff --git a/src/app/core/openaire/reciter-suggestions/models/openaire-suggestion-source.model.ts b/src/app/core/openaire/reciter-suggestions/models/openaire-suggestion-source.model.ts index d71b49b913f..00f5f11936b 100644 --- a/src/app/core/openaire/reciter-suggestions/models/openaire-suggestion-source.model.ts +++ b/src/app/core/openaire/reciter-suggestions/models/openaire-suggestion-source.model.ts @@ -5,7 +5,7 @@ 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 {CacheableObject} from "../../../cache/cacheable-object.model"; +import {CacheableObject} from '../../../cache/cacheable-object.model'; /** * The interface representing the Suggestion Source model diff --git a/src/app/core/openaire/reciter-suggestions/models/openaire-suggestion-target.model.ts b/src/app/core/openaire/reciter-suggestions/models/openaire-suggestion-target.model.ts index 06f85ea5b3c..96a43c96544 100644 --- a/src/app/core/openaire/reciter-suggestions/models/openaire-suggestion-target.model.ts +++ b/src/app/core/openaire/reciter-suggestions/models/openaire-suggestion-target.model.ts @@ -5,7 +5,7 @@ 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 {CacheableObject} from "../../../cache/cacheable-object.model"; +import {CacheableObject} from '../../../cache/cacheable-object.model'; /** * The interface representing the Suggestion Target model diff --git a/src/app/core/openaire/reciter-suggestions/models/openaire-suggestion.model.ts b/src/app/core/openaire/reciter-suggestions/models/openaire-suggestion.model.ts index 2d28ccf9bc3..0f84072c6ba 100644 --- a/src/app/core/openaire/reciter-suggestions/models/openaire-suggestion.model.ts +++ b/src/app/core/openaire/reciter-suggestions/models/openaire-suggestion.model.ts @@ -6,7 +6,7 @@ import { ResourceType } from '../../../shared/resource-type'; import { HALLink } from '../../../shared/hal-link.model'; import { typedObject } from '../../../cache/builders/build-decorators'; import { MetadataMap, MetadataMapSerializer } from '../../../shared/metadata.models'; -import {CacheableObject} from "../../../cache/cacheable-object.model"; +import {CacheableObject} from '../../../cache/cacheable-object.model'; export interface SuggestionEvidences { [sectionId: string]: { diff --git a/src/app/core/openaire/reciter-suggestions/openaire-suggestions-data.service.ts b/src/app/core/openaire/reciter-suggestions/openaire-suggestions-data.service.ts index 7454caa1d93..3c1d04c0404 100644 --- a/src/app/core/openaire/reciter-suggestions/openaire-suggestions-data.service.ts +++ b/src/app/core/openaire/reciter-suggestions/openaire-suggestions-data.service.ts @@ -1,3 +1,4 @@ +/* eslint-disable max-classes-per-file */ import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Store } from '@ngrx/store'; @@ -22,8 +23,8 @@ import { OpenaireSuggestionTarget } from './models/openaire-suggestion-target.mo import { OpenaireSuggestion } from './models/openaire-suggestion.model'; import { RequestParam } from '../../cache/models/request-param.model'; import { NoContent } from '../../shared/NoContent.model'; -import {CoreState} from "../../core-state.model"; -import {FindListOptions} from "../../data/find-list-options.model"; +import {CoreState} from '../../core-state.model'; +import {FindListOptions} from '../../data/find-list-options.model'; /* tslint:disable:max-classes-per-file */ diff --git a/src/app/core/submission/workspaceitem-data.service.ts b/src/app/core/submission/workspaceitem-data.service.ts index a4dbaab6ca5..6c0e909bb48 100644 --- a/src/app/core/submission/workspaceitem-data.service.ts +++ b/src/app/core/submission/workspaceitem-data.service.ts @@ -17,10 +17,10 @@ import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { RequestParam } from '../cache/models/request-param.model'; import { CoreState } from '../core-state.model'; import { FindListOptions } from '../data/find-list-options.model'; -import {HttpOptions} from "../dspace-rest/dspace-rest.service"; -import {find, map} from "rxjs/operators"; -import {PostRequest} from "../data/request.models"; -import {hasValue} from "../../shared/empty.util"; +import {HttpOptions} from '../dspace-rest/dspace-rest.service'; +import {find, map} from 'rxjs/operators'; +import {PostRequest} from '../data/request.models'; +import {hasValue} from '../../shared/empty.util'; /** * A service that provides methods to make REST requests with workspaceitems endpoint. diff --git a/src/app/openaire/openaire.module.ts b/src/app/openaire/openaire.module.ts index ace4622190f..8ae52b27f4b 100644 --- a/src/app/openaire/openaire.module.ts +++ b/src/app/openaire/openaire.module.ts @@ -18,7 +18,7 @@ import { SuggestionsPopupComponent } from './reciter-suggestions/suggestions-pop import { SuggestionsNotificationComponent } from './reciter-suggestions/suggestions-notification/suggestions-notification.component'; import { TranslateModule } from '@ngx-translate/core'; import { SearchModule } from '../shared/search/search.module'; -import {openaireEffects} from "./openaire.effects"; +import {openaireEffects} from './openaire.effects'; const MODULES = [ CommonModule, diff --git a/src/app/openaire/reciter-suggestions/selectors.ts b/src/app/openaire/reciter-suggestions/selectors.ts index 292e63472a7..cfbb36d4c2a 100644 --- a/src/app/openaire/reciter-suggestions/selectors.ts +++ b/src/app/openaire/reciter-suggestions/selectors.ts @@ -2,7 +2,7 @@ import {createFeatureSelector, createSelector, MemoizedSelector} from '@ngrx/sto import { openaireSelector, OpenaireState } from '../openaire.reducer'; import { OpenaireSuggestionTarget } from '../../core/openaire/reciter-suggestions/models/openaire-suggestion-target.model'; import { SuggestionTargetState } from './suggestion-targets/suggestion-targets.reducer'; -import {subStateSelector} from "../../submission/selectors"; +import {subStateSelector} from '../../submission/selectors'; /** * Returns the Reciter Suggestion Target state. diff --git a/src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.actions.ts b/src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.actions.ts index 6c44d40b915..33dfb1474b2 100644 --- a/src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.actions.ts +++ b/src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.actions.ts @@ -1,3 +1,4 @@ +/* eslint-disable max-classes-per-file */ import { Action } from '@ngrx/store'; import { type } from '../../../shared/ngrx/type'; import { OpenaireSuggestionTarget } from '../../../core/openaire/reciter-suggestions/models/openaire-suggestion-target.model'; diff --git a/src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.effects.ts b/src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.effects.ts index 9a007fab214..29672ad49b8 100644 --- a/src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.effects.ts +++ b/src/app/openaire/reciter-suggestions/suggestion-targets/suggestion-targets.effects.ts @@ -78,10 +78,10 @@ export class SuggestionTargetsEffects { @Effect() refreshUserTargets$ = this.actions$.pipe( ofType(SuggestionTargetActionTypes.REFRESH_USER_SUGGESTIONS), switchMap((action: RefreshUserSuggestionsAction) => { - return this.store$.select((state: any) => state.core.auth.user) + return this.store$.select((state: any) => state.core.auth.userId) .pipe( - switchMap((user: EPerson) => { - return this.suggestionsService.retrieveCurrentUserSuggestions(user.uuid) + switchMap((userId: string) => { + return this.suggestionsService.retrieveCurrentUserSuggestions(userId) .pipe( map((suggestionTargets: OpenaireSuggestionTarget[]) => new AddUserSuggestionsAction(suggestionTargets)), catchError((errors) => of(errors)) diff --git a/src/app/openaire/reciter-suggestions/suggestions.service.ts b/src/app/openaire/reciter-suggestions/suggestions.service.ts index 5908c8c6bff..67b496b903a 100644 --- a/src/app/openaire/reciter-suggestions/suggestions.service.ts +++ b/src/app/openaire/reciter-suggestions/suggestions.service.ts @@ -25,8 +25,8 @@ import { TranslateService } from '@ngx-translate/core'; import { NoContent } from '../../core/shared/NoContent.model'; import { environment } from '../../../environments/environment'; import { WorkspaceItem } from '../../core/submission/models/workspaceitem.model'; -import {FindListOptions} from "../../core/data/find-list-options.model"; -import {SuggestionConfig} from "../../../config/layout-config.interfaces"; +import {FindListOptions} from '../../core/data/find-list-options.model'; +import {SuggestionConfig} from '../../../config/layout-config.interfaces'; export interface SuggestionBulkResult { success: number; diff --git a/src/app/shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component.ts b/src/app/shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component.ts index ca8343cfad4..3b3ef59ae51 100644 --- a/src/app/shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component.ts +++ b/src/app/shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit } from '@angular/core'; +import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; import { ActivatedRoute, ActivatedRouteSnapshot } from '@angular/router'; import { DSpaceObject } from '../../../core/shared/dspace-object.model'; import { RemoteData } from '../../../core/data/remote-data'; @@ -21,11 +21,22 @@ export enum SelectorActionType { template: '' }) export abstract class DSOSelectorModalWrapperComponent implements OnInit { + + /** + * The discovery configuration. + */ + @Input() configuration = 'default'; + /** * The current page's DSO */ @Input() dsoRD: RemoteData; + /** + * Representing if component should emit value of selected entries or navigate + */ + @Input() emitOnly = false; + /** * Optional header to display above the selection list * Supports i18n keys @@ -47,6 +58,11 @@ export abstract class DSOSelectorModalWrapperComponent implements OnInit { */ action: SelectorActionType; + /** + * Event emitted when a DSO entry is selected if emitOnly is set to true + */ + @Output() select: EventEmitter = new EventEmitter(); + constructor(protected activeModal: NgbActiveModal, protected route: ActivatedRoute) { } @@ -85,7 +101,11 @@ export abstract class DSOSelectorModalWrapperComponent implements OnInit { */ selectObject(dso: DSpaceObject) { this.close(); - this.navigate(dso); + if (this.emitOnly) { + this.select.emit(dso); + } else { + this.navigate(dso); + } } /** diff --git a/src/app/shared/mocks/openaire.mock.ts b/src/app/shared/mocks/openaire.mock.ts index 95ea8717274..d6c50510cd5 100644 --- a/src/app/shared/mocks/openaire.mock.ts +++ b/src/app/shared/mocks/openaire.mock.ts @@ -1,17 +1,5 @@ -import { of as observableOf } from 'rxjs'; -import { ResourceType } from '../../core/shared/resource-type'; -import { OpenaireBrokerTopicObject } from '../../core/openaire/broker/models/openaire-broker-topic.model'; -import { OpenaireBrokerEventObject } from '../../core/openaire/broker/models/openaire-broker-event.model'; -import { OpenaireBrokerTopicRestService } from '../../core/openaire/broker/topics/openaire-broker-topic-rest.service'; -import { OpenaireBrokerEventRestService } from '../../core/openaire/broker/events/openaire-broker-event-rest.service'; import { DSpaceObject } from '../../core/shared/dspace-object.model'; -import { OpenaireStateService } from '../../openaire/openaire-state.service'; import { Item } from '../../core/shared/item.model'; -import { - createNoContentRemoteDataObject$, - createSuccessfulRemoteDataObject, - createSuccessfulRemoteDataObject$ -} from '../remote-data.utils'; import { SearchResult } from '../search/models/search-result.model'; import { SuggestionsService } from '../../openaire/reciter-suggestions/suggestions.service'; @@ -1330,414 +1318,6 @@ export const OpenaireMockDspaceObject: SearchResult = Object.assig } ); -// Topics -// ------------------------------------------------------------------------------- - -export const openaireBrokerTopicObjectMorePid: OpenaireBrokerTopicObject = { - type: new ResourceType('nbtopic'), - id: 'ENRICH!MORE!PID', - name: 'ENRICH/MORE/PID', - lastEvent: '2020/10/09 10:11 UTC', - totalEvents: 33, - _links: { - self: { - href: 'https://rest.api/rest/api/integration/nbtopics/ENRICH!MORE!PID' - } - } -}; - -export const openaireBrokerTopicObjectMoreAbstract: OpenaireBrokerTopicObject = { - type: new ResourceType('nbtopic'), - id: 'ENRICH!MORE!ABSTRACT', - name: 'ENRICH/MORE/ABSTRACT', - lastEvent: '2020/09/08 21:14 UTC', - totalEvents: 5, - _links: { - self: { - href: 'https://rest.api/rest/api/integration/nbtopics/ENRICH!MORE!ABSTRACT' - } - } -}; - -export const openaireBrokerTopicObjectMissingPid: OpenaireBrokerTopicObject = { - type: new ResourceType('nbtopic'), - id: 'ENRICH!MISSING!PID', - name: 'ENRICH/MISSING/PID', - lastEvent: '2020/10/01 07:36 UTC', - totalEvents: 4, - _links: { - self: { - href: 'https://rest.api/rest/api/integration/nbtopics/ENRICH!MISSING!PID' - } - } -}; - -export const openaireBrokerTopicObjectMissingAbstract: OpenaireBrokerTopicObject = { - type: new ResourceType('nbtopic'), - id: 'ENRICH!MISSING!ABSTRACT', - name: 'ENRICH/MISSING/ABSTRACT', - lastEvent: '2020/10/08 16:14 UTC', - totalEvents: 71, - _links: { - self: { - href: 'https://rest.api/rest/api/integration/nbtopics/ENRICH!MISSING!ABSTRACT' - } - } -}; - -export const openaireBrokerTopicObjectMissingAcm: OpenaireBrokerTopicObject = { - type: new ResourceType('nbtopic'), - id: 'ENRICH!MISSING!SUBJECT!ACM', - name: 'ENRICH/MISSING/SUBJECT/ACM', - lastEvent: '2020/09/21 17:51 UTC', - totalEvents: 18, - _links: { - self: { - href: 'https://rest.api/rest/api/integration/nbtopics/ENRICH!MISSING!SUBJECT!ACM' - } - } -}; - -export const openaireBrokerTopicObjectMissingProject: OpenaireBrokerTopicObject = { - type: new ResourceType('nbtopic'), - id: 'ENRICH!MISSING!PROJECT', - name: 'ENRICH/MISSING/PROJECT', - lastEvent: '2020/09/17 10:28 UTC', - totalEvents: 6, - _links: { - self: { - href: 'https://rest.api/rest/api/integration/nbtopics/ENRICH!MISSING!PROJECT' - } - } -}; - -// Events -// ------------------------------------------------------------------------------- - -export const openaireBrokerEventObjectMissingPid: OpenaireBrokerEventObject = { - id: '123e4567-e89b-12d3-a456-426614174001', - uuid: '123e4567-e89b-12d3-a456-426614174001', - type: new ResourceType('nbevent'), - originalId: 'oai:www.openstarts.units.it:10077/21486', - title: 'Index nominum et rerum', - trust: 0.375, - eventDate: '2020/10/09 10:11 UTC', - status: 'PENDING', - message: { - type: 'doi', - value: '10.18848/1447-9494/cgp/v15i09/45934', - abstract: null, - openaireId: null, - acronym: null, - code: null, - funder: null, - fundingProgram: null, - jurisdiction: null, - title: null - }, - _links: { - self: { - href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174001', - }, - target: { - href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174001/target' - }, - related: { - href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174001/related' - } - }, - target: observableOf(createSuccessfulRemoteDataObject(ItemMockPid1)), - related: observableOf(createSuccessfulRemoteDataObject(ItemMockPid10)) -}; - -export const openaireBrokerEventObjectMissingPid2: OpenaireBrokerEventObject = { - id: '123e4567-e89b-12d3-a456-426614174004', - uuid: '123e4567-e89b-12d3-a456-426614174004', - type: new ResourceType('openaireBrokerEvent'), - originalId: 'oai:www.openstarts.units.it:10077/21486', - title: 'UNA NUOVA RILETTURA DELL\u0027 ARISTOTELE DI FRANZ BRENTANO ALLA LUCE DI ALCUNI INEDITI', - trust: 1.0, - eventDate: '2020/10/09 10:11 UTC', - status: 'PENDING', - message: { - type: 'urn', - value: 'http://thesis2.sba.units.it/store/handle/item/12238', - abstract: null, - openaireId: null, - acronym: null, - code: null, - funder: null, - fundingProgram: null, - jurisdiction: null, - title: null - }, - _links: { - self: { - href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174004' - }, - target: { - href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174004/target' - }, - related: { - href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174004/related' - } - }, - target: observableOf(createSuccessfulRemoteDataObject(ItemMockPid2)), - related: observableOf(createSuccessfulRemoteDataObject(ItemMockPid10)) -}; - -export const openaireBrokerEventObjectMissingPid3: OpenaireBrokerEventObject = { - id: '123e4567-e89b-12d3-a456-426614174005', - uuid: '123e4567-e89b-12d3-a456-426614174005', - type: new ResourceType('openaireBrokerEvent'), - originalId: 'oai:www.openstarts.units.it:10077/554', - title: 'Sustainable development', - trust: 0.375, - eventDate: '2020/10/09 10:11 UTC', - status: 'PENDING', - message: { - type: 'doi', - value: '10.4324/9780203408889', - abstract: null, - openaireId: null, - acronym: null, - code: null, - funder: null, - fundingProgram: null, - jurisdiction: null, - title: null - }, - _links: { - self: { - href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174005' - }, - target: { - href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174005/target' - }, - related: { - href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174005/related' - } - }, - target: observableOf(createSuccessfulRemoteDataObject(ItemMockPid3)), - related: observableOf(createSuccessfulRemoteDataObject(ItemMockPid10)) -}; - -export const openaireBrokerEventObjectMissingPid4: OpenaireBrokerEventObject = { - id: '123e4567-e89b-12d3-a456-426614174006', - uuid: '123e4567-e89b-12d3-a456-426614174006', - type: new ResourceType('openaireBrokerEvent'), - originalId: 'oai:www.openstarts.units.it:10077/10787', - title: 'Reply to Critics', - trust: 1.0, - eventDate: '2020/10/09 10:11 UTC', - status: 'PENDING', - message: { - type: 'doi', - value: '10.1080/13698230.2018.1430104', - abstract: null, - openaireId: null, - acronym: null, - code: null, - funder: null, - fundingProgram: null, - jurisdiction: null, - title: null - }, - _links: { - self: { - href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174006' - }, - target: { - href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174006/target' - }, - related: { - href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174006/related' - } - }, - target: observableOf(createSuccessfulRemoteDataObject(ItemMockPid4)), - related: observableOf(createSuccessfulRemoteDataObject(ItemMockPid10)) -}; - -export const openaireBrokerEventObjectMissingPid5: OpenaireBrokerEventObject = { - id: '123e4567-e89b-12d3-a456-426614174007', - uuid: '123e4567-e89b-12d3-a456-426614174007', - type: new ResourceType('openaireBrokerEvent'), - originalId: 'oai:www.openstarts.units.it:10077/11339', - title: 'PROGETTAZIONE, SINTESI E VALUTAZIONE DELL\u0027ATTIVITA\u0027 ANTIMICOBATTERICA ED ANTIFUNGINA DI NUOVI DERIVATI ETEROCICLICI', - trust: 0.375, - eventDate: '2020/10/09 10:11 UTC', - status: 'PENDING', - message: { - type: 'urn', - value: 'http://thesis2.sba.units.it/store/handle/item/12477', - abstract: null, - openaireId: null, - acronym: null, - code: null, - funder: null, - fundingProgram: null, - jurisdiction: null, - title: null - }, - _links: { - self: { - href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174007' - }, - target: { - href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174007/target' - }, - related: { - href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174007/related' - } - }, - target: observableOf(createSuccessfulRemoteDataObject(ItemMockPid5)), - related: observableOf(createSuccessfulRemoteDataObject(ItemMockPid10)) -}; - -export const openaireBrokerEventObjectMissingPid6: OpenaireBrokerEventObject = { - id: '123e4567-e89b-12d3-a456-426614174008', - uuid: '123e4567-e89b-12d3-a456-426614174008', - type: new ResourceType('openaireBrokerEvent'), - originalId: 'oai:www.openstarts.units.it:10077/29860', - title: 'Donald Davidson', - trust: 0.375, - eventDate: '2020/10/09 10:11 UTC', - status: 'PENDING', - message: { - type: 'doi', - value: '10.1111/j.1475-4975.2004.00098.x', - abstract: null, - openaireId: null, - acronym: null, - code: null, - funder: null, - fundingProgram: null, - jurisdiction: null, - title: null - }, - _links: { - self: { - href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174008' - }, - target: { - href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174008/target' - }, - related: { - href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174008/related' - } - }, - target: observableOf(createSuccessfulRemoteDataObject(ItemMockPid6)), - related: observableOf(createSuccessfulRemoteDataObject(ItemMockPid10)) -}; - -export const openaireBrokerEventObjectMissingAbstract: OpenaireBrokerEventObject = { - id: '123e4567-e89b-12d3-a456-426614174009', - uuid: '123e4567-e89b-12d3-a456-426614174009', - type: new ResourceType('openaireBrokerEvent'), - originalId: 'oai:www.openstarts.units.it:10077/21110', - title: 'Missing abstract article', - trust: 0.751, - eventDate: '2020/10/09 10:11 UTC', - status: 'PENDING', - message: { - type: null, - value: null, - abstract: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla scelerisque vestibulum tellus sed lacinia. Aenean vitae sapien a quam congue ultrices. Sed vehicula sollicitudin ligula, vitae lacinia velit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla scelerisque vestibulum tellus sed lacinia. Aenean vitae sapien a quam congue ultrices. Sed vehicula sollicitudin ligula, vitae lacinia velit.', - openaireId: null, - acronym: null, - code: null, - funder: null, - fundingProgram: null, - jurisdiction: null, - title: null - }, - _links: { - self: { - href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174009' - }, - target: { - href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174009/target' - }, - related: { - href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174009/related' - } - }, - target: observableOf(createSuccessfulRemoteDataObject(ItemMockPid7)), - related: observableOf(createSuccessfulRemoteDataObject(ItemMockPid10)) -}; - -export const openaireBrokerEventObjectMissingProjectFound: OpenaireBrokerEventObject = { - id: '123e4567-e89b-12d3-a456-426614174002', - uuid: '123e4567-e89b-12d3-a456-426614174002', - type: new ResourceType('openaireBrokerEvent'), - originalId: 'oai:www.openstarts.units.it:10077/21838', - title: 'Egypt, crossroad of translations and literary interweavings (3rd-6th centuries). A reconsideration of earlier Coptic literature', - trust: 1.0, - eventDate: '2020/10/09 10:11 UTC', - status: 'PENDING', - message: { - type: null, - value: null, - abstract: null, - openaireId: null, - acronym: 'PAThs', - code: '687567', - funder: 'EC', - fundingProgram: 'H2020', - jurisdiction: 'EU', - title: 'Tracking Papyrus and Parchment Paths: An Archaeological Atlas of Coptic Literature.\nLiterary Texts in their Geographical Context: Production, Copying, Usage, Dissemination and Storage' - }, - _links: { - self: { - href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174002' - }, - target: { - href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174002/target' - }, - related: { - href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174002/related' - } - }, - target: createSuccessfulRemoteDataObject$(ItemMockPid8), - related: createSuccessfulRemoteDataObject$(ItemMockPid10) -}; - -export const openaireBrokerEventObjectMissingProjectNotFound: OpenaireBrokerEventObject = { - id: '123e4567-e89b-12d3-a456-426614174003', - uuid: '123e4567-e89b-12d3-a456-426614174003', - type: new ResourceType('openaireBrokerEvent'), - originalId: 'oai:www.openstarts.units.it:10077/21838', - title: 'Morocco, crossroad of translations and literary interweavings (3rd-6th centuries). A reconsideration of earlier Coptic literature', - trust: 1.0, - eventDate: '2020/10/09 10:11 UTC', - status: 'PENDING', - message: { - type: null, - value: null, - abstract: null, - openaireId: null, - acronym: 'PAThs', - code: '687567B', - funder: 'EC', - fundingProgram: 'H2021', - jurisdiction: 'EU', - title: 'Tracking Unknown Papyrus and Parchment Paths: An Archaeological Atlas of Coptic Literature.\nLiterary Texts in their Geographical Context: Production, Copying, Usage, Dissemination and Storage' - }, - _links: { - self: { - href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174003' - }, - target: { - href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174003/target' - }, - related: { - href: 'https://rest.api/rest/api/integration/nbevents/123e4567-e89b-12d3-a456-426614174003/related' - } - }, - target: createSuccessfulRemoteDataObject$(ItemMockPid9), - related: createNoContentRemoteDataObject$() -}; - // Classes // ------------------------------------------------------------------------------- @@ -1757,31 +1337,6 @@ export function getMockOpenaireStateService(): any { dispatchMarkUserSuggestionsAsVisitedAction: jasmine.createSpy('dispatchMarkUserSuggestionsAsVisitedAction') }); } - -/** - * Mock for [[OpenaireBrokerTopicRestService]] - */ -export function getMockOpenaireBrokerTopicRestService(): OpenaireBrokerTopicRestService { - return jasmine.createSpyObj('OpenaireBrokerTopicRestService', { - getTopics: jasmine.createSpy('getTopics'), - getTopic: jasmine.createSpy('getTopic'), - }); -} - -/** - * Mock for [[OpenaireBrokerEventRestService]] - */ -export function getMockOpenaireBrokerEventRestService(): OpenaireBrokerEventRestService { - return jasmine.createSpyObj('OpenaireBrokerEventRestService', { - getEventsByTopic: jasmine.createSpy('getEventsByTopic'), - getEvent: jasmine.createSpy('getEvent'), - patchEvent: jasmine.createSpy('patchEvent'), - boundProject: jasmine.createSpy('boundProject'), - removeProject: jasmine.createSpy('removeProject'), - clearFindByTopicRequests: jasmine.createSpy('.clearFindByTopicRequests') - }); -} - /** * Mock for [[OpenaireBrokerEventRestService]] */ diff --git a/src/app/suggestions-page/suggestions-page.component.ts b/src/app/suggestions-page/suggestions-page.component.ts index 850a30da221..3d6ced5ef57 100644 --- a/src/app/suggestions-page/suggestions-page.component.ts +++ b/src/app/suggestions-page/suggestions-page.component.ts @@ -20,8 +20,8 @@ import { SuggestionTargetsStateService } from '../openaire/reciter-suggestions/s import { WorkspaceitemDataService } from '../core/submission/workspaceitem-data.service'; import { PaginationService } from '../core/pagination/pagination.service'; import { WorkspaceItem } from '../core/submission/models/workspaceitem.model'; -import {FindListOptions} from "../core/data/find-list-options.model"; -import {redirectOn4xx} from "../core/shared/authorized.operators"; +import {FindListOptions} from '../core/data/find-list-options.model'; +import {redirectOn4xx} from '../core/shared/authorized.operators'; @Component({ selector: 'ds-suggestion-page', diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index fbc04033d61..f79cf799e5d 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -3033,6 +3033,7 @@ "reciter.suggestion.totalScore": "Total Score", + "reciter.suggestion.type.oaire": "OpenAIRE", "register-email.title": "New user registration", diff --git a/src/config/app-config.interface.ts b/src/config/app-config.interface.ts index 6446e7f127f..fbd84fae844 100644 --- a/src/config/app-config.interface.ts +++ b/src/config/app-config.interface.ts @@ -14,7 +14,7 @@ import { AuthConfig } from './auth-config.interfaces'; import { UIServerConfig } from './ui-server-config.interface'; import { MediaViewerConfig } from './media-viewer-config.interface'; import { BrowseByConfig } from './browse-by-config.interface'; -import {SuggestionConfig} from "./layout-config.interfaces"; +import {SuggestionConfig} from './layout-config.interfaces'; interface AppConfig extends Config { ui: UIServerConfig; diff --git a/src/config/default-app-config.ts b/src/config/default-app-config.ts index aea29fc819e..9def4849f57 100644 --- a/src/config/default-app-config.ts +++ b/src/config/default-app-config.ts @@ -14,7 +14,7 @@ import { ServerConfig } from './server-config.interface'; import { SubmissionConfig } from './submission-config.interface'; import { ThemeConfig } from './theme.model'; import { UIServerConfig } from './ui-server-config.interface'; -import {SuggestionConfig} from "./layout-config.interfaces"; +import {SuggestionConfig} from './layout-config.interfaces'; export class DefaultAppConfig implements AppConfig { production = false; diff --git a/src/environments/environment.test.ts b/src/environments/environment.test.ts index 7c24ef8f05f..6bf3a9ab8f9 100644 --- a/src/environments/environment.test.ts +++ b/src/environments/environment.test.ts @@ -2,6 +2,7 @@ import { BuildConfig } from 'src/config/build-config.interface'; import { RestRequestMethod } from '../app/core/data/rest-request-method'; import { NotificationAnimationsType } from '../app/shared/notifications/models/notification-animations-type'; +import {SuggestionConfig} from '../config/layout-config.interfaces'; export const environment: BuildConfig = { production: false, @@ -203,6 +204,13 @@ export const environment: BuildConfig = { undoTimeout: 10000 // 10 seconds } }, + suggestion: [ + // { + // // Use this configuration to map a suggestion import to a specific collection based on the suggestion type. + // source: 'suggestionSource', + // collectionId: 'collectionUUID' + // } + ], themes: [ { name: 'full-item-page-theme', From 63bba677bbcffda2554b263ef38f06c5e31aae00 Mon Sep 17 00:00:00 2001 From: Luca Giamminonni Date: Thu, 7 Jul 2022 17:09:48 +0200 Subject: [PATCH 04/34] [CST-5249] Removed Openaire prefix on Suggestion classes --- .../admin-notifications-routing.module.ts | 1 + src/app/core/core.module.ts | 12 ++-- ...ts => suggestion-objects.resource-type.ts} | 0 ...ce.model.ts => suggestion-source.model.ts} | 4 +- ...et.model.ts => suggestion-target.model.ts} | 4 +- ...uggestion.model.ts => suggestion.model.ts} | 4 +- ...service.ts => suggestions-data.service.ts} | 68 +++++++++---------- .../mocks/reciter-suggestion-targets.mock.ts | 6 +- .../shared/mocks/reciter-suggestion.mock.ts | 8 +-- .../{openaire.mock.ts => suggestion.mock.ts} | 0 .../reciter-suggestions/selectors.ts | 10 +-- .../suggestion-actions.component.ts | 4 +- .../suggestion-evidences.component.ts | 2 +- .../suggestion-list-element.component.ts | 6 +- .../suggestion-targets.actions.ts | 10 +-- .../suggestion-targets.component.ts | 6 +- .../suggestion-targets.effects.ts | 8 +-- .../suggestion-targets.reducer.ts | 6 +- .../suggestion-targets.state.service.ts | 6 +- .../suggestions-notification.component.ts | 6 +- .../suggestions-popup.component.ts | 6 +- .../suggestions.service.ts | 42 ++++++------ src/app/suggestion-notifications/selectors.ts | 2 +- .../suggestion-notifications.module.ts | 6 +- .../suggestion-notifications.reducer.ts | 2 +- .../suggestions-page.component.spec.ts | 6 +- .../suggestions-page.component.ts | 26 +++---- .../suggestions-page.module.ts | 4 +- .../suggestions-page.resolver.ts | 10 +-- 29 files changed, 138 insertions(+), 137 deletions(-) rename src/app/core/suggestion-notifications/reciter-suggestions/models/{openaire-suggestion-objects.resource-type.ts => suggestion-objects.resource-type.ts} (100%) rename src/app/core/suggestion-notifications/reciter-suggestions/models/{openaire-suggestion-source.model.ts => suggestion-source.model.ts} (87%) rename src/app/core/suggestion-notifications/reciter-suggestions/models/{openaire-suggestion-target.model.ts => suggestion-target.model.ts} (89%) rename src/app/core/suggestion-notifications/reciter-suggestions/models/{openaire-suggestion.model.ts => suggestion.model.ts} (92%) rename src/app/core/suggestion-notifications/reciter-suggestions/{openaire-suggestions-data.service.ts => suggestions-data.service.ts} (79%) rename src/app/shared/mocks/{openaire.mock.ts => suggestion.mock.ts} (100%) diff --git a/src/app/admin/admin-notifications/admin-notifications-routing.module.ts b/src/app/admin/admin-notifications/admin-notifications-routing.module.ts index ca6a8cf572d..60fa679777c 100644 --- a/src/app/admin/admin-notifications/admin-notifications-routing.module.ts +++ b/src/app/admin/admin-notifications/admin-notifications-routing.module.ts @@ -87,6 +87,7 @@ import { SourceDataResolver } from './admin-quality-assurance-source-page-compon I18nBreadcrumbsService, AdminNotificationsSuggestionTargetsPageResolver, SourceDataResolver, + AdminQualityAssuranceSourcePageResolver, AdminQualityAssuranceTopicsPageResolver, AdminQualityAssuranceEventsPageResolver, ] diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index bd82e0adc01..8986fa76fff 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -166,9 +166,9 @@ import { SearchConfig } from './shared/search/search-filters/search-config.model import { SequenceService } from './shared/sequence.service'; import { CoreState } from './core-state.model'; import { GroupDataService } from './eperson/group-data.service'; -import { OpenaireSuggestionTarget } from './suggestion-notifications/reciter-suggestions/models/openaire-suggestion-target.model'; -import { OpenaireSuggestion } from './suggestion-notifications/reciter-suggestions/models/openaire-suggestion.model'; -import { OpenaireSuggestionSource } from './suggestion-notifications/reciter-suggestions/models/openaire-suggestion-source.model'; +import { SuggestionTarget } from './suggestion-notifications/reciter-suggestions/models/suggestion-target.model'; +import { Suggestion } from './suggestion-notifications/reciter-suggestions/models/suggestion.model'; +import { SuggestionSource } from './suggestion-notifications/reciter-suggestions/models/suggestion-source.model'; import { ResearcherProfileService } from './profile/researcher-profile.service'; import { ProfileClaimService } from '../profile-page/profile-claim/profile-claim.service'; import { ResearcherProfile } from './profile/model/researcher-profile.model'; @@ -372,9 +372,9 @@ export const models = ShortLivedToken, Registration, UsageReport, - OpenaireSuggestion, - OpenaireSuggestionTarget, - OpenaireSuggestionSource, + Suggestion, + SuggestionTarget, + SuggestionSource, QualityAssuranceTopicObject, QualityAssuranceEventObject, Root, diff --git a/src/app/core/suggestion-notifications/reciter-suggestions/models/openaire-suggestion-objects.resource-type.ts b/src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-objects.resource-type.ts similarity index 100% rename from src/app/core/suggestion-notifications/reciter-suggestions/models/openaire-suggestion-objects.resource-type.ts rename to src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-objects.resource-type.ts diff --git a/src/app/core/suggestion-notifications/reciter-suggestions/models/openaire-suggestion-source.model.ts b/src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-source.model.ts similarity index 87% rename from src/app/core/suggestion-notifications/reciter-suggestions/models/openaire-suggestion-source.model.ts rename to src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-source.model.ts index 00f5f11936b..64ddf9863c8 100644 --- a/src/app/core/suggestion-notifications/reciter-suggestions/models/openaire-suggestion-source.model.ts +++ b/src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-source.model.ts @@ -1,6 +1,6 @@ import { autoserialize, deserialize } from 'cerialize'; -import { SUGGESTION_SOURCE } from './openaire-suggestion-objects.resource-type'; +import { SUGGESTION_SOURCE } from './suggestion-objects.resource-type'; import { excludeFromEquals } from '../../../utilities/equals.decorators'; import { ResourceType } from '../../../shared/resource-type'; import { HALLink } from '../../../shared/hal-link.model'; @@ -11,7 +11,7 @@ import {CacheableObject} from '../../../cache/cacheable-object.model'; * The interface representing the Suggestion Source model */ @typedObject -export class OpenaireSuggestionSource implements CacheableObject { +export class SuggestionSource implements CacheableObject { /** * A string representing the kind of object, e.g. community, item, … */ diff --git a/src/app/core/suggestion-notifications/reciter-suggestions/models/openaire-suggestion-target.model.ts b/src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-target.model.ts similarity index 89% rename from src/app/core/suggestion-notifications/reciter-suggestions/models/openaire-suggestion-target.model.ts rename to src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-target.model.ts index 96a43c96544..fa23dd0ffa7 100644 --- a/src/app/core/suggestion-notifications/reciter-suggestions/models/openaire-suggestion-target.model.ts +++ b/src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-target.model.ts @@ -1,6 +1,6 @@ import { autoserialize, deserialize } from 'cerialize'; -import { SUGGESTION_TARGET } from './openaire-suggestion-objects.resource-type'; +import { SUGGESTION_TARGET } from './suggestion-objects.resource-type'; import { excludeFromEquals } from '../../../utilities/equals.decorators'; import { ResourceType } from '../../../shared/resource-type'; import { HALLink } from '../../../shared/hal-link.model'; @@ -11,7 +11,7 @@ import {CacheableObject} from '../../../cache/cacheable-object.model'; * The interface representing the Suggestion Target model */ @typedObject -export class OpenaireSuggestionTarget implements CacheableObject { +export class SuggestionTarget implements CacheableObject { /** * A string representing the kind of object, e.g. community, item, … */ diff --git a/src/app/core/suggestion-notifications/reciter-suggestions/models/openaire-suggestion.model.ts b/src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion.model.ts similarity index 92% rename from src/app/core/suggestion-notifications/reciter-suggestions/models/openaire-suggestion.model.ts rename to src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion.model.ts index 0f84072c6ba..c36d36794b5 100644 --- a/src/app/core/suggestion-notifications/reciter-suggestions/models/openaire-suggestion.model.ts +++ b/src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion.model.ts @@ -1,6 +1,6 @@ import { autoserialize, autoserializeAs, deserialize } from 'cerialize'; -import { SUGGESTION } from './openaire-suggestion-objects.resource-type'; +import { SUGGESTION } from './suggestion-objects.resource-type'; import { excludeFromEquals } from '../../../utilities/equals.decorators'; import { ResourceType } from '../../../shared/resource-type'; import { HALLink } from '../../../shared/hal-link.model'; @@ -18,7 +18,7 @@ export interface SuggestionEvidences { * The interface representing the Suggestion Source model */ @typedObject -export class OpenaireSuggestion implements CacheableObject { +export class Suggestion implements CacheableObject { /** * A string representing the kind of object, e.g. community, item, … */ diff --git a/src/app/core/suggestion-notifications/reciter-suggestions/openaire-suggestions-data.service.ts b/src/app/core/suggestion-notifications/reciter-suggestions/suggestions-data.service.ts similarity index 79% rename from src/app/core/suggestion-notifications/reciter-suggestions/openaire-suggestions-data.service.ts rename to src/app/core/suggestion-notifications/reciter-suggestions/suggestions-data.service.ts index 3c1d04c0404..880c7b2f336 100644 --- a/src/app/core/suggestion-notifications/reciter-suggestions/openaire-suggestions-data.service.ts +++ b/src/app/core/suggestion-notifications/reciter-suggestions/suggestions-data.service.ts @@ -15,12 +15,12 @@ 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 { SUGGESTION_TARGET } from './models/openaire-suggestion-objects.resource-type'; +import { SUGGESTION_TARGET } from './models/suggestion-objects.resource-type'; import { FollowLinkConfig } from '../../../shared/utils/follow-link-config.model'; import { PaginatedList } from '../../data/paginated-list.model'; -import { OpenaireSuggestionSource } from './models/openaire-suggestion-source.model'; -import { OpenaireSuggestionTarget } from './models/openaire-suggestion-target.model'; -import { OpenaireSuggestion } from './models/openaire-suggestion.model'; +import { SuggestionSource } from './models/suggestion-source.model'; +import { SuggestionTarget } from './models/suggestion-target.model'; +import { Suggestion } from './models/suggestion.model'; import { RequestParam } from '../../cache/models/request-param.model'; import { NoContent } from '../../shared/NoContent.model'; import {CoreState} from '../../core-state.model'; @@ -31,7 +31,7 @@ import {FindListOptions} from '../../data/find-list-options.model'; /** * A private DataService implementation to delegate specific methods to. */ -class SuggestionDataServiceImpl extends DataService { +class SuggestionDataServiceImpl extends DataService { /** * The REST endpoint. */ @@ -46,7 +46,7 @@ class SuggestionDataServiceImpl extends DataService { * @param {HALEndpointService} halService * @param {NotificationsService} notificationsService * @param {HttpClient} http - * @param {ChangeAnalyzer} comparator + * @param {ChangeAnalyzer} comparator */ constructor( protected requestService: RequestService, @@ -56,7 +56,7 @@ class SuggestionDataServiceImpl extends DataService { protected halService: HALEndpointService, protected notificationsService: NotificationsService, protected http: HttpClient, - protected comparator: ChangeAnalyzer) { + protected comparator: ChangeAnalyzer) { super(); } } @@ -64,7 +64,7 @@ class SuggestionDataServiceImpl extends DataService { /** * A private DataService implementation to delegate specific methods to. */ -class SuggestionTargetsDataServiceImpl extends DataService { +class SuggestionTargetsDataServiceImpl extends DataService { /** * The REST endpoint. */ @@ -79,7 +79,7 @@ class SuggestionTargetsDataServiceImpl extends DataService} comparator + * @param {ChangeAnalyzer} comparator */ constructor( protected requestService: RequestService, @@ -89,7 +89,7 @@ class SuggestionTargetsDataServiceImpl extends DataService) { + protected comparator: ChangeAnalyzer) { super(); } } @@ -97,7 +97,7 @@ class SuggestionTargetsDataServiceImpl extends DataService { +class SuggestionSourcesDataServiceImpl extends DataService { /** * The REST endpoint. */ @@ -112,7 +112,7 @@ class SuggestionSourcesDataServiceImpl extends DataService} comparator + * @param {ChangeAnalyzer} comparator */ constructor( protected requestService: RequestService, @@ -122,7 +122,7 @@ class SuggestionSourcesDataServiceImpl extends DataService) { + protected comparator: ChangeAnalyzer) { super(); } } @@ -132,7 +132,7 @@ class SuggestionSourcesDataServiceImpl extends DataService} comparatorSuggestions - * @param {DefaultChangeAnalyzer} comparatorSources - * @param {DefaultChangeAnalyzer} comparatorTargets + * @param {DefaultChangeAnalyzer} comparatorSuggestions + * @param {DefaultChangeAnalyzer} comparatorSources + * @param {DefaultChangeAnalyzer} comparatorTargets */ constructor( protected requestService: RequestService, @@ -171,9 +171,9 @@ export class OpenaireSuggestionsDataService { protected halService: HALEndpointService, protected notificationsService: NotificationsService, protected http: HttpClient, - protected comparatorSuggestions: DefaultChangeAnalyzer, - protected comparatorSources: DefaultChangeAnalyzer, - protected comparatorTargets: DefaultChangeAnalyzer, + protected comparatorSuggestions: DefaultChangeAnalyzer, + protected comparatorSources: DefaultChangeAnalyzer, + protected comparatorTargets: DefaultChangeAnalyzer, ) { this.suggestionsDataService = new SuggestionDataServiceImpl(requestService, rdbService, null, objectCache, halService, notificationsService, http, comparatorSuggestions); this.suggestionSourcesDataService = new SuggestionSourcesDataServiceImpl(requestService, rdbService, null, objectCache, halService, notificationsService, http, comparatorSources); @@ -185,10 +185,10 @@ export class OpenaireSuggestionsDataService { * * @param options * Find list options object. - * @return Observable>> + * @return Observable>> * The list of Suggestion Sources. */ - public getSources(options: FindListOptions = {}): Observable>> { + public getSources(options: FindListOptions = {}): Observable>> { return this.suggestionSourcesDataService.findAll(options); } @@ -201,14 +201,14 @@ export class OpenaireSuggestionsDataService { * Find list options object. * @param linksToFollow * List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved. - * @return Observable>> + * @return Observable>> * The list of Suggestion Target. */ public getTargets( source: string, options: FindListOptions = {}, - ...linksToFollow: FollowLinkConfig[] - ): Observable>> { + ...linksToFollow: FollowLinkConfig[] + ): Observable>> { options.searchParams = [new RequestParam('source', source)]; return this.suggestionTargetsDataService.searchBy(this.searchFindBySourceMethod, options, true, true, ...linksToFollow); @@ -223,14 +223,14 @@ export class OpenaireSuggestionsDataService { * Find list options object. * @param linksToFollow * List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved. - * @return Observable>> + * @return Observable>> * The list of Suggestion Target. */ public getTargetsByUser( userId: string, options: FindListOptions = {}, - ...linksToFollow: FollowLinkConfig[] - ): Observable>> { + ...linksToFollow: FollowLinkConfig[] + ): Observable>> { options.searchParams = [new RequestParam('target', userId)]; return this.suggestionTargetsDataService.searchBy(this.searchFindByTargetMethod, options, true, true, ...linksToFollow); @@ -242,10 +242,10 @@ export class OpenaireSuggestionsDataService { * @param targetId * The target id to retrieve. * - * @return Observable> + * @return Observable> * The list of Suggestion Target. */ - public getTargetById(targetId: string): Observable> { + public getTargetById(targetId: string): Observable> { return this.suggestionTargetsDataService.findById(targetId); } @@ -261,7 +261,7 @@ export class OpenaireSuggestionsDataService { * Used to fetch Suggestion notification for user * @suggestionId */ - public getSuggestion(suggestionId: string, ...linksToFollow: FollowLinkConfig[]): Observable> { + public getSuggestion(suggestionId: string, ...linksToFollow: FollowLinkConfig[]): Observable> { return this.suggestionsDataService.findById(suggestionId, true, true, ...linksToFollow); } @@ -276,15 +276,15 @@ export class OpenaireSuggestionsDataService { * Find list options object. * @param linksToFollow * List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved. - * @return Observable>> + * @return Observable>> * The list of Suggestion. */ public getSuggestionsByTargetAndSource( target: string, source: string, options: FindListOptions = {}, - ...linksToFollow: FollowLinkConfig[] - ): Observable>> { + ...linksToFollow: FollowLinkConfig[] + ): Observable>> { options.searchParams = [ new RequestParam('target', target), new RequestParam('source', source) diff --git a/src/app/shared/mocks/reciter-suggestion-targets.mock.ts b/src/app/shared/mocks/reciter-suggestion-targets.mock.ts index ae8cacb2737..489bb7733d8 100644 --- a/src/app/shared/mocks/reciter-suggestion-targets.mock.ts +++ b/src/app/shared/mocks/reciter-suggestion-targets.mock.ts @@ -1,9 +1,9 @@ import { ResourceType } from '../../core/shared/resource-type'; -import { OpenaireSuggestionTarget } from '../../core/suggestion-notifications/reciter-suggestions/models/openaire-suggestion-target.model'; +import { SuggestionTarget } from '../../core/suggestion-notifications/reciter-suggestions/models/suggestion-target.model'; // REST Mock --------------------------------------------------------------------- // ------------------------------------------------------------------------------- -export const mockSuggestionTargetsObjectOne: OpenaireSuggestionTarget = { +export const mockSuggestionTargetsObjectOne: SuggestionTarget = { type: new ResourceType('suggestiontarget'), id: 'reciter:gf3d657-9d6d-4a87-b905-fef0f8cae26', display: 'Bollini, Andrea', @@ -22,7 +22,7 @@ export const mockSuggestionTargetsObjectOne: OpenaireSuggestionTarget = { } }; -export const mockSuggestionTargetsObjectTwo: OpenaireSuggestionTarget = { +export const mockSuggestionTargetsObjectTwo: SuggestionTarget = { type: new ResourceType('suggestiontarget'), id: 'reciter:nhy567-9d6d-ty67-b905-fef0f8cae26', display: 'Digilio, Andrea', diff --git a/src/app/shared/mocks/reciter-suggestion.mock.ts b/src/app/shared/mocks/reciter-suggestion.mock.ts index ae1732808e0..aceca72fc8f 100644 --- a/src/app/shared/mocks/reciter-suggestion.mock.ts +++ b/src/app/shared/mocks/reciter-suggestion.mock.ts @@ -2,10 +2,10 @@ // REST Mock --------------------------------------------------------------------- // ------------------------------------------------------------------------------- -import { OpenaireSuggestion } from '../../core/suggestion-notifications/reciter-suggestions/models/openaire-suggestion.model'; -import { SUGGESTION } from '../../core/suggestion-notifications/reciter-suggestions/models/openaire-suggestion-objects.resource-type'; +import { Suggestion } from '../../core/suggestion-notifications/reciter-suggestions/models/suggestion.model'; +import { SUGGESTION } from '../../core/suggestion-notifications/reciter-suggestions/models/suggestion-objects.resource-type'; -export const mockSuggestionPublicationOne: OpenaireSuggestion = { +export const mockSuggestionPublicationOne: Suggestion = { id: '24694772', display: 'publication one', source: 'reciter', @@ -107,7 +107,7 @@ export const mockSuggestionPublicationOne: OpenaireSuggestion = { } }; -export const mockSuggestionPublicationTwo: OpenaireSuggestion = { +export const mockSuggestionPublicationTwo: Suggestion = { id: '24694772', display: 'publication two', source: 'reciter', diff --git a/src/app/shared/mocks/openaire.mock.ts b/src/app/shared/mocks/suggestion.mock.ts similarity index 100% rename from src/app/shared/mocks/openaire.mock.ts rename to src/app/shared/mocks/suggestion.mock.ts diff --git a/src/app/suggestion-notifications/reciter-suggestions/selectors.ts b/src/app/suggestion-notifications/reciter-suggestions/selectors.ts index 6ba474bfd83..aa81ab4bc87 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/selectors.ts +++ b/src/app/suggestion-notifications/reciter-suggestions/selectors.ts @@ -1,6 +1,6 @@ import {createFeatureSelector, createSelector, MemoizedSelector} from '@ngrx/store'; import { suggestionNotificationsSelector, SuggestionNotificationsState } from '../suggestion-notifications.reducer'; -import { OpenaireSuggestionTarget } from '../../core/suggestion-notifications/reciter-suggestions/models/openaire-suggestion-target.model'; +import { SuggestionTarget } from '../../core/suggestion-notifications/reciter-suggestions/models/suggestion-target.model'; import { SuggestionTargetState } from './suggestion-targets/suggestion-targets.reducer'; import {subStateSelector} from '../../submission/selectors'; @@ -10,7 +10,7 @@ import {subStateSelector} from '../../submission/selectors'; * @param {AppState} state Top level state. * @return {SuggestionNotificationsState} */ -const _getReciterSuggestionTargetState = createFeatureSelector('openaire'); +const _getReciterSuggestionTargetState = createFeatureSelector('suggestion-notifications'); // Reciter Suggestion Targets // ---------------------------------------------------------------------------- @@ -29,8 +29,8 @@ export function reciterSuggestionTargetStateSelector(): MemoizedSelector { - return subStateSelector(reciterSuggestionTargetStateSelector(), 'targets'); +export function reciterSuggestionTargetObjectSelector(): MemoizedSelector { + return subStateSelector(reciterSuggestionTargetStateSelector(), 'targets'); } /** @@ -81,7 +81,7 @@ export const getreciterSuggestionTargetTotalsSelector = createSelector(_getRecit /** * Returns Suggestion Targets for the current user. * @function getCurrentUserReciterSuggestionTargetSelector - * @return {OpenaireSuggestionTarget[]} + * @return {SuggestionTarget[]} */ export const getCurrentUserSuggestionTargetsSelector = createSelector(_getReciterSuggestionTargetState, (state: SuggestionNotificationsState) => state.suggestionTarget.currentUserTargets diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestion-actions/suggestion-actions.component.ts b/src/app/suggestion-notifications/reciter-suggestions/suggestion-actions/suggestion-actions.component.ts index 7a3312af0c3..35599669ef6 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/suggestion-actions/suggestion-actions.component.ts +++ b/src/app/suggestion-notifications/reciter-suggestions/suggestion-actions/suggestion-actions.component.ts @@ -1,7 +1,7 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { ItemType } from '../../../core/shared/item-relationships/item-type.model'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; -import { OpenaireSuggestion } from '../../../core/suggestion-notifications/reciter-suggestions/models/openaire-suggestion.model'; +import { Suggestion } from '../../../core/suggestion-notifications/reciter-suggestions/models/suggestion.model'; import { SuggestionApproveAndImport } from '../suggestion-list-element/suggestion-list-element.component'; import { Collection } from '../../../core/shared/collection.model'; import { take } from 'rxjs/operators'; @@ -14,7 +14,7 @@ import { CreateItemParentSelectorComponent } from '../../../shared/dso-selector/ }) export class SuggestionActionsComponent { - @Input() object: OpenaireSuggestion; + @Input() object: Suggestion; @Input() isBulk = false; diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestion-list-element/suggestion-evidences/suggestion-evidences.component.ts b/src/app/suggestion-notifications/reciter-suggestions/suggestion-list-element/suggestion-evidences/suggestion-evidences.component.ts index 7bd5fc6e1bc..431749eaa58 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/suggestion-list-element/suggestion-evidences/suggestion-evidences.component.ts +++ b/src/app/suggestion-notifications/reciter-suggestions/suggestion-list-element/suggestion-evidences/suggestion-evidences.component.ts @@ -1,6 +1,6 @@ import { Component, Input } from '@angular/core'; import { fadeIn } from '../../../../shared/animations/fade'; -import { SuggestionEvidences } from '../../../../core/suggestion-notifications/reciter-suggestions/models/openaire-suggestion.model'; +import { SuggestionEvidences } from '../../../../core/suggestion-notifications/reciter-suggestions/models/suggestion.model'; @Component({ selector: 'ds-suggestion-evidences', diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestion-list-element/suggestion-list-element.component.ts b/src/app/suggestion-notifications/reciter-suggestions/suggestion-list-element/suggestion-list-element.component.ts index ca531655fc5..a1ef454037b 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/suggestion-list-element/suggestion-list-element.component.ts +++ b/src/app/suggestion-notifications/reciter-suggestions/suggestion-list-element/suggestion-list-element.component.ts @@ -3,12 +3,12 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { fadeIn } from '../../../shared/animations/fade'; -import { OpenaireSuggestion } from '../../../core/suggestion-notifications/reciter-suggestions/models/openaire-suggestion.model'; +import { Suggestion } from '../../../core/suggestion-notifications/reciter-suggestions/models/suggestion.model'; import { Item } from '../../../core/shared/item.model'; import { isNotEmpty } from '../../../shared/empty.util'; export interface SuggestionApproveAndImport { - suggestion: OpenaireSuggestion; + suggestion: Suggestion; collectionId: string; } @@ -20,7 +20,7 @@ export interface SuggestionApproveAndImport { }) export class SuggestionListElementComponent implements OnInit { - @Input() object: OpenaireSuggestion; + @Input() object: Suggestion; @Input() isSelected = false; diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.actions.ts b/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.actions.ts index 627b8155546..09e4f6e20c6 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.actions.ts +++ b/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.actions.ts @@ -1,7 +1,7 @@ /* eslint-disable max-classes-per-file */ import { Action } from '@ngrx/store'; import { type } from '../../../shared/ngrx/type'; -import { OpenaireSuggestionTarget } from '../../../core/suggestion-notifications/reciter-suggestions/models/openaire-suggestion-target.model'; +import { SuggestionTarget } from '../../../core/suggestion-notifications/reciter-suggestions/models/suggestion-target.model'; /** * For each action type in an action group, make a simple @@ -66,7 +66,7 @@ export class RetrieveAllTargetsErrorAction implements Action { export class AddTargetAction implements Action { type = SuggestionTargetActionTypes.ADD_TARGETS; payload: { - targets: OpenaireSuggestionTarget[]; + targets: SuggestionTarget[]; totalPages: number; currentPage: number; totalElements: number; @@ -84,7 +84,7 @@ export class AddTargetAction implements Action { * @param totalElements * the total available Suggestion Targets */ - constructor(targets: OpenaireSuggestionTarget[], totalPages: number, currentPage: number, totalElements: number) { + constructor(targets: SuggestionTarget[], totalPages: number, currentPage: number, totalElements: number) { this.payload = { targets, totalPages, @@ -102,7 +102,7 @@ export class AddTargetAction implements Action { export class AddUserSuggestionsAction implements Action { type = SuggestionTargetActionTypes.ADD_USER_SUGGESTIONS; payload: { - suggestionTargets: OpenaireSuggestionTarget[]; + suggestionTargets: SuggestionTarget[]; }; /** @@ -111,7 +111,7 @@ export class AddUserSuggestionsAction implements Action { * @param suggestionTargets * the user suggestions target */ - constructor(suggestionTargets: OpenaireSuggestionTarget[]) { + constructor(suggestionTargets: SuggestionTarget[]) { this.payload = { suggestionTargets }; } diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.component.ts b/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.component.ts index 7cd882d01ed..cfa168e2b83 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.component.ts +++ b/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.component.ts @@ -4,7 +4,7 @@ import { Router } from '@angular/router'; import { Observable, Subscription } from 'rxjs'; import { distinctUntilChanged, take } from 'rxjs/operators'; -import { OpenaireSuggestionTarget } from '../../../core/suggestion-notifications/reciter-suggestions/models/openaire-suggestion-target.model'; +import { SuggestionTarget } from '../../../core/suggestion-notifications/reciter-suggestions/models/suggestion-target.model'; import { hasValue } from '../../../shared/empty.util'; import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; import { SuggestionTargetsStateService } from './suggestion-targets.state.service'; @@ -39,7 +39,7 @@ export class SuggestionTargetsComponent implements OnInit { /** * The Suggestion Target list. */ - public targets$: Observable; + public targets$: Observable; /** * The total number of Suggestion Targets. */ @@ -144,7 +144,7 @@ export class SuggestionTargetsComponent implements OnInit { }); } - public getTargetUuid(target: OpenaireSuggestionTarget) { + public getTargetUuid(target: SuggestionTarget) { return this.suggestionService.getTargetUuid(target); } } diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.effects.ts b/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.effects.ts index 01b305bbd4c..9c595a1e0df 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.effects.ts +++ b/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.effects.ts @@ -18,7 +18,7 @@ import { PaginatedList } from '../../../core/data/paginated-list.model'; import { SuggestionsService } from '../suggestions.service'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; import { AuthActionTypes, RetrieveAuthenticatedEpersonSuccessAction } from '../../../core/auth/auth.actions'; -import { OpenaireSuggestionTarget } from '../../../core/suggestion-notifications/reciter-suggestions/models/openaire-suggestion-target.model'; +import { SuggestionTarget } from '../../../core/suggestion-notifications/reciter-suggestions/models/suggestion-target.model'; import { EPerson } from '../../../core/eperson/models/eperson.model'; /** @@ -38,7 +38,7 @@ export class SuggestionTargetsEffects { action.payload.elementsPerPage, action.payload.currentPage ).pipe( - map((targets: PaginatedList) => + map((targets: PaginatedList) => new AddTargetAction(targets.page, targets.totalPages, targets.currentPage, targets.totalElements) ), catchError((error: Error) => { @@ -68,7 +68,7 @@ export class SuggestionTargetsEffects { ofType(AuthActionTypes.RETRIEVE_AUTHENTICATED_EPERSON_SUCCESS), switchMap((action: RetrieveAuthenticatedEpersonSuccessAction) => { return this.suggestionsService.retrieveCurrentUserSuggestions(action.payload).pipe( - map((suggestionTargets: OpenaireSuggestionTarget[]) => new AddUserSuggestionsAction(suggestionTargets)) + map((suggestionTargets: SuggestionTarget[]) => new AddUserSuggestionsAction(suggestionTargets)) ); })); @@ -83,7 +83,7 @@ export class SuggestionTargetsEffects { switchMap((userId: string) => { return this.suggestionsService.retrieveCurrentUserSuggestions(userId) .pipe( - map((suggestionTargets: OpenaireSuggestionTarget[]) => new AddUserSuggestionsAction(suggestionTargets)), + map((suggestionTargets: SuggestionTarget[]) => new AddUserSuggestionsAction(suggestionTargets)), catchError((errors) => of(errors)) ); }), diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.reducer.ts b/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.reducer.ts index b0b551dfa9a..2d315e69956 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.reducer.ts +++ b/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.reducer.ts @@ -1,17 +1,17 @@ import { SuggestionTargetActionTypes, SuggestionTargetsActions } from './suggestion-targets.actions'; -import { OpenaireSuggestionTarget } from '../../../core/suggestion-notifications/reciter-suggestions/models/openaire-suggestion-target.model'; +import { SuggestionTarget } from '../../../core/suggestion-notifications/reciter-suggestions/models/suggestion-target.model'; /** * The interface representing the OpenAIRE suggestion targets state. */ export interface SuggestionTargetState { - targets: OpenaireSuggestionTarget[]; + targets: SuggestionTarget[]; processing: boolean; loaded: boolean; totalPages: number; currentPage: number; totalElements: number; - currentUserTargets: OpenaireSuggestionTarget[]; + currentUserTargets: SuggestionTarget[]; currentUserTargetsVisited: boolean; } diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.state.service.ts b/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.state.service.ts index 164750d2215..97792df6af7 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.state.service.ts +++ b/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.state.service.ts @@ -13,7 +13,7 @@ import { isreciterSuggestionTargetProcessingSelector, reciterSuggestionTargetObjectSelector } from '../selectors'; -import { OpenaireSuggestionTarget } from '../../../core/suggestion-notifications/reciter-suggestions/models/openaire-suggestion-target.model'; +import { SuggestionTarget } from '../../../core/suggestion-notifications/reciter-suggestions/models/suggestion-target.model'; import { ClearSuggestionTargetsAction, MarkUserSuggestionsAsVisitedAction, @@ -40,7 +40,7 @@ export class SuggestionTargetsStateService { * @return Observable * The list of Reciter Suggestion Targets. */ - public getReciterSuggestionTargets(): Observable { + public getReciterSuggestionTargets(): Observable { return this.store.pipe(select(reciterSuggestionTargetObjectSelector())); } @@ -127,7 +127,7 @@ export class SuggestionTargetsStateService { * @return Observable * The Reciter Suggestion Targets object. */ - public getCurrentUserSuggestionTargets(): Observable { + public getCurrentUserSuggestionTargets(): Observable { return this.store.pipe(select(getCurrentUserSuggestionTargetsSelector)); } diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestions-notification/suggestions-notification.component.ts b/src/app/suggestion-notifications/reciter-suggestions/suggestions-notification/suggestions-notification.component.ts index 19f94c433df..23b34c4de99 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/suggestions-notification/suggestions-notification.component.ts +++ b/src/app/suggestion-notifications/reciter-suggestions/suggestions-notification/suggestions-notification.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit } from '@angular/core'; -import { OpenaireSuggestionTarget } from '../../../core/suggestion-notifications/reciter-suggestions/models/openaire-suggestion-target.model'; +import { SuggestionTarget } from '../../../core/suggestion-notifications/reciter-suggestions/models/suggestion-target.model'; import { TranslateService } from '@ngx-translate/core'; import { SuggestionTargetsStateService } from '../suggestion-targets/suggestion-targets.state.service'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; @@ -18,7 +18,7 @@ export class SuggestionsNotificationComponent implements OnInit { /** * The user suggestion targets. */ - suggestionsRD$: Observable; + suggestionsRD$: Observable; constructor( private translateService: TranslateService, @@ -35,7 +35,7 @@ export class SuggestionsNotificationComponent implements OnInit { * Interpolated params to build the notification suggestions notification. * @param suggestionTarget */ - public getNotificationSuggestionInterpolation(suggestionTarget: OpenaireSuggestionTarget): any { + public getNotificationSuggestionInterpolation(suggestionTarget: SuggestionTarget): any { return this.suggestionsService.getNotificationSuggestionInterpolation(suggestionTarget); } diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestions-popup/suggestions-popup.component.ts b/src/app/suggestion-notifications/reciter-suggestions/suggestions-popup/suggestions-popup.component.ts index 9478ed2d1d6..0195f074ae8 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/suggestions-popup/suggestions-popup.component.ts +++ b/src/app/suggestion-notifications/reciter-suggestions/suggestions-popup/suggestions-popup.component.ts @@ -4,7 +4,7 @@ import { SuggestionTargetsStateService } from '../suggestion-targets/suggestion- import { NotificationsService } from '../../../shared/notifications/notifications.service'; import { SuggestionsService } from '../suggestions.service'; import { takeUntil } from 'rxjs/operators'; -import { OpenaireSuggestionTarget } from '../../../core/suggestion-notifications/reciter-suggestions/models/openaire-suggestion-target.model'; +import { SuggestionTarget } from '../../../core/suggestion-notifications/reciter-suggestions/models/suggestion-target.model'; import { isNotEmpty } from '../../../shared/empty.util'; import { combineLatest, Subject } from 'rxjs'; @@ -38,7 +38,7 @@ export class SuggestionsPopupComponent implements OnInit, OnDestroy { ]).pipe(takeUntil(notifier)).subscribe(([suggestions, visited]) => { if (isNotEmpty(suggestions)) { if (!visited) { - suggestions.forEach((suggestionTarget: OpenaireSuggestionTarget) => this.showNotificationForNewSuggestions(suggestionTarget)); + suggestions.forEach((suggestionTarget: SuggestionTarget) => this.showNotificationForNewSuggestions(suggestionTarget)); this.reciterSuggestionStateService.dispatchMarkUserSuggestionsAsVisitedAction(); notifier.next(null); notifier.complete(); @@ -52,7 +52,7 @@ export class SuggestionsPopupComponent implements OnInit, OnDestroy { * @param suggestionTarget * @private */ - private showNotificationForNewSuggestions(suggestionTarget: OpenaireSuggestionTarget): void { + private showNotificationForNewSuggestions(suggestionTarget: SuggestionTarget): void { const content = this.translateService.instant(this.labelPrefix + 'notification.suggestion', this.suggestionsService.getNotificationSuggestionInterpolation(suggestionTarget)); this.notificationsService.success('', content, {timeOut:0}, true); diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestions.service.ts b/src/app/suggestion-notifications/reciter-suggestions/suggestions.service.ts index c4a2fa4b4b2..be43285df26 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/suggestions.service.ts +++ b/src/app/suggestion-notifications/reciter-suggestions/suggestions.service.ts @@ -3,11 +3,11 @@ import { Injectable } from '@angular/core'; import { of, forkJoin, Observable } from 'rxjs'; import { catchError, map, mergeMap, take } from 'rxjs/operators'; -import { OpenaireSuggestionsDataService } from '../../core/suggestion-notifications/reciter-suggestions/openaire-suggestions-data.service'; +import { SuggestionsDataService } from '../../core/suggestion-notifications/reciter-suggestions/suggestions-data.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 { OpenaireSuggestionTarget } from '../../core/suggestion-notifications/reciter-suggestions/models/openaire-suggestion-target.model'; +import { SuggestionTarget } from '../../core/suggestion-notifications/reciter-suggestions/models/suggestion-target.model'; import { ResearcherProfileService } from '../../core/profile/researcher-profile.service'; import { AuthService } from '../../core/auth/auth.service'; import { EPerson } from '../../core/eperson/models/eperson.model'; @@ -19,7 +19,7 @@ import { getFirstSucceededRemoteDataPayload, getFirstSucceededRemoteListPayload } from '../../core/shared/operators'; -import { OpenaireSuggestion } from '../../core/suggestion-notifications/reciter-suggestions/models/openaire-suggestion.model'; +import { Suggestion } from '../../core/suggestion-notifications/reciter-suggestions/models/suggestion.model'; import { WorkspaceitemDataService } from '../../core/submission/workspaceitem-data.service'; import { TranslateService } from '@ngx-translate/core'; import { NoContent } from '../../core/shared/NoContent.model'; @@ -43,12 +43,12 @@ export class SuggestionsService { * Initialize the service variables. * @param {AuthService} authService * @param {ResearcherProfileService} researcherProfileService - * @param {OpenaireSuggestionsDataService} suggestionsDataService + * @param {SuggestionsDataService} suggestionsDataService */ constructor( private authService: AuthService, private researcherProfileService: ResearcherProfileService, - private suggestionsDataService: OpenaireSuggestionsDataService, + private suggestionsDataService: SuggestionsDataService, private translateService: TranslateService ) { } @@ -65,7 +65,7 @@ export class SuggestionsService { * @return Observable> * The list of Suggestion Targets. */ - public getTargets(source, elementsPerPage, currentPage): Observable> { + public getTargets(source, elementsPerPage, currentPage): Observable> { const sortOptions = new SortOptions('display', SortDirection.ASC); const findListOptions: FindListOptions = { @@ -77,7 +77,7 @@ export class SuggestionsService { return this.suggestionsDataService.getTargets(source, findListOptions).pipe( getFinishedRemoteData(), take(1), - map((rd: RemoteData>) => { + map((rd: RemoteData>) => { if (rd.hasSucceeded) { return rd.payload; } else { @@ -98,10 +98,10 @@ export class SuggestionsService { * The page number to retrieve * @param sortOptions * The sort options - * @return Observable>> + * @return Observable>> * The list of Suggestion. */ - public getSuggestions(targetId: string, elementsPerPage, currentPage, sortOptions: SortOptions): Observable> { + public getSuggestions(targetId: string, elementsPerPage, currentPage, sortOptions: SortOptions): Observable> { const [source, target] = targetId.split(':'); const findListOptions: FindListOptions = { @@ -145,7 +145,7 @@ export class SuggestionsService { * @param userUuid * The EPerson id for which to retrieve suggestion targets */ - public retrieveCurrentUserSuggestions(userUuid: string): Observable { + public retrieveCurrentUserSuggestions(userUuid: string): Observable { return this.researcherProfileService.findById(userUuid).pipe( getFirstSucceededRemoteDataPayload(), mergeMap((profile: ResearcherProfile) => { @@ -173,7 +173,7 @@ export class SuggestionsService { * @private */ public approveAndImport(workspaceitemService: WorkspaceitemDataService, - suggestion: OpenaireSuggestion, + suggestion: Suggestion, collectionId: string): Observable { const resolvedCollectionId = this.resolveCollectionId(suggestion, collectionId); @@ -201,10 +201,10 @@ export class SuggestionsService { * @param collectionId the collectionId */ public approveAndImportMultiple(workspaceitemService: WorkspaceitemDataService, - suggestions: OpenaireSuggestion[], + suggestions: Suggestion[], collectionId: string): Observable { - return forkJoin(suggestions.map((suggestion: OpenaireSuggestion) => + return forkJoin(suggestions.map((suggestion: Suggestion) => this.approveAndImport(workspaceitemService, suggestion, collectionId))) .pipe(map((results: WorkspaceItem[]) => { return { @@ -218,8 +218,8 @@ export class SuggestionsService { * Perform a bulk notMine operation. * @param suggestions the array containing the suggestions */ - public notMineMultiple(suggestions: OpenaireSuggestion[]): Observable { - return forkJoin(suggestions.map((suggestion: OpenaireSuggestion) => this.notMine(suggestion.id))) + public notMineMultiple(suggestions: Suggestion[]): Observable { + return forkJoin(suggestions.map((suggestion: Suggestion) => this.notMine(suggestion.id))) .pipe(map((results: RemoteData[]) => { return { success: results.filter((result) => result != null).length, @@ -234,7 +234,7 @@ export class SuggestionsService { * @param target * @return the researchUuid */ - public getTargetUuid(target: OpenaireSuggestionTarget): string { + public getTargetUuid(target: SuggestionTarget): string { const tokens = target.id.split(':'); return tokens.length === 2 ? tokens[1] : null; } @@ -243,7 +243,7 @@ export class SuggestionsService { * Interpolated params to build the notification suggestions notification. * @param suggestionTarget */ - public getNotificationSuggestionInterpolation(suggestionTarget: OpenaireSuggestionTarget): any { + public getNotificationSuggestionInterpolation(suggestionTarget: SuggestionTarget): any { return { count: suggestionTarget.total, source: this.translateService.instant(this.translateSuggestionSource(suggestionTarget.source)), @@ -266,7 +266,7 @@ export class SuggestionsService { * @param suggestion * @param collectionId */ - public resolveCollectionId(suggestion: OpenaireSuggestion, collectionId): string { + public resolveCollectionId(suggestion: Suggestion, collectionId): string { if (hasValue(collectionId)) { return collectionId; } @@ -280,13 +280,13 @@ export class SuggestionsService { * in the configuration. * @param suggestions */ - public isCollectionFixed(suggestions: OpenaireSuggestion[]): boolean { + public isCollectionFixed(suggestions: Suggestion[]): boolean { return this.getFixedCollectionIds(suggestions).length === 1; } - private getFixedCollectionIds(suggestions: OpenaireSuggestion[]): string[] { + private getFixedCollectionIds(suggestions: Suggestion[]): string[] { const collectionIds = {}; - suggestions.forEach((suggestion: OpenaireSuggestion) => { + suggestions.forEach((suggestion: Suggestion) => { const conf = environment.suggestion.find((suggestionConf: SuggestionConfig) => suggestionConf.source === suggestion.source); if (hasValue(conf)) { collectionIds[conf.collectionId] = true; diff --git a/src/app/suggestion-notifications/selectors.ts b/src/app/suggestion-notifications/selectors.ts index c5947e3196d..a20e9000253 100644 --- a/src/app/suggestion-notifications/selectors.ts +++ b/src/app/suggestion-notifications/selectors.ts @@ -12,7 +12,7 @@ import { QualityAssuranceSourceObject } from '../core/suggestion-notifications/q * @param {AppState} state Top level state. * @return {SuggestionNotificationsState} */ -const _getNotificationsState = createFeatureSelector('notifications'); +const _getNotificationsState = createFeatureSelector('suggestion-notifications'); // Quality Assurance topics // ---------------------------------------------------------------------------- diff --git a/src/app/suggestion-notifications/suggestion-notifications.module.ts b/src/app/suggestion-notifications/suggestion-notifications.module.ts index b69427d70db..2b2e41ece3e 100644 --- a/src/app/suggestion-notifications/suggestion-notifications.module.ts +++ b/src/app/suggestion-notifications/suggestion-notifications.module.ts @@ -34,13 +34,13 @@ import {SuggestionActionsComponent} from './reciter-suggestions/suggestion-actio import {SuggestionTargetsComponent} from './reciter-suggestions/suggestion-targets/suggestion-targets.component'; import {SuggestionTargetsStateService} from './reciter-suggestions/suggestion-targets/suggestion-targets.state.service'; import {SuggestionsService} from './reciter-suggestions/suggestions.service'; -import {OpenaireSuggestionsDataService} from '../core/suggestion-notifications/reciter-suggestions/openaire-suggestions-data.service'; +import {SuggestionsDataService} from '../core/suggestion-notifications/reciter-suggestions/suggestions-data.service'; const MODULES = [ CommonModule, SharedModule, CoreModule.forRoot(), - StoreModule.forFeature('notifications', suggestionNotificationsReducers, storeModuleConfig as StoreConfig), + StoreModule.forFeature('suggestion-notifications', suggestionNotificationsReducers, storeModuleConfig as StoreConfig), EffectsModule.forFeature(suggestionNotificationsEffects), TranslateModule ]; @@ -72,7 +72,7 @@ const PROVIDERS = [ QualityAssuranceEventRestService, SuggestionTargetsStateService, SuggestionsService, - OpenaireSuggestionsDataService + SuggestionsDataService ]; @NgModule({ diff --git a/src/app/suggestion-notifications/suggestion-notifications.reducer.ts b/src/app/suggestion-notifications/suggestion-notifications.reducer.ts index cdd072bb397..bab0984304d 100644 --- a/src/app/suggestion-notifications/suggestion-notifications.reducer.ts +++ b/src/app/suggestion-notifications/suggestion-notifications.reducer.ts @@ -21,4 +21,4 @@ export const suggestionNotificationsReducers: ActionReducerMap('notifications'); +export const suggestionNotificationsSelector = createFeatureSelector('suggestion-notifications'); diff --git a/src/app/suggestions-page/suggestions-page.component.spec.ts b/src/app/suggestions-page/suggestions-page.component.spec.ts index aef93326058..61e66a99cf7 100644 --- a/src/app/suggestions-page/suggestions-page.component.spec.ts +++ b/src/app/suggestions-page/suggestions-page.component.spec.ts @@ -9,9 +9,9 @@ import { of as observableOf } from 'rxjs'; import { SuggestionsPageComponent } from './suggestions-page.component'; import { SuggestionListElementComponent } from '../suggestion-notifications/reciter-suggestions/suggestion-list-element/suggestion-list-element.component'; import { SuggestionsService } from '../suggestion-notifications/reciter-suggestions/suggestions.service'; -import { getMockSuggestionNotificationsStateService, getMockSuggestionsService } from '../shared/mocks/openaire.mock'; +import { getMockSuggestionNotificationsStateService, getMockSuggestionsService } from '../shared/mocks/suggestion.mock'; import { buildPaginatedList, PaginatedList } from '../core/data/paginated-list.model'; -import { OpenaireSuggestion } from '../core/suggestion-notifications/reciter-suggestions/models/openaire-suggestion.model'; +import { Suggestion } from '../core/suggestion-notifications/reciter-suggestions/models/suggestion.model'; import { mockSuggestionPublicationOne, mockSuggestionPublicationTwo } from '../shared/mocks/reciter-suggestion.mock'; import { SuggestionEvidencesComponent } from '../suggestion-notifications/reciter-suggestions/suggestion-list-element/suggestion-evidences/suggestion-evidences.component'; import { ObjectKeysPipe } from '../shared/utils/object-keys-pipe'; @@ -38,7 +38,7 @@ describe('SuggestionPageComponent', () => { let scheduler: TestScheduler; const mockSuggestionsService = getMockSuggestionsService(); const mockSuggestionsTargetStateService = getMockSuggestionNotificationsStateService(); - const suggestionTargetsList: PaginatedList = buildPaginatedList(new PageInfo(), [mockSuggestionPublicationOne, mockSuggestionPublicationTwo]); + const suggestionTargetsList: PaginatedList = buildPaginatedList(new PageInfo(), [mockSuggestionPublicationOne, mockSuggestionPublicationTwo]); const router = new RouterStub(); const routeStub = { data: observableOf({ diff --git a/src/app/suggestions-page/suggestions-page.component.ts b/src/app/suggestions-page/suggestions-page.component.ts index e8e76ff606d..b4ca0108d5a 100644 --- a/src/app/suggestions-page/suggestions-page.component.ts +++ b/src/app/suggestions-page/suggestions-page.component.ts @@ -11,8 +11,8 @@ import { RemoteData } from '../core/data/remote-data'; import { getFirstSucceededRemoteDataPayload } from '../core/shared/operators'; import { SuggestionBulkResult, SuggestionsService } from '../suggestion-notifications/reciter-suggestions/suggestions.service'; import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model'; -import { OpenaireSuggestion } from '../core/suggestion-notifications/reciter-suggestions/models/openaire-suggestion.model'; -import { OpenaireSuggestionTarget } from '../core/suggestion-notifications/reciter-suggestions/models/openaire-suggestion-target.model'; +import { Suggestion } from '../core/suggestion-notifications/reciter-suggestions/models/suggestion.model'; +import { SuggestionTarget } from '../core/suggestion-notifications/reciter-suggestions/models/suggestion-target.model'; import { AuthService } from '../core/auth/auth.service'; import { SuggestionApproveAndImport } from '../suggestion-notifications/reciter-suggestions/suggestion-list-element/suggestion-list-element.component'; import { NotificationsService } from '../shared/notifications/notifications.service'; @@ -56,18 +56,18 @@ export class SuggestionsPageComponent implements OnInit { /** * A list of remote data objects of suggestions */ - suggestionsRD$: BehaviorSubject> = new BehaviorSubject>({} as any); + suggestionsRD$: BehaviorSubject> = new BehaviorSubject>({} as any); - targetRD$: Observable>; + targetRD$: Observable>; targetId$: Observable; - suggestionTarget: OpenaireSuggestionTarget; + suggestionTarget: SuggestionTarget; suggestionId: any; suggestionSource: any; researcherName: any; researcherUuid: any; - selectedSuggestions: { [id: string]: OpenaireSuggestion } = {}; + selectedSuggestions: { [id: string]: Suggestion } = {}; isBulkOperationPending = false; constructor( @@ -85,17 +85,17 @@ export class SuggestionsPageComponent implements OnInit { ngOnInit(): void { this.targetRD$ = this.route.data.pipe( - map((data: Data) => data.suggestionTargets as RemoteData), + map((data: Data) => data.suggestionTargets as RemoteData), redirectOn4xx(this.router, this.authService) ); this.targetId$ = this.targetRD$.pipe( getFirstSucceededRemoteDataPayload(), - map((target: OpenaireSuggestionTarget) => target.id) + map((target: SuggestionTarget) => target.id) ); this.targetRD$.pipe( getFirstSucceededRemoteDataPayload() - ).subscribe((suggestionTarget: OpenaireSuggestionTarget) => { + ).subscribe((suggestionTarget: SuggestionTarget) => { this.suggestionTarget = suggestionTarget; this.suggestionId = suggestionTarget.id; this.researcherName = suggestionTarget.display; @@ -135,7 +135,7 @@ export class SuggestionsPageComponent implements OnInit { ); }), take(1) - ).subscribe((results: PaginatedList) => { + ).subscribe((results: PaginatedList) => { this.processing$.next(false); this.suggestionsRD$.next(results); this.suggestionService.clearSuggestionRequests(); @@ -232,7 +232,7 @@ export class SuggestionsPageComponent implements OnInit { * @param object the suggestions * @param selected the new selected value for the suggestion */ - onSelected(object: OpenaireSuggestion, selected: boolean) { + onSelected(object: Suggestion, selected: boolean) { if (selected) { this.selectedSuggestions[object.id] = object; } else { @@ -244,7 +244,7 @@ export class SuggestionsPageComponent implements OnInit { * When Toggle Select All occurs. * @param suggestions all the visible suggestions inside the page */ - onToggleSelectAll(suggestions: OpenaireSuggestion[]) { + onToggleSelectAll(suggestions: Suggestion[]) { if ( this.getSelectedSuggestionsCount() > 0) { this.selectedSuggestions = {}; } else { @@ -265,7 +265,7 @@ export class SuggestionsPageComponent implements OnInit { * Return true if all the suggestion are configured with the same fixed collection in the configuration. * @param suggestions */ - isCollectionFixed(suggestions: OpenaireSuggestion[]): boolean { + isCollectionFixed(suggestions: Suggestion[]): boolean { return this.suggestionService.isCollectionFixed(suggestions); } diff --git a/src/app/suggestions-page/suggestions-page.module.ts b/src/app/suggestions-page/suggestions-page.module.ts index 726af26c0f3..a3f24f34682 100644 --- a/src/app/suggestions-page/suggestions-page.module.ts +++ b/src/app/suggestions-page/suggestions-page.module.ts @@ -5,7 +5,7 @@ import { SuggestionsPageComponent } from './suggestions-page.component'; import { SharedModule } from '../shared/shared.module'; import { SuggestionsPageRoutingModule } from './suggestions-page-routing.module'; import { SuggestionsService } from '../suggestion-notifications/reciter-suggestions/suggestions.service'; -import { OpenaireSuggestionsDataService } from '../core/suggestion-notifications/reciter-suggestions/openaire-suggestions-data.service'; +import { SuggestionsDataService } from '../core/suggestion-notifications/reciter-suggestions/suggestions-data.service'; import {SuggestionNotificationsModule} from '../suggestion-notifications/suggestion-notifications.module'; @NgModule({ @@ -17,7 +17,7 @@ import {SuggestionNotificationsModule} from '../suggestion-notifications/suggest SuggestionsPageRoutingModule ], providers: [ - OpenaireSuggestionsDataService, + SuggestionsDataService, SuggestionsService ] }) diff --git a/src/app/suggestions-page/suggestions-page.resolver.ts b/src/app/suggestions-page/suggestions-page.resolver.ts index cf22b04aa68..6cc89a72d43 100644 --- a/src/app/suggestions-page/suggestions-page.resolver.ts +++ b/src/app/suggestions-page/suggestions-page.resolver.ts @@ -6,15 +6,15 @@ import { find } from 'rxjs/operators'; import { RemoteData } from '../core/data/remote-data'; import { hasValue } from '../shared/empty.util'; -import { OpenaireSuggestionsDataService } from '../core/suggestion-notifications/reciter-suggestions/openaire-suggestions-data.service'; -import { OpenaireSuggestionTarget } from '../core/suggestion-notifications/reciter-suggestions/models/openaire-suggestion-target.model'; +import { SuggestionsDataService } from '../core/suggestion-notifications/reciter-suggestions/suggestions-data.service'; +import { SuggestionTarget } from '../core/suggestion-notifications/reciter-suggestions/models/suggestion-target.model'; /** * This class represents a resolver that requests a specific collection before the route is activated */ @Injectable() -export class SuggestionsPageResolver implements Resolve> { - constructor(private suggestionsDataService: OpenaireSuggestionsDataService) { +export class SuggestionsPageResolver implements Resolve> { + constructor(private suggestionsDataService: SuggestionsDataService) { } /** @@ -24,7 +24,7 @@ export class SuggestionsPageResolver implements Resolve> Emits the found collection based on the parameters in the current route, * or an error if something went wrong */ - resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable> { + resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable> { return this.suggestionsDataService.getTargetById(route.params.targetId).pipe( find((RD) => hasValue(RD.hasFailed) || RD.hasSucceeded), ); From 9b556fd7039449c9b8a762cb335f0b1b813001da Mon Sep 17 00:00:00 2001 From: Francesco Bacchelli Date: Tue, 22 Aug 2023 11:16:30 +0200 Subject: [PATCH 05/34] Merge branch CST-5249_suggestion of https://github.com/4Science/DSpace into CST-11299 --- .../builders/remote-data-build.service.ts | 1 + src/app/core/core.module.ts | 6 +- .../suggestion-objects.resource-type.ts | 16 ----- .../suggestion-source-object.resource-type.ts | 9 +++ .../models/suggestion-source.model.ts | 2 +- .../suggestion-target-object.resource-type.ts | 9 +++ .../models/suggestion-target.model.ts | 2 +- .../source/suggestion-source-data.service.ts | 2 +- .../suggestions-data.service.ts | 4 +- .../target/suggestion-target-data.service.ts | 3 +- src/app/home-page/home-page.component.html | 1 + src/app/home-page/home-page.module.ts | 18 ++--- src/app/menu.resolver.ts | 12 ++++ .../my-dspace-page.component.html | 1 + .../my-dspace-page/my-dspace-page.module.ts | 18 ++--- .../profile-page/profile-page.component.html | 1 + src/app/profile-page/profile-page.module.ts | 16 +++-- .../dso-selector-modal-wrapper.component.ts | 18 ++++- .../reciter-suggestions/selectors.ts | 10 +-- .../suggestion-targets.component.ts | 1 + .../suggestion-targets.effects.ts | 2 +- .../suggestion-targets.state.service.ts | 14 ++-- .../suggestions-popup.component.ts | 2 + .../suggestions.service.ts | 4 +- .../suggestion-notifications.module.ts | 12 ++++ src/assets/i18n/en.json5 | 68 +++++++++++++++++++ src/themes/custom/lazy-theme.module.ts | 3 + 27 files changed, 193 insertions(+), 62 deletions(-) create mode 100644 src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-source-object.resource-type.ts create mode 100644 src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-target-object.resource-type.ts diff --git a/src/app/core/cache/builders/remote-data-build.service.ts b/src/app/core/cache/builders/remote-data-build.service.ts index 075bf3ca0ca..5b5e362406c 100644 --- a/src/app/core/cache/builders/remote-data-build.service.ts +++ b/src/app/core/cache/builders/remote-data-build.service.ts @@ -161,6 +161,7 @@ export class RemoteDataBuildService { } else { // in case the elements of the paginated list were already filled in, because they're UnCacheableObjects paginatedList.page = paginatedList.page + .filter((obj: any) => obj != null) .map((obj: any) => this.plainObjectToInstance(obj)) .map((obj: any) => this.linkService.resolveLinks(obj, ...pageLink.linksToFollow) diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index e176af7d550..7acf132df9b 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -185,6 +185,8 @@ import { FlatBrowseDefinition } from './shared/flat-browse-definition.model'; import { ValueListBrowseDefinition } from './shared/value-list-browse-definition.model'; import { NonHierarchicalBrowseDefinition } from './shared/non-hierarchical-browse-definition'; import { BulkAccessConditionOptions } from './config/models/bulk-access-condition-options.model'; +import { SuggestionTarget } from './suggestion-notifications/reciter-suggestions/models/suggestion-target.model'; +import { SuggestionSource } from './suggestion-notifications/reciter-suggestions/models/suggestion-source.model'; /** * When not in production, endpoint responses can be mocked for testing purposes @@ -386,7 +388,9 @@ export const models = IdentifierData, Subscription, ItemRequest, - BulkAccessConditionOptions + BulkAccessConditionOptions, + SuggestionTarget, + SuggestionSource ]; @NgModule({ diff --git a/src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-objects.resource-type.ts b/src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-objects.resource-type.ts index e31006959ff..8f87027a8c5 100644 --- a/src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-objects.resource-type.ts +++ b/src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-objects.resource-type.ts @@ -1,21 +1,5 @@ import { ResourceType } from '../../../shared/resource-type'; -/** - * The resource type for the Suggestion Target object - * - * Needs to be in a separate file to prevent circular - * dependencies in webpack. - */ -export const SUGGESTION_TARGET = new ResourceType('suggestiontarget'); - -/** - * The resource type for the Suggestion Source object - * - * Needs to be in a separate file to prevent circular - * dependencies in webpack. - */ -export const SUGGESTION_SOURCE = new ResourceType('suggestionsource'); - /** * The resource type for the Suggestion object * diff --git a/src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-source-object.resource-type.ts b/src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-source-object.resource-type.ts new file mode 100644 index 00000000000..2e26fe4301c --- /dev/null +++ b/src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-source-object.resource-type.ts @@ -0,0 +1,9 @@ +import { ResourceType } from '../../../shared/resource-type'; + +/** + * The resource type for the Suggestion Source object + * + * Needs to be in a separate file to prevent circular + * dependencies in webpack. + */ +export const SUGGESTION_SOURCE = new ResourceType('suggestionsource'); diff --git a/src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-source.model.ts b/src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-source.model.ts index 64ddf9863c8..007520800d3 100644 --- a/src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-source.model.ts +++ b/src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-source.model.ts @@ -1,6 +1,6 @@ import { autoserialize, deserialize } from 'cerialize'; -import { SUGGESTION_SOURCE } from './suggestion-objects.resource-type'; +import { SUGGESTION_SOURCE } from './suggestion-source-object.resource-type'; import { excludeFromEquals } from '../../../utilities/equals.decorators'; import { ResourceType } from '../../../shared/resource-type'; import { HALLink } from '../../../shared/hal-link.model'; diff --git a/src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-target-object.resource-type.ts b/src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-target-object.resource-type.ts new file mode 100644 index 00000000000..71dd41912ac --- /dev/null +++ b/src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-target-object.resource-type.ts @@ -0,0 +1,9 @@ +import { ResourceType } from '../../../shared/resource-type'; + +/** + * The resource type for the Suggestion Target object + * + * Needs to be in a separate file to prevent circular + * dependencies in webpack. + */ +export const SUGGESTION_TARGET = new ResourceType('suggestiontarget'); diff --git a/src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-target.model.ts b/src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-target.model.ts index 970a8e7e285..2afe170e777 100644 --- a/src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-target.model.ts +++ b/src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-target.model.ts @@ -2,7 +2,7 @@ import { autoserialize, deserialize } from 'cerialize'; import { CacheableObject } from '../../../cache/cacheable-object.model'; -import { SUGGESTION_TARGET } from './suggestion-objects.resource-type'; +import { SUGGESTION_TARGET } from './suggestion-target-object.resource-type'; import { excludeFromEquals } from '../../../utilities/equals.decorators'; import { ResourceType } from '../../../shared/resource-type'; import { HALLink } from '../../../shared/hal-link.model'; diff --git a/src/app/core/suggestion-notifications/reciter-suggestions/source/suggestion-source-data.service.ts b/src/app/core/suggestion-notifications/reciter-suggestions/source/suggestion-source-data.service.ts index e4286c6b5b8..c3e142044e1 100644 --- a/src/app/core/suggestion-notifications/reciter-suggestions/source/suggestion-source-data.service.ts +++ b/src/app/core/suggestion-notifications/reciter-suggestions/source/suggestion-source-data.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; import { dataService } from '../../../data/base/data-service.decorator'; -import { SUGGESTION_SOURCE } from '../models/suggestion-objects.resource-type'; +import { SUGGESTION_SOURCE } from '../models/suggestion-source-object.resource-type'; import { IdentifiableDataService } from '../../../data/base/identifiable-data.service'; import { SuggestionSource } from '../models/suggestion-source.model'; import { FindAllData, FindAllDataImpl } from '../../../data/base/find-all-data'; diff --git a/src/app/core/suggestion-notifications/reciter-suggestions/suggestions-data.service.ts b/src/app/core/suggestion-notifications/reciter-suggestions/suggestions-data.service.ts index abdcbca0c16..944a13e3a3c 100644 --- a/src/app/core/suggestion-notifications/reciter-suggestions/suggestions-data.service.ts +++ b/src/app/core/suggestion-notifications/reciter-suggestions/suggestions-data.service.ts @@ -168,8 +168,8 @@ export class SuggestionsDataService { ...linksToFollow: FollowLinkConfig[] ): Observable>> { options.searchParams = [new RequestParam('target', userId)]; - - return this.suggestionTargetsDataService.getTargetsByUser(this.searchFindByTargetMethod, options, ...linksToFollow); + //return this.suggestionTargetsDataService.getTargetsByUser(this.searchFindByTargetMethod, options, ...linksToFollow); + return this.suggestionTargetsDataService.getTargetsByUser(userId, options, ...linksToFollow); } /** diff --git a/src/app/core/suggestion-notifications/reciter-suggestions/target/suggestion-target-data.service.ts b/src/app/core/suggestion-notifications/reciter-suggestions/target/suggestion-target-data.service.ts index 372d1f9917c..ce5f131c1d5 100644 --- a/src/app/core/suggestion-notifications/reciter-suggestions/target/suggestion-target-data.service.ts +++ b/src/app/core/suggestion-notifications/reciter-suggestions/target/suggestion-target-data.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; import { dataService } from '../../../data/base/data-service.decorator'; -import { SUGGESTION_TARGET } from '../models/suggestion-objects.resource-type'; + import { IdentifiableDataService } from '../../../data/base/identifiable-data.service'; import { SuggestionTarget } from '../models/suggestion-target.model'; import { FindAllData, FindAllDataImpl } from '../../../data/base/find-all-data'; @@ -20,6 +20,7 @@ import { Observable } from 'rxjs/internal/Observable'; import { RequestParam } from '../../../cache/models/request-param.model'; import { SearchData, SearchDataImpl } from '../../../data/base/search-data'; import { DefaultChangeAnalyzer } from '../../../data/default-change-analyzer.service'; +import { SUGGESTION_TARGET } from '../models/suggestion-target-object.resource-type'; @Injectable() @dataService(SUGGESTION_TARGET) diff --git a/src/app/home-page/home-page.component.html b/src/app/home-page/home-page.component.html index caa86ac290a..49329b3f044 100644 --- a/src/app/home-page/home-page.component.html +++ b/src/app/home-page/home-page.component.html @@ -7,3 +7,4 @@ + diff --git a/src/app/home-page/home-page.module.ts b/src/app/home-page/home-page.module.ts index 1681abd8058..00f9dbd8f9b 100644 --- a/src/app/home-page/home-page.module.ts +++ b/src/app/home-page/home-page.module.ts @@ -13,6 +13,7 @@ import { RecentItemListComponent } from './recent-item-list/recent-item-list.com import { JournalEntitiesModule } from '../entity-groups/journal-entities/journal-entities.module'; import { ResearchEntitiesModule } from '../entity-groups/research-entities/research-entities.module'; import { ThemedTopLevelCommunityListComponent } from './top-level-community-list/themed-top-level-community-list.component'; +import { SuggestionNotificationsModule } from '../suggestion-notifications/suggestion-notifications.module'; const DECLARATIONS = [ HomePageComponent, @@ -25,14 +26,15 @@ const DECLARATIONS = [ ]; @NgModule({ - imports: [ - CommonModule, - SharedModule.withEntryComponents(), - JournalEntitiesModule.withEntryComponents(), - ResearchEntitiesModule.withEntryComponents(), - HomePageRoutingModule, - StatisticsModule.forRoot() - ], + imports: [ + CommonModule, + SharedModule.withEntryComponents(), + JournalEntitiesModule.withEntryComponents(), + ResearchEntitiesModule.withEntryComponents(), + HomePageRoutingModule, + StatisticsModule.forRoot(), + SuggestionNotificationsModule + ], declarations: [ ...DECLARATIONS, ], diff --git a/src/app/menu.resolver.ts b/src/app/menu.resolver.ts index 7a3a16d6265..70e2b6462f8 100644 --- a/src/app/menu.resolver.ts +++ b/src/app/menu.resolver.ts @@ -47,6 +47,7 @@ import { import { ExportBatchSelectorComponent } from './shared/dso-selector/modal-wrappers/export-batch-selector/export-batch-selector.component'; +import { NOTIFICATIONS_RECITER_SUGGESTION_PATH } from './admin/admin-notifications/admin-notifications-routing-paths'; /** * Creates all of the app's menus @@ -555,6 +556,17 @@ export class MenuResolver implements Resolve { link: '/admin/notifications/quality-assurance' } as LinkMenuItemModel, }, + { + id: 'notifications_reciter', + parentID: 'notifications', + active: false, + visible: authorized, + model: { + type: MenuItemType.LINK, + text: 'menu.section.notifications_reciter', + link: '/admin/notifications/' + NOTIFICATIONS_RECITER_SUGGESTION_PATH + } as LinkMenuItemModel, + }, /* Admin Search */ { id: 'admin_search', diff --git a/src/app/my-dspace-page/my-dspace-page.component.html b/src/app/my-dspace-page/my-dspace-page.component.html index ea5784170fb..c5e49b0cece 100644 --- a/src/app/my-dspace-page/my-dspace-page.component.html +++ b/src/app/my-dspace-page/my-dspace-page.component.html @@ -1,5 +1,6 @@
+
{{'profile.head' | translate}}
+ diff --git a/src/app/profile-page/profile-page.module.ts b/src/app/profile-page/profile-page.module.ts index 0e2902de33c..c5bfad17138 100644 --- a/src/app/profile-page/profile-page.module.ts +++ b/src/app/profile-page/profile-page.module.ts @@ -12,16 +12,18 @@ import { ThemedProfilePageComponent } from './themed-profile-page.component'; import { FormModule } from '../shared/form/form.module'; import { UiSwitchModule } from 'ngx-ui-switch'; import { ProfileClaimItemModalComponent } from './profile-claim-item-modal/profile-claim-item-modal.component'; +import { SuggestionNotificationsModule } from '../suggestion-notifications/suggestion-notifications.module'; @NgModule({ - imports: [ - ProfilePageRoutingModule, - CommonModule, - SharedModule, - FormModule, - UiSwitchModule - ], + imports: [ + ProfilePageRoutingModule, + CommonModule, + SharedModule, + FormModule, + UiSwitchModule, + SuggestionNotificationsModule + ], exports: [ ProfilePageComponent, ThemedProfilePageComponent, diff --git a/src/app/shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component.ts b/src/app/shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component.ts index 3f81687c9f8..6798449094d 100644 --- a/src/app/shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component.ts +++ b/src/app/shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { ActivatedRoute, ActivatedRouteSnapshot } from '@angular/router'; import { DSpaceObject } from '../../../core/shared/dspace-object.model'; import { RemoteData } from '../../../core/data/remote-data'; @@ -29,6 +29,11 @@ export abstract class DSOSelectorModalWrapperComponent implements OnInit { */ @Input() dsoRD: RemoteData; + /** + * Representing if component should emit value of selected entries or navigate + */ + @Input() emitOnly = false; + /** * Optional header to display above the selection list * Supports i18n keys @@ -50,6 +55,11 @@ export abstract class DSOSelectorModalWrapperComponent implements OnInit { */ action: SelectorActionType; + /** + * Event emitted when a DSO entry is selected if emitOnly is set to true + */ + @Output() select: EventEmitter = new EventEmitter(); + /** * Default DSO ordering */ @@ -93,7 +103,11 @@ export abstract class DSOSelectorModalWrapperComponent implements OnInit { */ selectObject(dso: DSpaceObject) { this.close(); - this.navigate(dso); + if (this.emitOnly) { + this.select.emit(dso); + } else { + this.navigate(dso); + } } /** diff --git a/src/app/suggestion-notifications/reciter-suggestions/selectors.ts b/src/app/suggestion-notifications/reciter-suggestions/selectors.ts index cbb99fe8697..ee906132714 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/selectors.ts +++ b/src/app/suggestion-notifications/reciter-suggestions/selectors.ts @@ -27,7 +27,7 @@ export function reciterSuggestionTargetStateSelector(): MemoizedSelector { return subStateSelector(reciterSuggestionTargetStateSelector(), 'targets'); @@ -47,7 +47,7 @@ export const isReciterSuggestionTargetLoadedSelector = createSelector(_getRecite * @function isDeduplicationSetsProcessingSelector * @return {boolean} */ -export const isreciterSuggestionTargetProcessingSelector = createSelector(_getReciterSuggestionTargetState, +export const isReciterSuggestionTargetProcessingSelector = createSelector(_getReciterSuggestionTargetState, (state: SuggestionNotificationsState) => state.suggestionTarget.processing ); @@ -56,7 +56,7 @@ export const isreciterSuggestionTargetProcessingSelector = createSelector(_getRe * @function getreciterSuggestionTargetTotalPagesSelector * @return {number} */ -export const getreciterSuggestionTargetTotalPagesSelector = createSelector(_getReciterSuggestionTargetState, +export const getReciterSuggestionTargetTotalPagesSelector = createSelector(_getReciterSuggestionTargetState, (state: SuggestionNotificationsState) => state.suggestionTarget.totalPages ); @@ -65,7 +65,7 @@ export const getreciterSuggestionTargetTotalPagesSelector = createSelector(_getR * @function getreciterSuggestionTargetCurrentPageSelector * @return {number} */ -export const getreciterSuggestionTargetCurrentPageSelector = createSelector(_getReciterSuggestionTargetState, +export const getReciterSuggestionTargetCurrentPageSelector = createSelector(_getReciterSuggestionTargetState, (state: SuggestionNotificationsState) => state.suggestionTarget.currentPage ); @@ -74,7 +74,7 @@ export const getreciterSuggestionTargetCurrentPageSelector = createSelector(_get * @function getreciterSuggestionTargetTotalsSelector * @return {number} */ -export const getreciterSuggestionTargetTotalsSelector = createSelector(_getReciterSuggestionTargetState, +export const getReciterSuggestionTargetTotalsSelector = createSelector(_getReciterSuggestionTargetState, (state: SuggestionNotificationsState) => state.suggestionTarget.totalElements ); diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.component.ts b/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.component.ts index cfa168e2b83..e5524765da0 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.component.ts +++ b/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.component.ts @@ -136,6 +136,7 @@ export class SuggestionTargetsComponent implements OnInit { distinctUntilChanged(), take(1) ).subscribe((options: PaginationComponentOptions) => { + console.log('HELLO suggestion called!', options); this.suggestionTargetsStateService.dispatchRetrieveReciterSuggestionTargets( this.source, options.pageSize, diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.effects.ts b/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.effects.ts index 3718e0cfad4..6ab587a6c21 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.effects.ts +++ b/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.effects.ts @@ -62,7 +62,7 @@ export class SuggestionTargetsEffects { /** * Fetch the current user suggestion */ - refreshUserTargets$ = createEffect(() => this.actions$.pipe( + RefreshUserSuggestionsAction = createEffect(() => this.actions$.pipe( ofType(SuggestionTargetActionTypes.REFRESH_USER_SUGGESTIONS), switchMap((action: RefreshUserSuggestionsAction) => { return this.store$.select((state: any) => state.core.auth.userId) diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.state.service.ts b/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.state.service.ts index 97792df6af7..fec9e01f3c7 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.state.service.ts +++ b/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.state.service.ts @@ -7,10 +7,10 @@ import { map } from 'rxjs/operators'; import { getCurrentUserSuggestionTargetsSelector, getCurrentUserSuggestionTargetsVisitedSelector, - getreciterSuggestionTargetCurrentPageSelector, - getreciterSuggestionTargetTotalsSelector, + getReciterSuggestionTargetCurrentPageSelector, + getReciterSuggestionTargetTotalsSelector, isReciterSuggestionTargetLoadedSelector, - isreciterSuggestionTargetProcessingSelector, + isReciterSuggestionTargetProcessingSelector, reciterSuggestionTargetObjectSelector } from '../selectors'; import { SuggestionTarget } from '../../../core/suggestion-notifications/reciter-suggestions/models/suggestion-target.model'; @@ -74,7 +74,7 @@ export class SuggestionTargetsStateService { * 'true' if there are operations running on the targets (ex.: a REST call), 'false' otherwise. */ public isReciterSuggestionTargetsProcessing(): Observable { - return this.store.pipe(select(isreciterSuggestionTargetProcessingSelector)); + return this.store.pipe(select(isReciterSuggestionTargetProcessingSelector)); } /** @@ -84,7 +84,7 @@ export class SuggestionTargetsStateService { * The number of the Reciter Suggestion Targets pages. */ public getReciterSuggestionTargetsTotalPages(): Observable { - return this.store.pipe(select(getreciterSuggestionTargetTotalsSelector)); + return this.store.pipe(select(getReciterSuggestionTargetTotalsSelector)); } /** @@ -94,7 +94,7 @@ export class SuggestionTargetsStateService { * The number of the current Reciter Suggestion Targets page. */ public getReciterSuggestionTargetsCurrentPage(): Observable { - return this.store.pipe(select(getreciterSuggestionTargetCurrentPageSelector)); + return this.store.pipe(select(getReciterSuggestionTargetCurrentPageSelector)); } /** @@ -104,7 +104,7 @@ export class SuggestionTargetsStateService { * The number of the Reciter Suggestion Targets. */ public getReciterSuggestionTargetsTotals(): Observable { - return this.store.pipe(select(getreciterSuggestionTargetTotalsSelector)); + return this.store.pipe(select(getReciterSuggestionTargetTotalsSelector)); } /** diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestions-popup/suggestions-popup.component.ts b/src/app/suggestion-notifications/reciter-suggestions/suggestions-popup/suggestions-popup.component.ts index 0195f074ae8..c0f94cadce3 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/suggestions-popup/suggestions-popup.component.ts +++ b/src/app/suggestion-notifications/reciter-suggestions/suggestions-popup/suggestions-popup.component.ts @@ -31,6 +31,8 @@ export class SuggestionsPopupComponent implements OnInit, OnDestroy { } public initializePopup() { + console.log('POPUP INIT dispatchRefreshUserSuggestionsAction'); + this.reciterSuggestionStateService.dispatchRefreshUserSuggestionsAction(); const notifier = new Subject(); this.subscription = combineLatest([ this.reciterSuggestionStateService.getCurrentUserSuggestionTargets(), diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestions.service.ts b/src/app/suggestion-notifications/reciter-suggestions/suggestions.service.ts index 662b4c03dec..b760ddf34db 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/suggestions.service.ts +++ b/src/app/suggestion-notifications/reciter-suggestions/suggestions.service.ts @@ -49,7 +49,9 @@ export class SuggestionsService { /** * Initialize the service variables. * @param {AuthService} authService - * @param {ResearcherProfileService} researcherProfileService + * @param {ResearcherProfileDataService} researcherProfileService + * @param {SuggestionSourceDataService} suggestionSourceDataService + * @param {SuggestionTargetDataService} suggestionTargetDataService * @param {SuggestionsDataService} suggestionsDataService */ constructor( diff --git a/src/app/suggestion-notifications/suggestion-notifications.module.ts b/src/app/suggestion-notifications/suggestion-notifications.module.ts index 4369d17375c..fa9d81eb756 100644 --- a/src/app/suggestion-notifications/suggestion-notifications.module.ts +++ b/src/app/suggestion-notifications/suggestion-notifications.module.ts @@ -40,6 +40,15 @@ import { } from './reciter-suggestions/suggestions-notification/suggestions-notification.component'; import { SuggestionsService } from './reciter-suggestions/suggestions.service'; import { SuggestionsDataService } from '../core/suggestion-notifications/reciter-suggestions/suggestions-data.service'; +import { + SuggestionSourceDataService +} from '../core/suggestion-notifications/reciter-suggestions/source/suggestion-source-data.service'; +import { + SuggestionTargetDataService +} from '../core/suggestion-notifications/reciter-suggestions/target/suggestion-target-data.service'; +import { + SuggestionTargetsStateService +} from './reciter-suggestions/suggestion-targets/suggestion-targets.state.service'; const MODULES = [ CommonModule, @@ -77,6 +86,9 @@ const PROVIDERS = [ QualityAssuranceSourceDataService, QualityAssuranceEventDataService, SuggestionsService, + SuggestionSourceDataService, + SuggestionTargetDataService, + SuggestionTargetsStateService, SuggestionsDataService ]; diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 4c13ec73d14..0d90ca693d9 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -24,6 +24,10 @@ "404.page-not-found": "page not found", + "admin.notifications.recitersuggestion.breadcrumbs": "Suggestions", + + "admin.notifications.recitersuggestion.page.title": "Suggestions", + "error-page.description.401": "unauthorized", "error-page.description.403": "forbidden", @@ -3052,6 +3056,12 @@ "mydspace.view-btn": "View", + "mydspace.import": "Import", + + "mydspace.notification.suggestion": "We found {{count}} publications
in the {{source}} that seems to be related to your profile.
Please review the suggestions", + + "mydspace.notification.suggestion.page": "We found {{count}} {{type}} in the {{source}} that seems to be related to your profile. Please review the suggestions.", + "nav.browse.header": "All of DSpace", "nav.community-browse.header": "By Community", @@ -3514,6 +3524,64 @@ "media-viewer.playlist": "Playlist", + "reciter.suggestion.loading": "Loading ...", + + "reciter.suggestion.title": "Suggestions", + + "reciter.suggestion.targets.description": "Below you can see all the suggestions ", + + "reciter.suggestion.targets": "Current Suggestions", + + "reciter.suggestion.table.name": "Researcher Name", + + "reciter.suggestion.table.actions": "Actions", + + "reciter.suggestion.button.review": "Review {{ total }} suggestion(s)", + + "reciter.suggestion.noTargets": "No target found.", + + "reciter.suggestion.target.error.service.retrieve": "An error occurred while loading the Suggestion targets", + + "reciter.suggestion.evidence.type": "Type", + + "reciter.suggestion.evidence.score": "Score", + + "reciter.suggestion.evidence.notes": "Notes", + + "reciter.suggestion.approveAndImport": "Approve & import", + + "reciter.suggestion.approveAndImport.success": "The suggestion has been imported successfully. View.", + + "reciter.suggestion.approveAndImport.bulk": "Approve & import Selected", + + "reciter.suggestion.approveAndImport.bulk.success": "{{ count }} suggestions have been imported successfully ", + + "reciter.suggestion.approveAndImport.bulk.error": "{{ count }} suggestions haven't been imported due to unexpected server errors", + + "reciter.suggestion.notMine": "Not mine", + + "reciter.suggestion.notMine.success": "The suggestion has been discarded", + + "reciter.suggestion.notMine.bulk": "Not mine Selected", + + "reciter.suggestion.notMine.bulk.success": "{{ count }} suggestions have been discarded ", + + "reciter.suggestion.notMine.bulk.error": "{{ count }} suggestions haven't been discarded due to unexpected server errors", + + "reciter.suggestion.seeEvidence": "See evidence", + + "reciter.suggestion.hideEvidence": "Hide evidence", + + "reciter.suggestion.suggestionFor": "Suggestion for", + + "reciter.suggestion.source.oaire": "OpenAIRE Graph", + + "reciter.suggestion.from.source": "from the ", + + "reciter.suggestion.totalScore": "Total Score", + + "reciter.suggestion.type.oaire": "OpenAIRE", + "register-email.title": "New user registration", "register-page.create-profile.header": "Create Profile", diff --git a/src/themes/custom/lazy-theme.module.ts b/src/themes/custom/lazy-theme.module.ts index edb3f5478c9..771e9e1b088 100644 --- a/src/themes/custom/lazy-theme.module.ts +++ b/src/themes/custom/lazy-theme.module.ts @@ -156,6 +156,8 @@ import { ItemStatusComponent } from './app/item-page/edit-item-page/item-status/ import { EditBitstreamPageComponent } from './app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component'; import { FormModule } from '../../app/shared/form/form.module'; import { RequestCopyModule } from 'src/app/request-copy/request-copy.module'; +import { SuggestionNotificationsModule } from '../../app/suggestion-notifications/suggestion-notifications.module'; + const DECLARATIONS = [ FileSectionComponent, @@ -299,6 +301,7 @@ const DECLARATIONS = [ NgxGalleryModule, FormModule, RequestCopyModule, + SuggestionNotificationsModule ], declarations: DECLARATIONS, exports: [ From e5cf16c4896a29a5ae40d3de5545ef8597a7197e Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Thu, 21 Dec 2023 14:52:39 +0100 Subject: [PATCH 06/34] fix lint --- src/app/core/data/data.service.ts | 1 - src/app/shared/mocks/suggestion.mock.ts | 1 - src/app/suggestions-page/suggestions-page-routing.module.ts | 1 - 3 files changed, 3 deletions(-) diff --git a/src/app/core/data/data.service.ts b/src/app/core/data/data.service.ts index 1433ebacce2..43b60f874d0 100644 --- a/src/app/core/data/data.service.ts +++ b/src/app/core/data/data.service.ts @@ -25,7 +25,6 @@ import { RequestParam } from '../cache/models/request-param.model'; import { ObjectCacheEntry } from '../cache/object-cache.reducer'; import { ObjectCacheService } from '../cache/object-cache.service'; import { DSpaceSerializer } from '../dspace-rest/dspace.serializer'; -import { DSpaceObject } from '../shared/dspace-object.model'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { getFirstCompletedRemoteData, getFirstSucceededRemoteData, getRemoteDataPayload } from '../shared/operators'; import { URLCombiner } from '../url-combiner/url-combiner'; diff --git a/src/app/shared/mocks/suggestion.mock.ts b/src/app/shared/mocks/suggestion.mock.ts index fd1a7c41f1e..8b1ab7acd55 100644 --- a/src/app/shared/mocks/suggestion.mock.ts +++ b/src/app/shared/mocks/suggestion.mock.ts @@ -1,7 +1,6 @@ import { DSpaceObject } from '../../core/shared/dspace-object.model'; import { Item } from '../../core/shared/item.model'; import { SearchResult } from '../search/models/search-result.model'; -import { SuggestionsService } from '../../suggestion-notifications/reciter-suggestions/suggestions.service'; // REST Mock --------------------------------------------------------------------- // ------------------------------------------------------------------------------- diff --git a/src/app/suggestions-page/suggestions-page-routing.module.ts b/src/app/suggestions-page/suggestions-page-routing.module.ts index 20ed658707e..05dc6321b70 100644 --- a/src/app/suggestions-page/suggestions-page-routing.module.ts +++ b/src/app/suggestions-page/suggestions-page-routing.module.ts @@ -5,7 +5,6 @@ import { SuggestionsPageResolver } from './suggestions-page.resolver'; import { SuggestionsPageComponent } from './suggestions-page.component'; import { AuthenticatedGuard } from '../core/auth/authenticated.guard'; import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver'; -import { SiteAdministratorGuard } from '../core/data/feature-authorization/feature-authorization-guard/site-administrator.guard'; @NgModule({ imports: [ From 4106e438d6f1cc304bd7bd1ba1c2ff1f90dcedc6 Mon Sep 17 00:00:00 2001 From: frabacche Date: Fri, 22 Dec 2023 12:18:58 +0100 Subject: [PATCH 07/34] CST-5249_suggestion test fixes --- .../workspaceitem-data.service.spec.ts | 11 ++++-- .../submission/workspaceitem-data.service.ts | 37 ++++++++++++++++++- .../suggestions-popup.component.spec.ts | 1 + src/config/app-config.interface.ts | 2 +- src/environments/environment.test.ts | 1 + 5 files changed, 46 insertions(+), 6 deletions(-) diff --git a/src/app/core/submission/workspaceitem-data.service.spec.ts b/src/app/core/submission/workspaceitem-data.service.spec.ts index e766a6a039c..50bc03a6538 100644 --- a/src/app/core/submission/workspaceitem-data.service.spec.ts +++ b/src/app/core/submission/workspaceitem-data.service.spec.ts @@ -77,23 +77,26 @@ describe('WorkspaceitemDataService test', () => { const notificationsService = {} as NotificationsService; const http = {} as HttpClient; const comparator = {} as any; - const comparatorEntry = {} as any; const store = {} as Store; const pageInfo = new PageInfo(); function initTestService() { hrefOnlyDataService = getMockHrefOnlyDataService(); return new WorkspaceitemDataService( + comparator, + halService, + http, + notificationsService, requestService, rdbService, objectCache, - halService, - notificationsService, + store ); } describe('composition', () => { - const initService = () => new WorkspaceitemDataService(null, null, null, null, null); + const initService = () => new WorkspaceitemDataService(null, null, + null, null, null, null, null, null); testSearchDataImplementation(initService); testDeleteDataImplementation(initService); }); diff --git a/src/app/core/submission/workspaceitem-data.service.ts b/src/app/core/submission/workspaceitem-data.service.ts index 8a036f64439..7352718c537 100644 --- a/src/app/core/submission/workspaceitem-data.service.ts +++ b/src/app/core/submission/workspaceitem-data.service.ts @@ -23,15 +23,19 @@ import {hasValue} from '../../shared/empty.util'; import { IdentifiableDataService } from '../data/base/identifiable-data.service'; import { NoContent } from '../shared/NoContent.model'; import { DeleteData, DeleteDataImpl } from '../data/base/delete-data'; +import { SearchData, SearchDataImpl } from '../data/base/search-data'; +import { Bitstream } from '../shared/bitstream.model'; +import { PaginatedList } from '../data/paginated-list.model'; /** * A service that provides methods to make REST requests with workspaceitems endpoint. */ @Injectable() @dataService(WorkspaceItem.type) -export class WorkspaceitemDataService extends IdentifiableDataService { +export class WorkspaceitemDataService extends IdentifiableDataService implements SearchData, DeleteData { protected linkPath = 'workspaceitems'; protected searchByItemLinkPath = 'item'; + private searchData: SearchDataImpl; private deleteData: DeleteData; constructor( @@ -45,6 +49,7 @@ export class WorkspaceitemDataService extends IdentifiableDataService) { super('workspaceitems', requestService, rdbService, objectCache, halService); this.deleteData = new DeleteDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive, this.constructIdEndpoint); + this.searchData = new SearchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive); } public delete(objectId: string, copyVirtualMetadata?: string[]): Observable> { @@ -93,4 +98,34 @@ export class WorkspaceitemDataService extends IdentifiableDataService>} + * Return an observable that emits response from the server + */ + public searchBy(searchMethod: string, options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig[]): Observable>> { + return this.searchData.searchBy(searchMethod, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow); + } + + /** + * Delete an existing object on the server + * @param href The self link of the object to be removed + * @param copyVirtualMetadata (optional parameter) the identifiers of the relationship types for which the virtual + * metadata should be saved as real metadata + * @return A RemoteData observable with an empty payload, but still representing the state of the request: statusCode, + * errorMessage, timeCompleted, etc + * Only emits once all request related to the DSO has been invalidated. + */ + deleteByHref(href: string, copyVirtualMetadata?: string[]): Observable> { + return this.deleteData.deleteByHref(href, copyVirtualMetadata); + } } diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestions-popup/suggestions-popup.component.spec.ts b/src/app/suggestion-notifications/reciter-suggestions/suggestions-popup/suggestions-popup.component.spec.ts index 67678354caf..e276b7d5d39 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/suggestions-popup/suggestions-popup.component.spec.ts +++ b/src/app/suggestion-notifications/reciter-suggestions/suggestions-popup/suggestions-popup.component.spec.ts @@ -71,6 +71,7 @@ describe('SuggestionsPopupComponent', () => { }); it('should show a notification when new publication suggestions are available', () => { + expect((component as any).notificationsService.success).toHaveBeenCalled(); expect(suggestionStateService.dispatchMarkUserSuggestionsAsVisitedAction).toHaveBeenCalled(); }); diff --git a/src/config/app-config.interface.ts b/src/config/app-config.interface.ts index a0d91564150..f89623e0184 100644 --- a/src/config/app-config.interface.ts +++ b/src/config/app-config.interface.ts @@ -14,7 +14,7 @@ import { AuthConfig } from './auth-config.interfaces'; import { UIServerConfig } from './ui-server-config.interface'; import { MediaViewerConfig } from './media-viewer-config.interface'; import { BrowseByConfig } from './browse-by-config.interface'; -import {SuggestionConfig} from './layout-config.interfaces'; +import { SuggestionConfig } from './layout-config.interfaces'; import { BundleConfig } from './bundle-config.interface'; import { ActuatorsConfig } from './actuators.config'; import { InfoConfig } from './info-config.interface'; diff --git a/src/environments/environment.test.ts b/src/environments/environment.test.ts index 8b906764620..0573aebebc3 100644 --- a/src/environments/environment.test.ts +++ b/src/environments/environment.test.ts @@ -4,6 +4,7 @@ import { RestRequestMethod } from '../app/core/data/rest-request-method'; import { NotificationAnimationsType } from '../app/shared/notifications/models/notification-animations-type'; export const environment: BuildConfig = { + suggestion: [], production: false, // Angular Universal settings From 5feaa1b5a66fe6a36d33b9b48dbbd0232daa9f75 Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Wed, 27 Dec 2023 13:00:55 +0100 Subject: [PATCH 08/34] fix tests --- .../workspaceitem-data.service.spec.ts | 26 +++++++++++-------- .../submission/workspaceitem-data.service.ts | 14 ++++++++-- .../suggestions-popup.component.spec.ts | 10 ++++--- src/environments/environment.test.ts | 4 ++- 4 files changed, 37 insertions(+), 17 deletions(-) diff --git a/src/app/core/submission/workspaceitem-data.service.spec.ts b/src/app/core/submission/workspaceitem-data.service.spec.ts index e766a6a039c..039e9423170 100644 --- a/src/app/core/submission/workspaceitem-data.service.spec.ts +++ b/src/app/core/submission/workspaceitem-data.service.spec.ts @@ -21,6 +21,9 @@ import { RequestEntry } from '../data/request-entry.model'; import { CoreState } from '../core-state.model'; import { testSearchDataImplementation } from '../data/base/search-data.spec'; import { testDeleteDataImplementation } from '../data/base/delete-data.spec'; +import { SearchData } from '../data/base/search-data'; +import { DeleteData } from '../data/base/delete-data'; +import { RequestParam } from '../cache/models/request-param.model'; describe('WorkspaceitemDataService test', () => { let scheduler: TestScheduler; @@ -68,15 +71,12 @@ describe('WorkspaceitemDataService test', () => { const wsiRD = createSuccessfulRemoteDataObject(wsi); const endpointURL = `https://rest.api/rest/api/submission/workspaceitems`; - const searchRequestURL = `https://rest.api/rest/api/submission/workspaceitems/search/item?uuid=1234-1234`; - const searchRequestURL$ = observableOf(searchRequestURL); const requestUUID = '8b3c613a-5a4b-438b-9686-be1d5b4a1c5a'; objectCache = {} as ObjectCacheService; const notificationsService = {} as NotificationsService; const http = {} as HttpClient; - const comparator = {} as any; const comparatorEntry = {} as any; const store = {} as Store; const pageInfo = new PageInfo(); @@ -84,18 +84,23 @@ describe('WorkspaceitemDataService test', () => { function initTestService() { hrefOnlyDataService = getMockHrefOnlyDataService(); return new WorkspaceitemDataService( + comparatorEntry, + halService, + http, + notificationsService, requestService, rdbService, objectCache, - halService, - notificationsService, + store ); } describe('composition', () => { - const initService = () => new WorkspaceitemDataService(null, null, null, null, null); - testSearchDataImplementation(initService); - testDeleteDataImplementation(initService); + const initSearchService = () => new WorkspaceitemDataService(null, null, null, null, null, null, null, null) as unknown as SearchData; + const initDeleteService = () => new WorkspaceitemDataService(null, null, null, null, null, null, null, null) as unknown as DeleteData; + + testSearchDataImplementation(initSearchService); + testDeleteDataImplementation(initDeleteService); }); describe('', () => { @@ -126,7 +131,6 @@ describe('WorkspaceitemDataService test', () => { service = initTestService(); spyOn((service as any), 'findByHref').and.callThrough(); - spyOn((service as any), 'getSearchByHref').and.returnValue(searchRequestURL$); }); afterEach(() => { @@ -137,8 +141,8 @@ describe('WorkspaceitemDataService test', () => { it('should proxy the call to DataService.findByHref', () => { scheduler.schedule(() => service.findByItem('1234-1234', true, true, pageInfo)); scheduler.flush(); - - expect((service as any).findByHref).toHaveBeenCalledWith(searchRequestURL$, true, true); + const searchUrl = service.getIDHref('item', [new RequestParam('uuid', encodeURIComponent('1234-1234'))]); + expect((service as any).findByHref).toHaveBeenCalledWith(searchUrl, true, true); }); it('should return a RemoteData for the search', () => { diff --git a/src/app/core/submission/workspaceitem-data.service.ts b/src/app/core/submission/workspaceitem-data.service.ts index 8a036f64439..e2e7274e10d 100644 --- a/src/app/core/submission/workspaceitem-data.service.ts +++ b/src/app/core/submission/workspaceitem-data.service.ts @@ -23,16 +23,19 @@ import {hasValue} from '../../shared/empty.util'; import { IdentifiableDataService } from '../data/base/identifiable-data.service'; import { NoContent } from '../shared/NoContent.model'; import { DeleteData, DeleteDataImpl } from '../data/base/delete-data'; +import { SearchData, SearchDataImpl } from '../data/base/search-data'; +import { PaginatedList } from '../data/paginated-list.model'; /** * A service that provides methods to make REST requests with workspaceitems endpoint. */ @Injectable() @dataService(WorkspaceItem.type) -export class WorkspaceitemDataService extends IdentifiableDataService { +export class WorkspaceitemDataService extends IdentifiableDataService implements DeleteData, SearchData{ protected linkPath = 'workspaceitems'; protected searchByItemLinkPath = 'item'; private deleteData: DeleteData; + private searchData: SearchData; constructor( protected comparator: DSOChangeAnalyzer, @@ -45,7 +48,7 @@ export class WorkspaceitemDataService extends IdentifiableDataService) { super('workspaceitems', requestService, rdbService, objectCache, halService); this.deleteData = new DeleteDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive, this.constructIdEndpoint); - + this.searchData = new SearchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive); } public delete(objectId: string, copyVirtualMetadata?: string[]): Observable> { return this.deleteData.delete(objectId, copyVirtualMetadata); @@ -93,4 +96,11 @@ export class WorkspaceitemDataService extends IdentifiableDataService> { + return this.deleteData.deleteByHref(href, copyVirtualMetadata); + } + + searchBy(searchMethod: string, options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig[]): Observable>> { + return this.searchData.searchBy(searchMethod, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow); + } } diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestions-popup/suggestions-popup.component.spec.ts b/src/app/suggestion-notifications/reciter-suggestions/suggestions-popup/suggestions-popup.component.spec.ts index 67678354caf..aade2c78fa1 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/suggestions-popup/suggestions-popup.component.spec.ts +++ b/src/app/suggestion-notifications/reciter-suggestions/suggestions-popup/suggestions-popup.component.spec.ts @@ -13,11 +13,13 @@ import { SuggestionsService } from '../suggestions.service'; describe('SuggestionsPopupComponent', () => { let component: SuggestionsPopupComponent; let fixture: ComponentFixture; + let notificationsService: NotificationsService; const suggestionStateService = jasmine.createSpyObj('SuggestionTargetsStateService', { hasUserVisitedSuggestions: jasmine.createSpy('hasUserVisitedSuggestions'), getCurrentUserSuggestionTargets: jasmine.createSpy('getCurrentUserSuggestionTargets'), - dispatchMarkUserSuggestionsAsVisitedAction: jasmine.createSpy('dispatchMarkUserSuggestionsAsVisitedAction') + dispatchMarkUserSuggestionsAsVisitedAction: jasmine.createSpy('dispatchMarkUserSuggestionsAsVisitedAction'), + dispatchRefreshUserSuggestionsAction: jasmine.createSpy('dispatchRefreshUserSuggestionsAction') }); const mockNotificationInterpolation = { count: 12, source: 'source', suggestionId: 'id', displayName: 'displayName' }; @@ -60,18 +62,20 @@ describe('SuggestionsPopupComponent', () => { describe('when there are publication suggestions', () => { beforeEach(() => { - suggestionStateService.hasUserVisitedSuggestions.and.returnValue(observableOf(false)); suggestionStateService.getCurrentUserSuggestionTargets.and.returnValue(observableOf([mockSuggestionTargetsObjectOne])); suggestionStateService.dispatchMarkUserSuggestionsAsVisitedAction.and.returnValue(observableOf(null)); + suggestionStateService.dispatchRefreshUserSuggestionsAction.and.returnValue(observableOf(null)); fixture = TestBed.createComponent(SuggestionsPopupComponent); component = fixture.componentInstance; + notificationsService = (component as any).notificationsService; fixture.detectChanges(); }); it('should show a notification when new publication suggestions are available', () => { - expect((component as any).notificationsService.success).toHaveBeenCalled(); + expect(notificationsService.success).toHaveBeenCalled(); + expect(suggestionStateService.dispatchRefreshUserSuggestionsAction).toHaveBeenCalled(); expect(suggestionStateService.dispatchMarkUserSuggestionsAsVisitedAction).toHaveBeenCalled(); }); diff --git a/src/environments/environment.test.ts b/src/environments/environment.test.ts index 8b906764620..afc4082dde3 100644 --- a/src/environments/environment.test.ts +++ b/src/environments/environment.test.ts @@ -319,5 +319,7 @@ export const environment: BuildConfig = { vocabulary: 'srsc', enabled: true } - ] + ], + + suggestion: [] }; From ffd118abf9b427245b6a0ca502f0c55aea910966 Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Wed, 27 Dec 2023 13:26:04 +0100 Subject: [PATCH 09/34] align with github --- src/environments/environment.test.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/environments/environment.test.ts b/src/environments/environment.test.ts index 0573aebebc3..afc4082dde3 100644 --- a/src/environments/environment.test.ts +++ b/src/environments/environment.test.ts @@ -4,7 +4,6 @@ import { RestRequestMethod } from '../app/core/data/rest-request-method'; import { NotificationAnimationsType } from '../app/shared/notifications/models/notification-animations-type'; export const environment: BuildConfig = { - suggestion: [], production: false, // Angular Universal settings @@ -320,5 +319,7 @@ export const environment: BuildConfig = { vocabulary: 'srsc', enabled: true } - ] + ], + + suggestion: [] }; From 3149842bcbcfd769dc69e8b6ec14450282d7bd7b Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Tue, 9 Jan 2024 16:17:50 +0100 Subject: [PATCH 10/34] add data services tests --- .../workspaceitem-data.service.spec.ts | 26 +++- .../source/suggestion-source-data.service.ts | 21 ++- .../suggestions-source-data.service.spec.ts | 115 +++++++++++++++ .../suggestions-data.service.ts | 12 +- .../target/suggestion-target-data.service.ts | 61 +++++--- .../suggestions-target-data.service.spec.ts | 138 ++++++++++++++++++ 6 files changed, 336 insertions(+), 37 deletions(-) create mode 100644 src/app/core/suggestion-notifications/reciter-suggestions/source/suggestions-source-data.service.spec.ts create mode 100644 src/app/core/suggestion-notifications/reciter-suggestions/target/suggestions-target-data.service.spec.ts diff --git a/src/app/core/submission/workspaceitem-data.service.spec.ts b/src/app/core/submission/workspaceitem-data.service.spec.ts index 039e9423170..34548c1e53f 100644 --- a/src/app/core/submission/workspaceitem-data.service.spec.ts +++ b/src/app/core/submission/workspaceitem-data.service.spec.ts @@ -1,4 +1,4 @@ -import { HttpClient } from '@angular/common/http'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; import { of as observableOf } from 'rxjs'; import { TestScheduler } from 'rxjs/testing'; @@ -8,7 +8,7 @@ import { ObjectCacheService } from '../cache/object-cache.service'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { RequestService } from '../data/request.service'; import { PageInfo } from '../shared/page-info.model'; -import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils'; +import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; import { HrefOnlyDataService } from '../data/href-only-data.service'; import { getMockHrefOnlyDataService } from '../../shared/mocks/href-only-data.service.mock'; import { WorkspaceitemDataService } from './workspaceitem-data.service'; @@ -24,6 +24,8 @@ import { testDeleteDataImplementation } from '../data/base/delete-data.spec'; import { SearchData } from '../data/base/search-data'; import { DeleteData } from '../data/base/delete-data'; import { RequestParam } from '../cache/models/request-param.model'; +import { PostRequest } from '../data/request.models'; +import { HttpOptions } from '../dspace-rest/dspace-rest.service'; describe('WorkspaceitemDataService test', () => { let scheduler: TestScheduler; @@ -109,7 +111,7 @@ describe('WorkspaceitemDataService test', () => { scheduler = getTestScheduler(); halService = jasmine.createSpyObj('halService', { - getEndpoint: cold('a', { a: endpointURL }) + getEndpoint: observableOf(endpointURL) }); responseCacheEntry = new RequestEntry(); responseCacheEntry.request = { href: 'https://rest.api/' } as any; @@ -125,7 +127,8 @@ describe('WorkspaceitemDataService test', () => { rdbService = jasmine.createSpyObj('rdbService', { buildSingle: hot('a|', { a: wsiRD - }) + }), + buildFromRequestUUID: createSuccessfulRemoteDataObject$({}) }); service = initTestService(); @@ -154,6 +157,19 @@ describe('WorkspaceitemDataService test', () => { }); }); - }); + describe('importExternalSourceEntry', () => { + it('should send a POST request containing the provided item request', (done) => { + const options: HttpOptions = Object.create({}); + let headers = new HttpHeaders(); + headers = headers.append('Content-Type', 'text/uri-list'); + options.headers = headers; + + service.importExternalSourceEntry('externalHref', 'testId').subscribe(() => { + expect(requestService.send).toHaveBeenCalledWith(new PostRequest(requestUUID, `${endpointURL}?owningCollection=testId`, 'externalHref', options)); + done(); + }); + }); + }); + }); }); diff --git a/src/app/core/suggestion-notifications/reciter-suggestions/source/suggestion-source-data.service.ts b/src/app/core/suggestion-notifications/reciter-suggestions/source/suggestion-source-data.service.ts index c3e142044e1..15b067f2ed1 100644 --- a/src/app/core/suggestion-notifications/reciter-suggestions/source/suggestion-source-data.service.ts +++ b/src/app/core/suggestion-notifications/reciter-suggestions/source/suggestion-source-data.service.ts @@ -53,7 +53,7 @@ export class SuggestionSourceDataService extends IdentifiableDataService[]): Observable>> { - return this.findAllData.findAll(options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow); + return this.findAll(options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow); } /** @@ -71,4 +71,23 @@ export class SuggestionSourceDataService extends IdentifiableDataService[]): Observable> { return this.findById(id, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow); } + + + /** + * Returns {@link RemoteData} of all object with a list of {@link FollowLinkConfig}, to indicate which embedded + * info should be added to the objects + * + * @param options Find list options object + * @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's + * no valid cached version. Defaults to true + * @param reRequestOnStale Whether or not the request should automatically be re- + * requested after the response becomes stale + * @param linksToFollow List of {@link FollowLinkConfig} that indicate which + * {@link HALLink}s should be automatically resolved + * @return {Observable>>} + * Return an observable that emits object list + */ + findAll(options: FindListOptions = {}, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig[]): Observable>> { + return this.findAllData.findAll(options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow); + } } diff --git a/src/app/core/suggestion-notifications/reciter-suggestions/source/suggestions-source-data.service.spec.ts b/src/app/core/suggestion-notifications/reciter-suggestions/source/suggestions-source-data.service.spec.ts new file mode 100644 index 00000000000..5f8d51a6410 --- /dev/null +++ b/src/app/core/suggestion-notifications/reciter-suggestions/source/suggestions-source-data.service.spec.ts @@ -0,0 +1,115 @@ +import { TestScheduler } from 'rxjs/testing'; +import { RequestService } from '../../../data/request.service'; +import { RemoteDataBuildService } from '../../../cache/builders/remote-data-build.service'; +import { ObjectCacheService } from '../../../cache/object-cache.service'; +import { HALEndpointService } from '../../../shared/hal-endpoint.service'; +import { RequestEntry } from '../../../data/request-entry.model'; +import { cold, getTestScheduler } from 'jasmine-marbles'; +import { RestResponse } from '../../../cache/response.models'; +import { of as observableOf } from 'rxjs'; +import { Store } from '@ngrx/store'; +import { CoreState } from '../../../core-state.model'; +import { HttpClient } from '@angular/common/http'; +import { NotificationsService } from '../../../../shared/notifications/notifications.service'; +import { DefaultChangeAnalyzer } from '../../../data/default-change-analyzer.service'; +import { testFindAllDataImplementation } from '../../../data/base/find-all-data.spec'; +import { FindAllData } from '../../../data/base/find-all-data'; +import { GetRequest } from '../../../data/request.models'; +import { + createSuccessfulRemoteDataObject$ +} from '../../../../shared/remote-data.utils'; +import { RemoteData } from '../../../data/remote-data'; +import { RequestEntryState } from '../../../data/request-entry-state.model'; +import { SuggestionSourceDataService } from './suggestion-source-data.service'; +import { SuggestionSource } from '../models/suggestion-source.model'; + +describe('SuggestionSourceDataService test', () => { + let scheduler: TestScheduler; + let service: SuggestionSourceDataService; + let requestService: RequestService; + let rdbService: RemoteDataBuildService; + let objectCache: ObjectCacheService; + let halService: HALEndpointService; + let notificationsService: NotificationsService; + let http: HttpClient; + let comparator: DefaultChangeAnalyzer; + let responseCacheEntry: RequestEntry; + + const store = {} as Store; + const endpointURL = `https://rest.api/rest/api/suggestionsources`; + const requestUUID = '8b3c613a-5a4b-438b-9686-be1d5b4a1c5a'; + + const remoteDataMocks = { + Success: new RemoteData(null, null, null, RequestEntryState.Success, null, null, 200), + }; + + function initTestService() { + return new SuggestionSourceDataService( + requestService, + rdbService, + store, + objectCache, + halService, + notificationsService, + http, + comparator + ); + } + + beforeEach(() => { + scheduler = getTestScheduler(); + + objectCache = {} as ObjectCacheService; + http = {} as HttpClient; + notificationsService = {} as NotificationsService; + comparator = {} as DefaultChangeAnalyzer; + responseCacheEntry = new RequestEntry(); + responseCacheEntry.request = { href: 'https://rest.api/' } as any; + responseCacheEntry.response = new RestResponse(true, 200, 'Success'); + + requestService = jasmine.createSpyObj('requestService', { + generateRequestId: requestUUID, + send: true, + removeByHrefSubstring: {}, + getByHref: observableOf(responseCacheEntry), + getByUUID: observableOf(responseCacheEntry), + }); + + halService = jasmine.createSpyObj('halService', { + getEndpoint: observableOf(endpointURL) + }); + + rdbService = jasmine.createSpyObj('rdbService', { + buildSingle: createSuccessfulRemoteDataObject$({}, 500), + buildList: cold('a', { a: remoteDataMocks.Success }) + }); + + + service = initTestService(); + }); + + describe('composition', () => { + const initFindAllService = () => new SuggestionSourceDataService(null, null, null, null, null, null, null, null) as unknown as FindAllData; + testFindAllDataImplementation(initFindAllService); + }); + + describe('getSources', () => { + it('should send a new GetRequest', () => { + const expected = new GetRequest(requestService.generateRequestId(), `${endpointURL}`); + scheduler.schedule(() => service.getSources().subscribe()); + scheduler.flush(); + + expect(requestService.send).toHaveBeenCalledWith(expected, true); + }); + }); + + describe('getSource', () => { + it('should send a new GetRequest', () => { + const expected = new GetRequest(requestService.generateRequestId(), `${endpointURL}/testId`); + scheduler.schedule(() => service.getSource('testId').subscribe()); + scheduler.flush(); + + expect(requestService.send).toHaveBeenCalledWith(expected, true); + }); + }); +}); diff --git a/src/app/core/suggestion-notifications/reciter-suggestions/suggestions-data.service.ts b/src/app/core/suggestion-notifications/reciter-suggestions/suggestions-data.service.ts index 944a13e3a3c..407ea6a7e65 100644 --- a/src/app/core/suggestion-notifications/reciter-suggestions/suggestions-data.service.ts +++ b/src/app/core/suggestion-notifications/reciter-suggestions/suggestions-data.service.ts @@ -33,7 +33,7 @@ import { SuggestionTargetDataService } from './target/suggestion-target-data.ser /** * A private DataService implementation to delegate specific methods to. */ -class SuggestionDataServiceImpl extends DataService { +export class SuggestionDataServiceImpl extends DataService { /** * The REST endpoint. */ @@ -70,7 +70,6 @@ class SuggestionDataServiceImpl extends DataService { @dataService(SUGGESTION) export class SuggestionsDataService { protected searchFindBySourceMethod = 'findBySource'; - protected searchFindByTargetMethod = 'findByTarget'; protected searchFindByTargetAndSourceMethod = 'findByTargetAndSource'; /** @@ -168,7 +167,6 @@ export class SuggestionsDataService { ...linksToFollow: FollowLinkConfig[] ): Observable>> { options.searchParams = [new RequestParam('target', userId)]; - //return this.suggestionTargetsDataService.getTargetsByUser(this.searchFindByTargetMethod, options, ...linksToFollow); return this.suggestionTargetsDataService.getTargetsByUser(userId, options, ...linksToFollow); } @@ -193,14 +191,6 @@ export class SuggestionsDataService { return this.suggestionsDataService.delete(suggestionId); } - /** - * Used to fetch Suggestion notification for user - * @suggestionId - */ - public getSuggestion(suggestionId: string, ...linksToFollow: FollowLinkConfig[]): Observable> { - return this.suggestionsDataService.findById(suggestionId, true, true, ...linksToFollow); - } - /** * Return the list of Suggestion for a given target and source * diff --git a/src/app/core/suggestion-notifications/reciter-suggestions/target/suggestion-target-data.service.ts b/src/app/core/suggestion-notifications/reciter-suggestions/target/suggestion-target-data.service.ts index ce5f131c1d5..5a5f91a87b2 100644 --- a/src/app/core/suggestion-notifications/reciter-suggestions/target/suggestion-target-data.service.ts +++ b/src/app/core/suggestion-notifications/reciter-suggestions/target/suggestion-target-data.service.ts @@ -28,10 +28,9 @@ export class SuggestionTargetDataService extends IdentifiableDataService; - private searchBy: SearchData; + private searchData: SearchData; protected searchFindBySourceMethod = 'findBySource'; protected searchFindByTargetMethod = 'findByTarget'; - protected searchFindByTargetAndSourceMethod = 'findByTargetAndSource'; constructor( protected requestService: RequestService, @@ -44,7 +43,7 @@ export class SuggestionTargetDataService extends IdentifiableDataService) { super('suggestiontargets', requestService, rdbService, objectCache, halService); this.findAllData = new FindAllDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive); - this.searchBy = new SearchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive); + this.searchData = new SearchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive); } /** * Return the list of Suggestion Target for a given source @@ -65,22 +64,7 @@ export class SuggestionTargetDataService extends IdentifiableDataService>> { options.searchParams = [new RequestParam('source', source)]; - return this.searchBy.searchBy(this.searchFindBySourceMethod, options, true, true, ...linksToFollow); - } - /** - * Return a single Suggestion target. - * - * @param id The Suggestion Target id - * @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's - * no valid cached version. Defaults to true - * @param reRequestOnStale Whether or not the request should automatically be re- - * requested after the response becomes stale - * @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved. - * - * @return Observable> The Quality Assurance source. - */ - public getTarget(id: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig[]): Observable> { - return this.findById(id, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow); + return this.searchBy(this.searchFindBySourceMethod, options, true, true, ...linksToFollow); } /** @@ -102,7 +86,7 @@ export class SuggestionTargetDataService extends IdentifiableDataService>> { options.searchParams = [new RequestParam('target', userId)]; - return this.searchBy.searchBy(this.searchFindByTargetMethod, options, true, true, ...linksToFollow); + return this.searchBy(this.searchFindByTargetMethod, options, true, true, ...linksToFollow); } /** * Return a Suggestion Target for a given id @@ -117,4 +101,41 @@ export class SuggestionTargetDataService extends IdentifiableDataService>} + * Return an observable that emits response from the server + */ + public searchBy(searchMethod: string, options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig[]): Observable>> { + return this.searchData.searchBy(searchMethod, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow); + } + + + /** + * Returns {@link RemoteData} of all object with a list of {@link FollowLinkConfig}, to indicate which embedded + * info should be added to the objects + * + * @param options Find list options object + * @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's + * no valid cached version. Defaults to true + * @param reRequestOnStale Whether or not the request should automatically be re- + * requested after the response becomes stale + * @param linksToFollow List of {@link FollowLinkConfig} that indicate which + * {@link HALLink}s should be automatically resolved + * @return {Observable>>} + * Return an observable that emits object list + */ + findAll(options: FindListOptions = {}, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig[]): Observable>> { + return this.findAllData.findAll(options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow); + } + } diff --git a/src/app/core/suggestion-notifications/reciter-suggestions/target/suggestions-target-data.service.spec.ts b/src/app/core/suggestion-notifications/reciter-suggestions/target/suggestions-target-data.service.spec.ts new file mode 100644 index 00000000000..ba5900b79fa --- /dev/null +++ b/src/app/core/suggestion-notifications/reciter-suggestions/target/suggestions-target-data.service.spec.ts @@ -0,0 +1,138 @@ +import { TestScheduler } from 'rxjs/testing'; +import { RequestService } from '../../../data/request.service'; +import { RemoteDataBuildService } from '../../../cache/builders/remote-data-build.service'; +import { ObjectCacheService } from '../../../cache/object-cache.service'; +import { HALEndpointService } from '../../../shared/hal-endpoint.service'; +import { RequestEntry } from '../../../data/request-entry.model'; +import { cold, getTestScheduler } from 'jasmine-marbles'; +import { RestResponse } from '../../../cache/response.models'; +import { of as observableOf } from 'rxjs'; +import { Store } from '@ngrx/store'; +import { CoreState } from '../../../core-state.model'; +import { HttpClient } from '@angular/common/http'; +import { NotificationsService } from '../../../../shared/notifications/notifications.service'; +import { SearchData } from '../../../data/base/search-data'; +import { testSearchDataImplementation } from '../../../data/base/search-data.spec'; +import { SuggestionTargetDataService } from './suggestion-target-data.service'; +import { DefaultChangeAnalyzer } from '../../../data/default-change-analyzer.service'; +import { SuggestionTarget } from '../models/suggestion-target.model'; +import { testFindAllDataImplementation } from '../../../data/base/find-all-data.spec'; +import { FindAllData } from '../../../data/base/find-all-data'; +import { GetRequest } from '../../../data/request.models'; +import { + createSuccessfulRemoteDataObject$ +} from '../../../../shared/remote-data.utils'; +import { RequestParam } from '../../../cache/models/request-param.model'; +import { RemoteData } from '../../../data/remote-data'; +import { RequestEntryState } from '../../../data/request-entry-state.model'; + +describe('SuggestionTargetDataService test', () => { + let scheduler: TestScheduler; + let service: SuggestionTargetDataService; + let requestService: RequestService; + let rdbService: RemoteDataBuildService; + let objectCache: ObjectCacheService; + let halService: HALEndpointService; + let notificationsService: NotificationsService; + let http: HttpClient; + let comparator: DefaultChangeAnalyzer; + let responseCacheEntry: RequestEntry; + + const store = {} as Store; + const endpointURL = `https://rest.api/rest/api/suggestiontargets`; + const requestUUID = '8b3c613a-5a4b-438b-9686-be1d5b4a1c5a'; + + const remoteDataMocks = { + Success: new RemoteData(null, null, null, RequestEntryState.Success, null, null, 200), + }; + + function initTestService() { + return new SuggestionTargetDataService( + requestService, + rdbService, + store, + objectCache, + halService, + notificationsService, + http, + comparator + ); + } + + beforeEach(() => { + scheduler = getTestScheduler(); + + objectCache = {} as ObjectCacheService; + http = {} as HttpClient; + notificationsService = {} as NotificationsService; + comparator = {} as DefaultChangeAnalyzer; + responseCacheEntry = new RequestEntry(); + responseCacheEntry.request = { href: 'https://rest.api/' } as any; + responseCacheEntry.response = new RestResponse(true, 200, 'Success'); + + requestService = jasmine.createSpyObj('requestService', { + generateRequestId: requestUUID, + send: true, + removeByHrefSubstring: {}, + getByHref: observableOf(responseCacheEntry), + getByUUID: observableOf(responseCacheEntry), + }); + + halService = jasmine.createSpyObj('halService', { + getEndpoint: observableOf(endpointURL) + }); + + rdbService = jasmine.createSpyObj('rdbService', { + buildSingle: createSuccessfulRemoteDataObject$({}, 500), + buildList: cold('a', { a: remoteDataMocks.Success }) + }); + + + service = initTestService(); + }); + + describe('composition', () => { + const initSearchService = () => new SuggestionTargetDataService(null, null, null, null, null, null, null, null) as unknown as SearchData; + const initFindAllService = () => new SuggestionTargetDataService(null, null, null, null, null, null, null, null) as unknown as FindAllData; + testSearchDataImplementation(initSearchService); + testFindAllDataImplementation(initFindAllService); + }); + + describe('getTargetById', () => { + it('should send a new GetRequest', () => { + const expected = new GetRequest(requestService.generateRequestId(), endpointURL + '/testId'); + scheduler.schedule(() => service.getTargetById('testId').subscribe()); + scheduler.flush(); + + expect(requestService.send).toHaveBeenCalledWith(expected, true); + }); + }); + + describe('getTargetsByUser', () => { + it('should send a new GetRequest', () => { + const options = { + searchParams: [new RequestParam('target', 'testId')] + }; + const searchFindByTargetMethod = 'findByTarget'; + const expected = new GetRequest(requestService.generateRequestId(), `${endpointURL}/search/${searchFindByTargetMethod}?target=testId`); + scheduler.schedule(() => service.getTargetsByUser('testId', options).subscribe()); + scheduler.flush(); + + expect(requestService.send).toHaveBeenCalledWith(expected, true); + }); + }); + + describe('getTargets', () => { + it('should send a new GetRequest', () => { + const options = { + searchParams: [new RequestParam('source', 'testId')] + }; + const searchFindBySourceMethod = 'findBySource'; + const expected = new GetRequest(requestService.generateRequestId(), `${endpointURL}/search/${searchFindBySourceMethod}?source=testId`); + scheduler.schedule(() => service.getTargets('testId', options).subscribe()); + scheduler.flush(); + + expect(requestService.send).toHaveBeenCalledWith(expected, true); + }); + }); +}); From d16bf492687306cff03a053032aa696e928c0d31 Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Tue, 9 Jan 2024 21:48:27 +0100 Subject: [PATCH 11/34] add tests --- .../cache/builders/build-decorators.spec.ts | 8 +- .../suggestion-data.service.spec.ts | 173 ++++++++++++++++++ .../suggestions-data.service.ts | 2 +- ...o-selector-modal-wrapper.component.spec.ts | 30 ++- ...filter-input-suggestions.component.spec.ts | 27 +++ 5 files changed, 237 insertions(+), 3 deletions(-) create mode 100644 src/app/core/suggestion-notifications/reciter-suggestions/suggestion-data.service.spec.ts diff --git a/src/app/core/cache/builders/build-decorators.spec.ts b/src/app/core/cache/builders/build-decorators.spec.ts index 150a07f0066..90d4b4fef83 100644 --- a/src/app/core/cache/builders/build-decorators.spec.ts +++ b/src/app/core/cache/builders/build-decorators.spec.ts @@ -1,7 +1,7 @@ import { HALLink } from '../../shared/hal-link.model'; import { HALResource } from '../../shared/hal-resource.model'; import { ResourceType } from '../../shared/resource-type'; -import { getLinkDefinition, link } from './build-decorators'; +import { dataService, getLinkDefinition, link } from './build-decorators'; class TestHALResource implements HALResource { _links: { @@ -46,5 +46,11 @@ describe('build decorators', () => { expect(result).toBeUndefined(); }); }); + + describe(`set data service`, () => { + it(`should throw error`, () => { + expect(dataService(null)).toThrow(); + }); + }); }); }); diff --git a/src/app/core/suggestion-notifications/reciter-suggestions/suggestion-data.service.spec.ts b/src/app/core/suggestion-notifications/reciter-suggestions/suggestion-data.service.spec.ts new file mode 100644 index 00000000000..93b29eff068 --- /dev/null +++ b/src/app/core/suggestion-notifications/reciter-suggestions/suggestion-data.service.spec.ts @@ -0,0 +1,173 @@ +import { TestScheduler } from 'rxjs/testing'; +import { SuggestionDataServiceImpl, SuggestionsDataService } from './suggestions-data.service'; +import { RequestService } from '../../data/request.service'; +import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service'; +import { ObjectCacheService } from '../../cache/object-cache.service'; +import { HALEndpointService } from '../../shared/hal-endpoint.service'; +import { NotificationsService } from '../../../shared/notifications/notifications.service'; +import { HttpClient } from '@angular/common/http'; +import { DefaultChangeAnalyzer } from '../../data/default-change-analyzer.service'; +import { Suggestion } from './models/suggestion.model'; +import { cold, getTestScheduler } from 'jasmine-marbles'; +import { RequestEntry } from '../../data/request-entry.model'; +import { RestResponse } from '../../cache/response.models'; +import { of as observableOf } from 'rxjs'; +import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; +import { RemoteData } from '../../data/remote-data'; +import { RequestEntryState } from '../../data/request-entry-state.model'; +import { SuggestionSource } from './models/suggestion-source.model'; +import { SuggestionTarget } from './models/suggestion-target.model'; +import { SuggestionSourceDataService } from './source/suggestion-source-data.service'; +import { SuggestionTargetDataService } from './target/suggestion-target-data.service'; +import { RequestParam } from '../../cache/models/request-param.model'; + +describe('SuggestionDataService test', () => { + let scheduler: TestScheduler; + let service: SuggestionsDataService; + let requestService: RequestService; + let rdbService: RemoteDataBuildService; + let objectCache: ObjectCacheService; + let halService: HALEndpointService; + let notificationsService: NotificationsService; + let http: HttpClient; + let comparatorSuggestion: DefaultChangeAnalyzer; + let comparatorSuggestionSource: DefaultChangeAnalyzer; + let comparatorSuggestionTarget: DefaultChangeAnalyzer; + let suggestionSourcesDataService: SuggestionSourceDataService; + let suggestionTargetsDataService: SuggestionTargetDataService; + let suggestionsDataService: SuggestionDataServiceImpl; + let responseCacheEntry: RequestEntry; + + + const testSource = 'test-source'; + const testUserId = '1234-4321'; + const endpointURL = `https://rest.api/rest/api/`; + const requestUUID = '8b3c613a-5a4b-438b-9686-be1d5b4a1c5a'; + const remoteDataMocks = { + Success: new RemoteData(null, null, null, RequestEntryState.Success, null, null, 200), + }; + + function initTestService() { + return new SuggestionsDataService( + requestService, + rdbService, + objectCache, + halService, + notificationsService, + http, + comparatorSuggestion, + comparatorSuggestionSource, + comparatorSuggestionTarget + ); + } + + beforeEach(() => { + scheduler = getTestScheduler(); + + objectCache = {} as ObjectCacheService; + http = {} as HttpClient; + notificationsService = {} as NotificationsService; + comparatorSuggestion = {} as DefaultChangeAnalyzer; + comparatorSuggestionTarget = {} as DefaultChangeAnalyzer; + comparatorSuggestionSource = {} as DefaultChangeAnalyzer; + responseCacheEntry = new RequestEntry(); + responseCacheEntry.request = { href: 'https://rest.api/' } as any; + responseCacheEntry.response = new RestResponse(true, 200, 'Success'); + + requestService = jasmine.createSpyObj('requestService', { + generateRequestId: requestUUID, + send: true, + removeByHrefSubstring: {}, + getByHref: observableOf(responseCacheEntry), + getByUUID: observableOf(responseCacheEntry), + setStaleByHrefSubstring: observableOf(true) + }); + + halService = jasmine.createSpyObj('halService', { + getEndpoint: observableOf(endpointURL) + }); + + rdbService = jasmine.createSpyObj('rdbService', { + buildSingle: createSuccessfulRemoteDataObject$({}, 500), + buildList: cold('a', { a: remoteDataMocks.Success }) + }); + + + suggestionSourcesDataService = jasmine.createSpyObj('suggestionSourcesDataService', { + getSources: observableOf(null), + }); + + suggestionTargetsDataService = jasmine.createSpyObj('suggestionTargetsDataService', { + getTargets: observableOf(null), + getTargetsByUser: observableOf(null), + findById: observableOf(null), + }); + + suggestionsDataService = jasmine.createSpyObj('suggestionsDataService', { + searchBy: observableOf(null), + delete: observableOf(null), + }); + + + service = initTestService(); + /* eslint-disable-next-line @typescript-eslint/dot-notation */ + service['suggestionSourcesDataService'] = suggestionSourcesDataService; + /* eslint-disable-next-line @typescript-eslint/dot-notation */ + service['suggestionTargetsDataService'] = suggestionTargetsDataService; + /* eslint-disable-next-line @typescript-eslint/dot-notation */ + service['suggestionsDataService'] = suggestionsDataService; + }); + + describe('Suggestion targets service', () => { + it('should call suggestionSourcesDataService.getTargets', () => { + const options = { + searchParams: [new RequestParam('source', testSource)] + }; + service.getTargets(testSource); + expect(suggestionTargetsDataService.getTargets).toHaveBeenCalledWith('findBySource', options); + }); + + it('should call suggestionSourcesDataService.getTargetsByUser', () => { + const options = { + searchParams: [new RequestParam('target', testUserId)] + }; + service.getTargetsByUser(testUserId); + expect(suggestionTargetsDataService.getTargetsByUser).toHaveBeenCalledWith(testUserId, options); + }); + + it('should call suggestionSourcesDataService.getTargetById', () => { + service.getTargetById('1'); + expect(suggestionTargetsDataService.findById).toHaveBeenCalledWith('1'); + }); + }); + + + describe('Suggestion sources service', () => { + it('should call suggestionSourcesDataService.getSources', () => { + service.getSources(); + expect(suggestionSourcesDataService.getSources).toHaveBeenCalled(); + }); + }); + + describe('Suggestion service', () => { + it('should call suggestionsDataService.searchBy', () => { + const options = { + searchParams: [new RequestParam('target', testUserId), new RequestParam('source', testSource)] + }; + service.getSuggestionsByTargetAndSource(testUserId, testSource); + expect(suggestionsDataService.searchBy).toHaveBeenCalledWith('findByTargetAndSource', options, true, true); + }); + + it('should call suggestionsDataService.delete', () => { + service.deleteSuggestion('1'); + expect(suggestionsDataService.delete).toHaveBeenCalledWith('1'); + }); + }); + + describe('Request service', () => { + it('should call requestService.setStaleByHrefSubstring', () => { + service.clearSuggestionRequests(); + expect(requestService.setStaleByHrefSubstring).toHaveBeenCalled(); + }); + }); +}); diff --git a/src/app/core/suggestion-notifications/reciter-suggestions/suggestions-data.service.ts b/src/app/core/suggestion-notifications/reciter-suggestions/suggestions-data.service.ts index 407ea6a7e65..58fb1650319 100644 --- a/src/app/core/suggestion-notifications/reciter-suggestions/suggestions-data.service.ts +++ b/src/app/core/suggestion-notifications/reciter-suggestions/suggestions-data.service.ts @@ -116,7 +116,7 @@ export class SuggestionsDataService { } /** - * Return the list of Suggestion Target + * Return the list of Suggestion Sources * * @param options * Find list options object. diff --git a/src/app/shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component.spec.ts b/src/app/shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component.spec.ts index e2dbaaa0fff..edefff85922 100644 --- a/src/app/shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component.spec.ts +++ b/src/app/shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component.spec.ts @@ -5,13 +5,14 @@ import { DSpaceObjectType } from '../../../core/shared/dspace-object-type.model' import { Item } from '../../../core/shared/item.model'; import { DSOSelectorModalWrapperComponent, SelectorActionType } from './dso-selector-modal-wrapper.component'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, ActivatedRouteSnapshot } from '@angular/router'; import { DSpaceObject } from '../../../core/shared/dspace-object.model'; import { By } from '@angular/platform-browser'; import { DSOSelectorComponent } from '../dso-selector/dso-selector.component'; import { MockComponent } from 'ng-mocks'; import { MetadataValue } from '../../../core/shared/metadata.models'; import { createSuccessfulRemoteDataObject } from '../../remote-data.utils'; +import { hasValue } from '../../empty.util'; describe('DSOSelectorModalWrapperComponent', () => { let component: DSOSelectorModalWrapperComponent; @@ -83,6 +84,20 @@ describe('DSOSelectorModalWrapperComponent', () => { }); }); + describe('selectObject with emit only', () => { + beforeEach(() => { + spyOn(component, 'navigate'); + spyOn(component, 'close'); + spyOn(component.select, 'emit'); + component.emitOnly = true; + component.selectObject(item); + }); + it('should call the close and navigate method on the component with the given DSO', () => { + expect(component.close).toHaveBeenCalled(); + expect(component.select.emit).toHaveBeenCalledWith(item); + }); + }); + describe('close', () => { beforeEach(() => { component.close(); @@ -113,6 +128,19 @@ describe('DSOSelectorModalWrapperComponent', () => { expect(component.close).toHaveBeenCalled(); }); }); + + describe('should find route data', () => { + beforeEach(() => { + spyOn(component, 'findRouteData'); + component.ngOnInit(); + }); + it('should call the findRouteData method on the component', () => { + expect(component.findRouteData).toHaveBeenCalled(); + }); + it('should return undefined', () => { + expect(component.findRouteData((route) => hasValue(route.data), {} as unknown as ActivatedRouteSnapshot)).toEqual(undefined); + }); + }); }); @Component({ diff --git a/src/app/shared/input-suggestions/filter-suggestions/filter-input-suggestions.component.spec.ts b/src/app/shared/input-suggestions/filter-suggestions/filter-input-suggestions.component.spec.ts index 6a0324d2ac4..6a67d84c288 100644 --- a/src/app/shared/input-suggestions/filter-suggestions/filter-input-suggestions.component.spec.ts +++ b/src/app/shared/input-suggestions/filter-suggestions/filter-input-suggestions.component.spec.ts @@ -54,4 +54,31 @@ describe('FilterInputSuggestionsComponent', () => { expect(comp.onClickSuggestion).toHaveBeenCalledWith(suggestions[clickedIndex]); }); }); + + describe('component methods', () => { + const testData = { + value: 'test-field' + } as unknown as any; + + beforeEach(() => { + spyOn(comp.submitSuggestion, 'emit'); + spyOn(comp.clickSuggestion, 'emit'); + spyOn(comp, 'close'); + }); + + it('should properly submit', () => { + comp.onSubmit(testData); + expect(comp.submitSuggestion.emit).toHaveBeenCalledWith(testData); + expect(comp.value).toBe(testData); + }); + + it('should update value on suggestion clicked', () => { + comp.onClickSuggestion(testData); + expect(comp.clickSuggestion.emit).toHaveBeenCalledWith(testData); + expect(comp.value).toBe(testData.value); + expect(comp.blockReopen).toBeTruthy(); + expect(comp.close).toHaveBeenCalled(); + }); + }); + }); From 6fda2cb4f78df2883fbdb60e1860b5d1fcae1211 Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Wed, 10 Jan 2024 12:57:13 +0100 Subject: [PATCH 12/34] add tests --- .../shared/mocks/reciter-suggestion.mock.ts | 2 +- src/app/shared/mocks/suggestion.mock.ts | 13 +- .../suggestion-list-element.component.spec.ts | 81 ++++++++ .../suggestion.service.spec.ts | 194 ++++++++++++++++++ .../suggestions-page.component.spec.ts | 112 +++++++++- 5 files changed, 398 insertions(+), 4 deletions(-) create mode 100644 src/app/suggestion-notifications/reciter-suggestions/suggestion-list-element/suggestion-list-element.component.spec.ts create mode 100644 src/app/suggestion-notifications/reciter-suggestions/suggestion.service.spec.ts diff --git a/src/app/shared/mocks/reciter-suggestion.mock.ts b/src/app/shared/mocks/reciter-suggestion.mock.ts index aceca72fc8f..b2dd0e7b128 100644 --- a/src/app/shared/mocks/reciter-suggestion.mock.ts +++ b/src/app/shared/mocks/reciter-suggestion.mock.ts @@ -6,7 +6,7 @@ import { Suggestion } from '../../core/suggestion-notifications/reciter-suggesti import { SUGGESTION } from '../../core/suggestion-notifications/reciter-suggestions/models/suggestion-objects.resource-type'; export const mockSuggestionPublicationOne: Suggestion = { - id: '24694772', + id: '24694773', display: 'publication one', source: 'reciter', externalSourceUri: 'https://dspace7.4science.cloud/server/api/integration/reciterSourcesEntry/pubmed/entryValues/24694772', diff --git a/src/app/shared/mocks/suggestion.mock.ts b/src/app/shared/mocks/suggestion.mock.ts index 8b1ab7acd55..d4ada21bf8d 100644 --- a/src/app/shared/mocks/suggestion.mock.ts +++ b/src/app/shared/mocks/suggestion.mock.ts @@ -1,6 +1,7 @@ import { DSpaceObject } from '../../core/shared/dspace-object.model'; import { Item } from '../../core/shared/item.model'; import { SearchResult } from '../search/models/search-result.model'; +import { of as observableOf } from 'rxjs'; // REST Mock --------------------------------------------------------------------- // ------------------------------------------------------------------------------- @@ -1333,7 +1334,8 @@ export function getMockSuggestionNotificationsStateService(): any { getOpenaireBrokerTopicsCurrentPage: jasmine.createSpy('getOpenaireBrokerTopicsCurrentPage'), getOpenaireBrokerTopicsTotals: jasmine.createSpy('getOpenaireBrokerTopicsTotals'), dispatchRetrieveOpenaireBrokerTopics: jasmine.createSpy('dispatchRetrieveOpenaireBrokerTopics'), - dispatchMarkUserSuggestionsAsVisitedAction: jasmine.createSpy('dispatchMarkUserSuggestionsAsVisitedAction') + dispatchMarkUserSuggestionsAsVisitedAction: jasmine.createSpy('dispatchMarkUserSuggestionsAsVisitedAction'), + dispatchRefreshUserSuggestionsAction: undefined }); } /** @@ -1342,10 +1344,17 @@ export function getMockSuggestionNotificationsStateService(): any { export function getMockSuggestionsService(): any { return jasmine.createSpyObj('SuggestionsService', { getTargets: jasmine.createSpy('getTargets'), - getSuggestions: jasmine.createSpy('getSuggestions'), + getSuggestions: observableOf([]), clearSuggestionRequests: jasmine.createSpy('clearSuggestionRequests'), deleteReviewedSuggestion: jasmine.createSpy('deleteReviewedSuggestion'), retrieveCurrentUserSuggestions: jasmine.createSpy('retrieveCurrentUserSuggestions'), getTargetUuid: jasmine.createSpy('getTargetUuid'), + notMine: observableOf(null), + notMineMultiple: observableOf({success: 1, fails: 0}), + approveAndImportMultiple: observableOf({success: 1, fails: 0}), + approveAndImport: observableOf({id: '1234'}), + isCollectionFixed: false, + translateSuggestionSource: 'testSource', + translateSuggestionType: 'testType', }); } diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestion-list-element/suggestion-list-element.component.spec.ts b/src/app/suggestion-notifications/reciter-suggestions/suggestion-list-element/suggestion-list-element.component.spec.ts new file mode 100644 index 00000000000..544b53774e7 --- /dev/null +++ b/src/app/suggestion-notifications/reciter-suggestions/suggestion-list-element/suggestion-list-element.component.spec.ts @@ -0,0 +1,81 @@ +import { SuggestionListElementComponent } from './suggestion-list-element.component'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { TranslateModule } from '@ngx-translate/core'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { TestScheduler } from 'rxjs/testing'; +import { getTestScheduler } from 'jasmine-marbles'; +import { mockSuggestionPublicationOne } from '../../../shared/mocks/reciter-suggestion.mock'; +import { Item } from '../../../core/shared/item.model'; + + +describe('SuggestionListElementComponent', () => { + let component: SuggestionListElementComponent; + let fixture: ComponentFixture; + let scheduler: TestScheduler; + + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + TranslateModule.forRoot() + ], + declarations: [SuggestionListElementComponent], + providers: [ + NgbModal + ], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents().then(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SuggestionListElementComponent); + component = fixture.componentInstance; + scheduler = getTestScheduler(); + + component.object = mockSuggestionPublicationOne; + }); + + describe('SuggestionListElementComponent test', () => { + + it('should create', () => { + scheduler.schedule(() => fixture.detectChanges()); + scheduler.flush(); + const expectedIndexableObject = Object.assign(new Item(), { + id: mockSuggestionPublicationOne.id, + metadata: mockSuggestionPublicationOne.metadata + }); + expect(component).toBeTruthy(); + expect(component.listableObject.hitHighlights).toEqual({}); + expect(component.listableObject.indexableObject).toEqual(expectedIndexableObject); + }); + + it('should check if has evidence', () => { + expect(component.hasEvidences()).toBeTruthy(); + }); + + it('should set seeEvidences', () => { + component.onSeeEvidences(true); + expect(component.seeEvidence).toBeTruthy(); + }); + + it('should emit selection', () => { + spyOn(component.selected, 'next'); + component.changeSelected({target: { checked: true}}); + expect(component.selected.next).toHaveBeenCalledWith(true); + }); + + it('should emit for deletion', () => { + spyOn(component.notMineClicked, 'emit'); + component.onNotMine('1234'); + expect(component.notMineClicked.emit).toHaveBeenCalledWith('1234'); + }); + + it('should emit for approve and import', () => { + const event = {collectionId:'1234', suggestion: mockSuggestionPublicationOne}; + spyOn(component.approveAndImport, 'emit'); + component.onApproveAndImport(event); + expect(component.approveAndImport.emit).toHaveBeenCalledWith(event); + }); + }); +}); diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestion.service.spec.ts b/src/app/suggestion-notifications/reciter-suggestions/suggestion.service.spec.ts new file mode 100644 index 00000000000..6abed7a1ce1 --- /dev/null +++ b/src/app/suggestion-notifications/reciter-suggestions/suggestion.service.spec.ts @@ -0,0 +1,194 @@ +import { SuggestionsService } from './suggestions.service'; +import { AuthService } from '../../core/auth/auth.service'; +import { ResearcherProfileDataService } from '../../core/profile/researcher-profile-data.service'; +import { + SuggestionsDataService +} from '../../core/suggestion-notifications/reciter-suggestions/suggestions-data.service'; +import { + SuggestionSourceDataService +} from '../../core/suggestion-notifications/reciter-suggestions/source/suggestion-source-data.service'; +import { + SuggestionTargetDataService +} from '../../core/suggestion-notifications/reciter-suggestions/target/suggestion-target-data.service'; +import { TestScheduler } from 'rxjs/testing'; +import { getTestScheduler } from 'jasmine-marbles'; +import { of as observableOf } from 'rxjs'; +import { FindListOptions } from '../../core/data/find-list-options.model'; +import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; +import { ResearcherProfile } from '../../core/profile/model/researcher-profile.model'; +import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; +import { WorkspaceitemDataService } from '../../core/submission/workspaceitem-data.service'; +import { mockSuggestionPublicationOne } from '../../shared/mocks/reciter-suggestion.mock'; +import { ResourceType } from '../../core/shared/resource-type'; + + +import { + SuggestionTarget +} from '../../core/suggestion-notifications/reciter-suggestions/models/suggestion-target.model'; + +describe('SuggestionsService test', () => { + let scheduler: TestScheduler; + let service: SuggestionsService; + let authService: AuthService; + let researcherProfileService: ResearcherProfileDataService; + let suggestionsDataService: SuggestionsDataService; + let suggestionSourceDataService: SuggestionSourceDataService; + let suggestionTargetDataService: SuggestionTargetDataService; + let translateService: any = { + instant: (str) => str, + }; + const suggestionTarget = { + id: '1234:4321', + display: 'display', + source: 'source', + total: 8, + type: new ResourceType('suggestiontarget') + }; + + const mockResercherProfile = { + id: '1234', + uuid: '1234', + visible: true + }; + + function initTestService() { + return new SuggestionsService( + authService, + researcherProfileService, + suggestionsDataService, + suggestionSourceDataService, + suggestionTargetDataService, + translateService + ); + } + + beforeEach(() => { + scheduler = getTestScheduler(); + + + suggestionSourceDataService = jasmine.createSpyObj('suggestionSourcesDataService', { + getSources: observableOf(null), + }); + + researcherProfileService = jasmine.createSpyObj('researcherProfileService', { + findById: createSuccessfulRemoteDataObject$(mockResercherProfile as ResearcherProfile), + findRelatedItemId: observableOf('1234'), + }); + + suggestionTargetDataService = jasmine.createSpyObj('suggestionTargetsDataService', { + getTargets: observableOf(null), + findById: observableOf(null), + }); + + suggestionsDataService = jasmine.createSpyObj('suggestionsDataService', { + searchBy: observableOf(null), + delete: observableOf(null), + deleteSuggestion: createSuccessfulRemoteDataObject$({}), + getSuggestionsByTargetAndSource : observableOf(null), + clearSuggestionRequests : null, + getTargetsByUser: observableOf(null), + }); + + service = initTestService(); + + }); + + describe('Suggestion service', () => { + it('should create', () => { + expect(service).toBeDefined(); + }); + + it('should get targets', () => { + const sortOptions = new SortOptions('display', SortDirection.ASC); + const findListOptions: FindListOptions = { + elementsPerPage: 10, + currentPage: 1, + sort: sortOptions + }; + service.getTargets('source', 10, 1); + expect(suggestionTargetDataService.getTargets).toHaveBeenCalledWith('source', findListOptions); + }); + + it('should get suggestions', () => { + const sortOptions = new SortOptions('display', SortDirection.ASC); + const findListOptions: FindListOptions = { + elementsPerPage: 10, + currentPage: 1, + sort: sortOptions + }; + service.getSuggestions('source:target', 10, 1, sortOptions); + expect(suggestionsDataService.getSuggestionsByTargetAndSource).toHaveBeenCalledWith('target', 'source', findListOptions); + }); + + it('should clear suggestions', () => { + service.clearSuggestionRequests(); + expect(suggestionsDataService.clearSuggestionRequests).toHaveBeenCalled(); + }); + + it('should delete reviewed suggestion', () => { + service.deleteReviewedSuggestion('1234'); + expect(suggestionsDataService.deleteSuggestion).toHaveBeenCalledWith('1234'); + }); + + it('should retrieve current user suggestions', () => { + service.retrieveCurrentUserSuggestions('1234'); + expect(researcherProfileService.findById).toHaveBeenCalledWith('1234'); + }); + + it('should approve and import suggestion', () => { + spyOn(service, 'resolveCollectionId'); + const workspaceitemService = {importExternalSourceEntry: (x,y) => observableOf(null)}; + service.approveAndImport(workspaceitemService as unknown as WorkspaceitemDataService, mockSuggestionPublicationOne, '1234'); + expect(service.resolveCollectionId).toHaveBeenCalled(); + }); + + it('should approve and import suggestions', () => { + spyOn(service, 'approveAndImport'); + const workspaceitemService = {importExternalSourceEntry: (x,y) => observableOf(null)}; + service.approveAndImportMultiple(workspaceitemService as unknown as WorkspaceitemDataService, [mockSuggestionPublicationOne], '1234'); + expect(service.approveAndImport).toHaveBeenCalledWith(workspaceitemService as unknown as WorkspaceitemDataService, mockSuggestionPublicationOne, '1234'); + }); + + it('should delete suggestion', () => { + spyOn(service, 'deleteReviewedSuggestion').and.returnValue(createSuccessfulRemoteDataObject$({})); + service.notMine('1234'); + expect(service.deleteReviewedSuggestion).toHaveBeenCalledWith('1234'); + }); + + it('should delete suggestions', () => { + spyOn(service, 'notMine'); + service.notMineMultiple([mockSuggestionPublicationOne]); + expect(service.notMine).toHaveBeenCalledWith(mockSuggestionPublicationOne.id); + }); + + it('should get target Uuid', () => { + expect(service.getTargetUuid(suggestionTarget as SuggestionTarget)).toBe('4321'); + expect(service.getTargetUuid({id: ''} as SuggestionTarget)).toBe(null); + }); + + it('should get suggestion interpolation', () => { + const result = service.getNotificationSuggestionInterpolation(suggestionTarget as SuggestionTarget); + expect(result.count).toEqual(suggestionTarget.total); + expect(result.source).toEqual('reciter.suggestion.source.' + suggestionTarget.source); + expect(result.type).toEqual('reciter.suggestion.type.' + suggestionTarget.source); + expect(result.suggestionId).toEqual(suggestionTarget.id); + expect(result.displayName).toEqual(suggestionTarget.display); + }); + + it('should translate suggestion type', () => { + expect(service.translateSuggestionType('source')).toEqual('reciter.suggestion.type.source'); + }); + + it('should translate suggestion source', () => { + expect(service.translateSuggestionSource('source')).toEqual('reciter.suggestion.source.source'); + }); + + it('should resolve collection id', () => { + expect(service.resolveCollectionId(mockSuggestionPublicationOne, '1234')).toEqual('1234'); + }); + + it('should check if collection is fixed', () => { + expect(service.isCollectionFixed([mockSuggestionPublicationOne])).toBeFalse(); + }); + }); +}); diff --git a/src/app/suggestions-page/suggestions-page.component.spec.ts b/src/app/suggestions-page/suggestions-page.component.spec.ts index 61e66a99cf7..0b0e235082d 100644 --- a/src/app/suggestions-page/suggestions-page.component.spec.ts +++ b/src/app/suggestions-page/suggestions-page.component.spec.ts @@ -7,7 +7,10 @@ import { TranslateModule, TranslateService } from '@ngx-translate/core'; import { of as observableOf } from 'rxjs'; import { SuggestionsPageComponent } from './suggestions-page.component'; -import { SuggestionListElementComponent } from '../suggestion-notifications/reciter-suggestions/suggestion-list-element/suggestion-list-element.component'; +import { + SuggestionApproveAndImport, + SuggestionListElementComponent +} from '../suggestion-notifications/reciter-suggestions/suggestion-list-element/suggestion-list-element.component'; import { SuggestionsService } from '../suggestion-notifications/reciter-suggestions/suggestions.service'; import { getMockSuggestionNotificationsStateService, getMockSuggestionsService } from '../shared/mocks/suggestion.mock'; import { buildPaginatedList, PaginatedList } from '../core/data/paginated-list.model'; @@ -104,4 +107,111 @@ describe('SuggestionPageComponent', () => { expect(component.researcherName).toBe(mockSuggestionTargetsObjectOne.display); expect(component.updatePage).toHaveBeenCalled(); }); + + it('should update page on pagination change', () => { + spyOn(component, 'updatePage').and.stub(); + + scheduler.schedule(() => fixture.detectChanges()); + scheduler.flush(); + component.onPaginationChange(); + expect(component.updatePage).toHaveBeenCalled(); + }); + + it('should update suggestion on page update', (done) => { + spyOn(component.processing$, 'next'); + spyOn(component.suggestionsRD$, 'next'); + + scheduler.schedule(() => fixture.detectChanges()); + scheduler.flush(); + paginationService.getFindListOptions().subscribe(() => { + expect(component.processing$.next).toHaveBeenCalled(); + expect(mockSuggestionsService.getSuggestions).toHaveBeenCalled(); + expect(component.suggestionsRD$.next).toHaveBeenCalled(); + expect(mockSuggestionsService.clearSuggestionRequests).toHaveBeenCalled(); + done(); + }); + component.updatePage(); + }); + + it('should flag suggestion for deletion', () => { + spyOn(component, 'updatePage').and.stub(); + + scheduler.schedule(() => fixture.detectChanges()); + scheduler.flush(); + component.notMine('1'); + expect(mockSuggestionsService.notMine).toHaveBeenCalledWith('1'); + expect(mockSuggestionsTargetStateService.dispatchRefreshUserSuggestionsAction).toHaveBeenCalled(); + expect(component.updatePage).toHaveBeenCalled(); + }); + + it('should flag all suggestion for deletion', () => { + spyOn(component, 'updatePage').and.stub(); + + scheduler.schedule(() => fixture.detectChanges()); + scheduler.flush(); + component.notMineAllSelected(); + expect(mockSuggestionsService.notMineMultiple).toHaveBeenCalled(); + expect(mockSuggestionsTargetStateService.dispatchRefreshUserSuggestionsAction).toHaveBeenCalled(); + expect(component.updatePage).toHaveBeenCalled(); + }); + + it('should approve and import', () => { + spyOn(component, 'updatePage').and.stub(); + + scheduler.schedule(() => fixture.detectChanges()); + scheduler.flush(); + component.approveAndImport({collectionId: '1234'} as unknown as SuggestionApproveAndImport); + expect(mockSuggestionsService.approveAndImport).toHaveBeenCalled(); + expect(mockSuggestionsTargetStateService.dispatchRefreshUserSuggestionsAction).toHaveBeenCalled(); + expect(component.updatePage).toHaveBeenCalled(); + }); + + it('should approve and import multiple suggestions', () => { + spyOn(component, 'updatePage').and.stub(); + + scheduler.schedule(() => fixture.detectChanges()); + scheduler.flush(); + component.approveAndImportAllSelected({collectionId: '1234'} as unknown as SuggestionApproveAndImport); + expect(mockSuggestionsService.approveAndImportMultiple).toHaveBeenCalled(); + expect(mockSuggestionsTargetStateService.dispatchRefreshUserSuggestionsAction).toHaveBeenCalled(); + expect(component.updatePage).toHaveBeenCalled(); + }); + + it('should select and deselect suggestion', () => { + component.selectedSuggestions = {}; + component.onSelected(mockSuggestionPublicationOne, true); + expect(component.selectedSuggestions[mockSuggestionPublicationOne.id]).toBe(mockSuggestionPublicationOne); + component.onSelected(mockSuggestionPublicationOne, false); + expect(component.selectedSuggestions[mockSuggestionPublicationOne.id]).toBeUndefined(); + }); + + it('should toggle all suggestions', () => { + component.selectedSuggestions = {}; + component.onToggleSelectAll([mockSuggestionPublicationOne, mockSuggestionPublicationTwo]); + expect(component.selectedSuggestions[mockSuggestionPublicationOne.id]).toEqual(mockSuggestionPublicationOne); + expect(component.selectedSuggestions[mockSuggestionPublicationTwo.id]).toEqual(mockSuggestionPublicationTwo); + component.onToggleSelectAll([mockSuggestionPublicationOne, mockSuggestionPublicationTwo]); + expect(component.selectedSuggestions).toEqual({}); + }); + + it('should return all selected suggestions count', () => { + component.selectedSuggestions = {}; + component.onToggleSelectAll([mockSuggestionPublicationOne, mockSuggestionPublicationTwo]); + expect(component.getSelectedSuggestionsCount()).toEqual(2); + }); + + it('should check if all collection is fixed', () => { + component.isCollectionFixed([mockSuggestionPublicationOne, mockSuggestionPublicationTwo]); + expect(mockSuggestionsService.isCollectionFixed).toHaveBeenCalled(); + }); + + it('should translate suggestion source', () => { + component.translateSuggestionSource(); + expect(mockSuggestionsService.translateSuggestionSource).toHaveBeenCalled(); + }); + + it('should translate suggestion type', () => { + component.translateSuggestionType(); + expect(mockSuggestionsService.translateSuggestionType).toHaveBeenCalled(); + }); }); From 4dedda3f9f990d0f353d886d16afd83630239201 Mon Sep 17 00:00:00 2001 From: frabacche Date: Tue, 23 Jan 2024 14:30:07 +0100 Subject: [PATCH 13/34] CST-5249_suggestion refactor label name openaire --- src/assets/i18n/en.json5 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 29fa6a01800..0e5ea850740 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -3612,13 +3612,13 @@ "reciter.suggestion.suggestionFor": "Suggestion for", - "reciter.suggestion.source.oaire": "OpenAIRE Graph", + "reciter.suggestion.source.openaire": "OpenAIRE Graph", "reciter.suggestion.from.source": "from the ", "reciter.suggestion.totalScore": "Total Score", - "reciter.suggestion.type.oaire": "OpenAIRE", + "reciter.suggestion.type.openaire": "OpenAIRE", "register-email.title": "New user registration", From 2bb421c7cf06865a49773e8142cc6dd7f92ca42f Mon Sep 17 00:00:00 2001 From: frabacche Date: Tue, 23 Jan 2024 17:44:49 +0100 Subject: [PATCH 14/34] CST-5249_suggestion community first code review --- .../admin-notifications-routing-paths.ts | 2 +- .../admin-notifications-routing.module.ts | 4 +-- src/app/menu.resolver.ts | 4 +-- .../suggestion-actions.component.html | 7 ++-- .../suggestion-list-element.component.html | 4 ++- .../suggestion-targets.component.html | 4 +-- .../suggestion-targets.component.ts | 1 - .../suggestions-page.component.html | 6 ++-- src/assets/i18n/en.json5 | 4 --- src/themes/custom/eager-theme.module.ts | 33 ++++++++++--------- 10 files changed, 35 insertions(+), 34 deletions(-) diff --git a/src/app/admin/admin-notifications/admin-notifications-routing-paths.ts b/src/app/admin/admin-notifications/admin-notifications-routing-paths.ts index 870458fa9f1..7ae141335fd 100644 --- a/src/app/admin/admin-notifications/admin-notifications-routing-paths.ts +++ b/src/app/admin/admin-notifications/admin-notifications-routing-paths.ts @@ -2,7 +2,7 @@ import { URLCombiner } from '../../core/url-combiner/url-combiner'; import { getNotificationsModuleRoute } from '../admin-routing-paths'; export const QUALITY_ASSURANCE_EDIT_PATH = 'quality-assurance'; -export const NOTIFICATIONS_RECITER_SUGGESTION_PATH = 'suggestion-targets'; +export const PUBLICATION_CLAIMS_PATH = 'publication-claims'; export function getQualityAssuranceRoute(id: string) { return new URLCombiner(getNotificationsModuleRoute(), QUALITY_ASSURANCE_EDIT_PATH, id).toString(); diff --git a/src/app/admin/admin-notifications/admin-notifications-routing.module.ts b/src/app/admin/admin-notifications/admin-notifications-routing.module.ts index 9692b65d929..782c592e5de 100644 --- a/src/app/admin/admin-notifications/admin-notifications-routing.module.ts +++ b/src/app/admin/admin-notifications/admin-notifications-routing.module.ts @@ -4,7 +4,7 @@ import { RouterModule } from '@angular/router'; import { AuthenticatedGuard } from '../../core/auth/authenticated.guard'; import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver'; import { I18nBreadcrumbsService } from '../../core/breadcrumbs/i18n-breadcrumbs.service'; -import { NOTIFICATIONS_RECITER_SUGGESTION_PATH } from './admin-notifications-routing-paths'; +import { PUBLICATION_CLAIMS_PATH } from './admin-notifications-routing-paths'; import { AdminNotificationsSuggestionTargetsPageComponent } from './admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page.component'; import { AdminNotificationsSuggestionTargetsPageResolver } from './admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page-resolver.service'; import { QUALITY_ASSURANCE_EDIT_PATH } from './admin-notifications-routing-paths'; @@ -25,7 +25,7 @@ import { RouterModule.forChild([ { canActivate: [ AuthenticatedGuard ], - path: `${NOTIFICATIONS_RECITER_SUGGESTION_PATH}`, + path: `${PUBLICATION_CLAIMS_PATH}`, component: AdminNotificationsSuggestionTargetsPageComponent, pathMatch: 'full', resolve: { diff --git a/src/app/menu.resolver.ts b/src/app/menu.resolver.ts index 506f30d6a5d..b5d8608d061 100644 --- a/src/app/menu.resolver.ts +++ b/src/app/menu.resolver.ts @@ -47,7 +47,7 @@ import { import { ExportBatchSelectorComponent } from './shared/dso-selector/modal-wrappers/export-batch-selector/export-batch-selector.component'; -import { NOTIFICATIONS_RECITER_SUGGESTION_PATH } from './admin/admin-notifications/admin-notifications-routing-paths'; +import { PUBLICATION_CLAIMS_PATH } from './admin/admin-notifications/admin-notifications-routing-paths'; /** * Creates all of the app's menus @@ -568,7 +568,7 @@ export class MenuResolver implements Resolve { model: { type: MenuItemType.LINK, text: 'menu.section.notifications_reciter', - link: '/admin/notifications/' + NOTIFICATIONS_RECITER_SUGGESTION_PATH + link: '/admin/notifications/' + PUBLICATION_CLAIMS_PATH } as LinkMenuItemModel, }, /* Admin Search */ diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestion-actions/suggestion-actions.component.html b/src/app/suggestion-notifications/reciter-suggestions/suggestion-actions/suggestion-actions.component.html index 7ec3e61395e..4d9fdb8b8e2 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/suggestion-actions/suggestion-actions.component.html +++ b/src/app/suggestion-notifications/reciter-suggestions/suggestion-actions/suggestion-actions.component.html @@ -21,8 +21,9 @@ - diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestion-list-element/suggestion-list-element.component.html b/src/app/suggestion-notifications/reciter-suggestions/suggestion-list-element/suggestion-list-element.component.html index f37d595c45b..5054f8806a5 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/suggestion-list-element/suggestion-list-element.component.html +++ b/src/app/suggestion-notifications/reciter-suggestions/suggestion-list-element/suggestion-list-element.component.html @@ -4,7 +4,8 @@
+ [checked]="isSelected" (change)="changeSelected($event)" + [attr.aria-label]="object.display"/>
@@ -19,6 +20,7 @@ diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.component.html b/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.component.html index 280f574ec4c..04e967c3d54 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.component.html +++ b/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.component.html @@ -1,7 +1,7 @@
- +

{{'reciter.suggestion.title'| translate}}

{{'reciter.suggestion.title'| transla - {{targetElement.display}} + {{targetElement.display}}
diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.component.ts b/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.component.ts index e5524765da0..cfa168e2b83 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.component.ts +++ b/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.component.ts @@ -136,7 +136,6 @@ export class SuggestionTargetsComponent implements OnInit { distinctUntilChanged(), take(1) ).subscribe((options: PaginationComponentOptions) => { - console.log('HELLO suggestion called!', options); this.suggestionTargetsStateService.dispatchRetrieveReciterSuggestionTargets( this.source, options.pageSize, diff --git a/src/app/suggestions-page/suggestions-page.component.html b/src/app/suggestions-page/suggestions-page.component.html index fb5f08b6a4c..59b6f58c5fc 100644 --- a/src/app/suggestions-page/suggestions-page.component.html +++ b/src/app/suggestions-page/suggestions-page.component.html @@ -4,12 +4,12 @@
-

+

{{ translateSuggestionType() | translate }} {{'reciter.suggestion.suggestionFor' | translate}} - {{researcherName}} + {{researcherName}} {{'reciter.suggestion.from.source' | translate}} {{ translateSuggestionSource() | translate }} -

+
diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index dea20aa1a50..0e5ea850740 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -1774,10 +1774,6 @@ "form.repeatable.sort.tip": "Drop the item in the new position", - "form.number-picker.decrement": "Decrement {{field}}", - - "form.number-picker.increment": "Increment {{field}}", - "grant-deny-request-copy.deny": "Don't send copy", "grant-deny-request-copy.email.back": "Back", diff --git a/src/themes/custom/eager-theme.module.ts b/src/themes/custom/eager-theme.module.ts index 31047e239ac..8889c0c3df1 100644 --- a/src/themes/custom/eager-theme.module.ts +++ b/src/themes/custom/eager-theme.module.ts @@ -102,21 +102,24 @@ const DECLARATIONS = [ ]; @NgModule({ - imports: [ - CommonModule, - SharedModule, - RootModule, - NavbarModule, - SharedBrowseByModule, - ResultsBackButtonModule, - ItemPageModule, - ItemSharedModule, - DsoPageModule, - ], - declarations: DECLARATIONS, - providers: [ - ...ENTRY_COMPONENTS.map((component) => ({provide: component})) - ], + imports: [ + CommonModule, + SharedModule, + RootModule, + NavbarModule, + SharedBrowseByModule, + ResultsBackButtonModule, + ItemPageModule, + ItemSharedModule, + DsoPageModule, + ], + declarations: DECLARATIONS, + providers: [ + ...ENTRY_COMPONENTS.map((component) => ({ provide: component })) + ], + exports: [ + ItemSearchResultListElementComponent + ] }) /** * This module is included in the main bundle that gets downloaded at first page load. So it should From afa6559b19e410cab444e865b26300a9e338babc Mon Sep 17 00:00:00 2001 From: frabacche Date: Wed, 24 Jan 2024 18:32:21 +0100 Subject: [PATCH 15/34] CST-5249_suggestion refactor deleting 'reciter', docs --- ...ublication-claim-page-resolver.service.ts} | 6 +- ...ons-publication-claim-page.component.html} | 0 ...ons-publication-claim-page.component.scss} | 0 ...-publication-claim-page.component.spec.ts} | 2 +- ...ations-publication-claim-page.component.ts | 10 +++ .../admin-notifications-routing-paths.ts | 2 +- .../admin-notifications-routing.module.ts | 12 ++-- ...tions-suggestion-targets-page.component.ts | 10 --- .../admin-notifications.module.ts | 2 +- src/app/core/core.module.ts | 4 +- .../suggestion-objects.resource-type.ts | 2 +- .../suggestion-source-object.resource-type.ts | 2 +- .../models/suggestion-source.model.ts | 10 +-- .../suggestion-target-object.resource-type.ts | 2 +- .../models/suggestion-target.model.ts | 10 +-- .../models/suggestion.model.ts | 15 ++-- .../source/suggestion-source-data.service.ts | 31 ++++---- .../suggestions-source-data.service.spec.ts | 30 ++++---- .../suggestion-data.service.spec.ts | 24 +++---- .../suggestions-data.service.ts | 32 ++++----- .../target/suggestion-target-data.service.ts | 32 ++++----- .../suggestions-target-data.service.spec.ts | 36 +++++----- src/app/menu.resolver.ts | 4 +- .../notifications/notifications-effects.ts | 2 +- src/app/notifications/notifications.module.ts | 22 +++--- .../notifications/notifications.reducer.ts | 2 +- ...k.ts => publication-claim-targets.mock.ts} | 2 +- ...tion.mock.ts => publication-claim.mock.ts} | 4 +- src/app/shared/mocks/suggestion.mock.ts | 4 +- .../{reciter-suggestions => }/selectors.ts | 56 +++++++-------- .../suggestion-actions.component.html | 4 +- .../suggestion-actions.component.scss | 0 .../suggestion-actions.component.ts | 21 +++--- .../suggestion-evidences.component.html | 0 .../suggestion-evidences.component.scss | 0 .../suggestion-evidences.component.ts | 7 +- .../suggestion-list-element.component.html | 2 +- .../suggestion-list-element.component.scss | 0 .../suggestion-list-element.component.spec.ts | 10 +-- .../suggestion-list-element.component.ts | 22 +++--- .../suggestion-targets.actions.ts | 4 +- .../suggestion-targets.component.html | 5 +- .../suggestion-targets.component.scss | 0 .../suggestion-targets.component.ts | 22 +++--- .../suggestion-targets.effects.ts | 6 +- .../suggestion-targets.reducer.ts | 2 +- .../suggestion-targets.state.service.ts | 70 +++++++++---------- .../suggestion.service.spec.ts | 32 ++++----- .../suggestions-notification.component.html | 0 .../suggestions-notification.component.scss | 0 .../suggestions-notification.component.ts | 11 +-- .../suggestions-popup.component.html | 0 .../suggestions-popup.component.scss | 0 .../suggestions-popup.component.spec.ts | 6 +- .../suggestions-popup.component.ts | 19 ++--- .../suggestions.service.ts | 47 +++++++------ .../suggestions-page-routing.module.ts | 11 +-- .../suggestions-page.component.html | 4 +- .../suggestions-page.component.spec.ts | 18 ++--- .../suggestions-page.component.ts | 18 ++--- .../suggestions-page.module.ts | 4 +- .../suggestions-page.resolver.ts | 4 +- src/assets/i18n/en.json5 | 68 +++++++++--------- 63 files changed, 410 insertions(+), 377 deletions(-) rename src/app/admin/admin-notifications/{admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page-resolver.service.ts => admin-notifications-publication-claim-page/admin-notifications-publication-claim-page-resolver.service.ts} (78%) rename src/app/admin/admin-notifications/{admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page.component.html => admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component.html} (100%) rename src/app/admin/admin-notifications/{admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page.component.scss => admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component.scss} (100%) rename src/app/admin/admin-notifications/{admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page.component.spec.ts => admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component.spec.ts} (95%) create mode 100644 src/app/admin/admin-notifications/admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component.ts delete mode 100644 src/app/admin/admin-notifications/admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page.component.ts rename src/app/core/suggestion-notifications/{reciter-suggestions => }/models/suggestion-objects.resource-type.ts (76%) rename src/app/core/suggestion-notifications/{reciter-suggestions => }/models/suggestion-source-object.resource-type.ts (77%) rename src/app/core/suggestion-notifications/{reciter-suggestions => }/models/suggestion-source.model.ts (71%) rename src/app/core/suggestion-notifications/{reciter-suggestions => }/models/suggestion-target-object.resource-type.ts (77%) rename src/app/core/suggestion-notifications/{reciter-suggestions => }/models/suggestion-target.model.ts (75%) rename src/app/core/suggestion-notifications/{reciter-suggestions => }/models/suggestion.model.ts (74%) rename src/app/core/suggestion-notifications/{reciter-suggestions => }/source/suggestion-source-data.service.ts (80%) rename src/app/core/suggestion-notifications/{reciter-suggestions => }/source/suggestions-source-data.service.spec.ts (76%) rename src/app/core/suggestion-notifications/{reciter-suggestions => }/suggestion-data.service.spec.ts (87%) rename src/app/core/suggestion-notifications/{reciter-suggestions => }/suggestions-data.service.ts (87%) rename src/app/core/suggestion-notifications/{reciter-suggestions => }/target/suggestion-target-data.service.ts (83%) rename src/app/core/suggestion-notifications/{reciter-suggestions => }/target/suggestions-target-data.service.spec.ts (77%) rename src/app/shared/mocks/{reciter-suggestion-targets.mock.ts => publication-claim-targets.mock.ts} (96%) rename src/app/shared/mocks/{reciter-suggestion.mock.ts => publication-claim.mock.ts} (98%) rename src/app/suggestion-notifications/{reciter-suggestions => }/selectors.ts (50%) rename src/app/suggestion-notifications/{reciter-suggestions => }/suggestion-actions/suggestion-actions.component.html (89%) rename src/app/suggestion-notifications/{reciter-suggestions => }/suggestion-actions/suggestion-actions.component.scss (100%) rename src/app/suggestion-notifications/{reciter-suggestions => }/suggestion-actions/suggestion-actions.component.ts (73%) rename src/app/suggestion-notifications/{reciter-suggestions => }/suggestion-list-element/suggestion-evidences/suggestion-evidences.component.html (100%) rename src/app/suggestion-notifications/{reciter-suggestions => }/suggestion-list-element/suggestion-evidences/suggestion-evidences.component.scss (100%) rename src/app/suggestion-notifications/{reciter-suggestions => }/suggestion-list-element/suggestion-evidences/suggestion-evidences.component.ts (58%) rename src/app/suggestion-notifications/{reciter-suggestions => }/suggestion-list-element/suggestion-list-element.component.html (95%) rename src/app/suggestion-notifications/{reciter-suggestions => }/suggestion-list-element/suggestion-list-element.component.scss (100%) rename src/app/suggestion-notifications/{reciter-suggestions => }/suggestion-list-element/suggestion-list-element.component.spec.ts (88%) rename src/app/suggestion-notifications/{reciter-suggestions => }/suggestion-list-element/suggestion-list-element.component.ts (77%) rename src/app/suggestion-notifications/{reciter-suggestions => }/suggestion-targets/suggestion-targets.actions.ts (96%) rename src/app/suggestion-notifications/{reciter-suggestions => }/suggestion-targets/suggestion-targets.component.html (87%) rename src/app/suggestion-notifications/{reciter-suggestions => }/suggestion-targets/suggestion-targets.component.scss (100%) rename src/app/suggestion-notifications/{reciter-suggestions => }/suggestion-targets/suggestion-targets.component.ts (80%) rename src/app/suggestion-notifications/{reciter-suggestions => }/suggestion-targets/suggestion-targets.effects.ts (91%) rename src/app/suggestion-notifications/{reciter-suggestions => }/suggestion-targets/suggestion-targets.reducer.ts (95%) rename src/app/suggestion-notifications/{reciter-suggestions => }/suggestion-targets/suggestion-targets.state.service.ts (54%) rename src/app/suggestion-notifications/{reciter-suggestions => }/suggestion.service.spec.ts (83%) rename src/app/suggestion-notifications/{reciter-suggestions => }/suggestions-notification/suggestions-notification.component.html (100%) rename src/app/suggestion-notifications/{reciter-suggestions => }/suggestions-notification/suggestions-notification.component.scss (100%) rename src/app/suggestion-notifications/{reciter-suggestions => }/suggestions-notification/suggestions-notification.component.ts (75%) rename src/app/suggestion-notifications/{reciter-suggestions => }/suggestions-popup/suggestions-popup.component.html (100%) rename src/app/suggestion-notifications/{reciter-suggestions => }/suggestions-popup/suggestions-popup.component.scss (100%) rename src/app/suggestion-notifications/{reciter-suggestions => }/suggestions-popup/suggestions-popup.component.spec.ts (92%) rename src/app/suggestion-notifications/{reciter-suggestions => }/suggestions-popup/suggestions-popup.component.ts (75%) rename src/app/suggestion-notifications/{reciter-suggestions => }/suggestions.service.ts (84%) diff --git a/src/app/admin/admin-notifications/admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page-resolver.service.ts b/src/app/admin/admin-notifications/admin-notifications-publication-claim-page/admin-notifications-publication-claim-page-resolver.service.ts similarity index 78% rename from src/app/admin/admin-notifications/admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page-resolver.service.ts rename to src/app/admin/admin-notifications/admin-notifications-publication-claim-page/admin-notifications-publication-claim-page-resolver.service.ts index df1f4b81e69..add9a504ddb 100644 --- a/src/app/admin/admin-notifications/admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page-resolver.service.ts +++ b/src/app/admin/admin-notifications/admin-notifications-publication-claim-page/admin-notifications-publication-claim-page-resolver.service.ts @@ -4,7 +4,7 @@ import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/r /** * Interface for the route parameters. */ -export interface AdminNotificationsSuggestionTargetsPageParams { +export interface AdminNotificationsPublicationClaimPageParams { pageId?: string; pageSize?: number; currentPage?: number; @@ -14,7 +14,7 @@ export interface AdminNotificationsSuggestionTargetsPageParams { * This class represents a resolver that retrieve the route data before the route is activated. */ @Injectable() -export class AdminNotificationsSuggestionTargetsPageResolver implements Resolve { +export class AdminNotificationsPublicationClaimPageResolver implements Resolve { /** * Method for resolving the parameters in the current route. @@ -22,7 +22,7 @@ export class AdminNotificationsSuggestionTargetsPageResolver implements Resolve< * @param {RouterStateSnapshot} state The current RouterStateSnapshot * @returns AdminNotificationsSuggestionTargetsPageParams Emits the route parameters */ - resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): AdminNotificationsSuggestionTargetsPageParams { + resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): AdminNotificationsPublicationClaimPageParams { return { pageId: route.queryParams.pageId, pageSize: parseInt(route.queryParams.pageSize, 10), diff --git a/src/app/admin/admin-notifications/admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page.component.html b/src/app/admin/admin-notifications/admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component.html similarity index 100% rename from src/app/admin/admin-notifications/admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page.component.html rename to src/app/admin/admin-notifications/admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component.html diff --git a/src/app/admin/admin-notifications/admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page.component.scss b/src/app/admin/admin-notifications/admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component.scss similarity index 100% rename from src/app/admin/admin-notifications/admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page.component.scss rename to src/app/admin/admin-notifications/admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component.scss diff --git a/src/app/admin/admin-notifications/admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page.component.spec.ts b/src/app/admin/admin-notifications/admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component.spec.ts similarity index 95% rename from src/app/admin/admin-notifications/admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page.component.spec.ts rename to src/app/admin/admin-notifications/admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component.spec.ts index f9e407f4021..c0209da8988 100644 --- a/src/app/admin/admin-notifications/admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page.component.spec.ts +++ b/src/app/admin/admin-notifications/admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component.spec.ts @@ -1,6 +1,6 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { AdminNotificationsSuggestionTargetsPageComponent } from './admin-notifications-suggestion-targets-page.component'; +import { AdminNotificationsSuggestionTargetsPageComponent } from './admin-notifications-publication-claim-page.component'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { CommonModule } from '@angular/common'; import { TranslateModule } from '@ngx-translate/core'; diff --git a/src/app/admin/admin-notifications/admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component.ts b/src/app/admin/admin-notifications/admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component.ts new file mode 100644 index 00000000000..10023ead934 --- /dev/null +++ b/src/app/admin/admin-notifications/admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component.ts @@ -0,0 +1,10 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'ds-admin-notifications-publication-claim-page', + templateUrl: './admin-notifications-publication-claim-page.component.html', + styleUrls: ['./admin-notifications-publication-claim-page.component.scss'] +}) +export class AdminNotificationsSuggestionTargetsPageComponent { + +} diff --git a/src/app/admin/admin-notifications/admin-notifications-routing-paths.ts b/src/app/admin/admin-notifications/admin-notifications-routing-paths.ts index 7ae141335fd..f92a96d2422 100644 --- a/src/app/admin/admin-notifications/admin-notifications-routing-paths.ts +++ b/src/app/admin/admin-notifications/admin-notifications-routing-paths.ts @@ -2,7 +2,7 @@ import { URLCombiner } from '../../core/url-combiner/url-combiner'; import { getNotificationsModuleRoute } from '../admin-routing-paths'; export const QUALITY_ASSURANCE_EDIT_PATH = 'quality-assurance'; -export const PUBLICATION_CLAIMS_PATH = 'publication-claims'; +export const PUBLICATION_CLAIMS_PATH = 'publication-claim'; export function getQualityAssuranceRoute(id: string) { return new URLCombiner(getNotificationsModuleRoute(), QUALITY_ASSURANCE_EDIT_PATH, id).toString(); diff --git a/src/app/admin/admin-notifications/admin-notifications-routing.module.ts b/src/app/admin/admin-notifications/admin-notifications-routing.module.ts index 782c592e5de..648bdc0a1f1 100644 --- a/src/app/admin/admin-notifications/admin-notifications-routing.module.ts +++ b/src/app/admin/admin-notifications/admin-notifications-routing.module.ts @@ -5,8 +5,8 @@ import { AuthenticatedGuard } from '../../core/auth/authenticated.guard'; import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver'; import { I18nBreadcrumbsService } from '../../core/breadcrumbs/i18n-breadcrumbs.service'; import { PUBLICATION_CLAIMS_PATH } from './admin-notifications-routing-paths'; -import { AdminNotificationsSuggestionTargetsPageComponent } from './admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page.component'; -import { AdminNotificationsSuggestionTargetsPageResolver } from './admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page-resolver.service'; +import { AdminNotificationsSuggestionTargetsPageComponent } from './admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component'; +import { AdminNotificationsPublicationClaimPageResolver } from './admin-notifications-publication-claim-page/admin-notifications-publication-claim-page-resolver.service'; import { QUALITY_ASSURANCE_EDIT_PATH } from './admin-notifications-routing-paths'; import { AdminQualityAssuranceTopicsPageComponent } from './admin-quality-assurance-topics-page/admin-quality-assurance-topics-page.component'; import { AdminQualityAssuranceEventsPageComponent } from './admin-quality-assurance-events-page/admin-quality-assurance-events-page.component'; @@ -30,11 +30,11 @@ import { pathMatch: 'full', resolve: { breadcrumb: I18nBreadcrumbResolver, - reciterSuggestionTargetParams: AdminNotificationsSuggestionTargetsPageResolver + suggestionTargetParams: AdminNotificationsPublicationClaimPageResolver }, data: { - title: 'admin.notifications.recitersuggestion.page.title', - breadcrumbKey: 'admin.notifications.recitersuggestion', + title: 'admin.notifications.publicationclaim.page.title', + breadcrumbKey: 'admin.notifications.publicationclaim', showBreadcrumbsFluid: false } }, @@ -89,7 +89,7 @@ import { providers: [ I18nBreadcrumbResolver, I18nBreadcrumbsService, - AdminNotificationsSuggestionTargetsPageResolver, + AdminNotificationsPublicationClaimPageResolver, SourceDataResolver, AdminQualityAssuranceSourcePageResolver, AdminQualityAssuranceTopicsPageResolver, diff --git a/src/app/admin/admin-notifications/admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page.component.ts b/src/app/admin/admin-notifications/admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page.component.ts deleted file mode 100644 index a9a77f50890..00000000000 --- a/src/app/admin/admin-notifications/admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page.component.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Component } from '@angular/core'; - -@Component({ - selector: 'ds-admin-notifications-reciter-page', - templateUrl: './admin-notifications-suggestion-targets-page.component.html', - styleUrls: ['./admin-notifications-suggestion-targets-page.component.scss'] -}) -export class AdminNotificationsSuggestionTargetsPageComponent { - -} diff --git a/src/app/admin/admin-notifications/admin-notifications.module.ts b/src/app/admin/admin-notifications/admin-notifications.module.ts index 93e184c9427..3566bdd91ad 100644 --- a/src/app/admin/admin-notifications/admin-notifications.module.ts +++ b/src/app/admin/admin-notifications/admin-notifications.module.ts @@ -3,7 +3,7 @@ import { NgModule } from '@angular/core'; import { CoreModule } from '../../core/core.module'; import { SharedModule } from '../../shared/shared.module'; import { AdminNotificationsRoutingModule } from './admin-notifications-routing.module'; -import { AdminNotificationsSuggestionTargetsPageComponent } from './admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page.component'; +import { AdminNotificationsSuggestionTargetsPageComponent } from './admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component'; import { AdminQualityAssuranceTopicsPageComponent } from './admin-quality-assurance-topics-page/admin-quality-assurance-topics-page.component'; import { AdminQualityAssuranceEventsPageComponent } from './admin-quality-assurance-events-page/admin-quality-assurance-events-page.component'; import { AdminQualityAssuranceSourcePageComponent } from './admin-quality-assurance-source-page-component/admin-quality-assurance-source-page.component'; diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index 987ab5e6034..f151f10f661 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -185,8 +185,8 @@ import { FlatBrowseDefinition } from './shared/flat-browse-definition.model'; import { ValueListBrowseDefinition } from './shared/value-list-browse-definition.model'; import { NonHierarchicalBrowseDefinition } from './shared/non-hierarchical-browse-definition'; import { BulkAccessConditionOptions } from './config/models/bulk-access-condition-options.model'; -import { SuggestionTarget } from './suggestion-notifications/reciter-suggestions/models/suggestion-target.model'; -import { SuggestionSource } from './suggestion-notifications/reciter-suggestions/models/suggestion-source.model'; +import { SuggestionTarget } from './suggestion-notifications/models/suggestion-target.model'; +import { SuggestionSource } from './suggestion-notifications/models/suggestion-source.model'; /** * When not in production, endpoint responses can be mocked for testing purposes diff --git a/src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-objects.resource-type.ts b/src/app/core/suggestion-notifications/models/suggestion-objects.resource-type.ts similarity index 76% rename from src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-objects.resource-type.ts rename to src/app/core/suggestion-notifications/models/suggestion-objects.resource-type.ts index 8f87027a8c5..8f83d86376b 100644 --- a/src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-objects.resource-type.ts +++ b/src/app/core/suggestion-notifications/models/suggestion-objects.resource-type.ts @@ -1,4 +1,4 @@ -import { ResourceType } from '../../../shared/resource-type'; +import { ResourceType } from '../../shared/resource-type'; /** * The resource type for the Suggestion object diff --git a/src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-source-object.resource-type.ts b/src/app/core/suggestion-notifications/models/suggestion-source-object.resource-type.ts similarity index 77% rename from src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-source-object.resource-type.ts rename to src/app/core/suggestion-notifications/models/suggestion-source-object.resource-type.ts index 2e26fe4301c..e319ed5109b 100644 --- a/src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-source-object.resource-type.ts +++ b/src/app/core/suggestion-notifications/models/suggestion-source-object.resource-type.ts @@ -1,4 +1,4 @@ -import { ResourceType } from '../../../shared/resource-type'; +import { ResourceType } from '../../shared/resource-type'; /** * The resource type for the Suggestion Source object diff --git a/src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-source.model.ts b/src/app/core/suggestion-notifications/models/suggestion-source.model.ts similarity index 71% rename from src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-source.model.ts rename to src/app/core/suggestion-notifications/models/suggestion-source.model.ts index 007520800d3..12d9d7e9d84 100644 --- a/src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-source.model.ts +++ b/src/app/core/suggestion-notifications/models/suggestion-source.model.ts @@ -1,11 +1,11 @@ import { autoserialize, deserialize } from 'cerialize'; import { SUGGESTION_SOURCE } from './suggestion-source-object.resource-type'; -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 {CacheableObject} from '../../../cache/cacheable-object.model'; +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 {CacheableObject} from '../../cache/cacheable-object.model'; /** * The interface representing the Suggestion Source model diff --git a/src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-target-object.resource-type.ts b/src/app/core/suggestion-notifications/models/suggestion-target-object.resource-type.ts similarity index 77% rename from src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-target-object.resource-type.ts rename to src/app/core/suggestion-notifications/models/suggestion-target-object.resource-type.ts index 71dd41912ac..81b1b5c2619 100644 --- a/src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-target-object.resource-type.ts +++ b/src/app/core/suggestion-notifications/models/suggestion-target-object.resource-type.ts @@ -1,4 +1,4 @@ -import { ResourceType } from '../../../shared/resource-type'; +import { ResourceType } from '../../shared/resource-type'; /** * The resource type for the Suggestion Target object diff --git a/src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-target.model.ts b/src/app/core/suggestion-notifications/models/suggestion-target.model.ts similarity index 75% rename from src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-target.model.ts rename to src/app/core/suggestion-notifications/models/suggestion-target.model.ts index 2afe170e777..99d9a8628ad 100644 --- a/src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion-target.model.ts +++ b/src/app/core/suggestion-notifications/models/suggestion-target.model.ts @@ -1,12 +1,12 @@ import { autoserialize, deserialize } from 'cerialize'; -import { CacheableObject } from '../../../cache/cacheable-object.model'; +import { CacheableObject } from '../../cache/cacheable-object.model'; import { SUGGESTION_TARGET } from './suggestion-target-object.resource-type'; -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 { 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'; /** * The interface representing the Suggestion Target model diff --git a/src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion.model.ts b/src/app/core/suggestion-notifications/models/suggestion.model.ts similarity index 74% rename from src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion.model.ts rename to src/app/core/suggestion-notifications/models/suggestion.model.ts index c36d36794b5..ad58b1cfe55 100644 --- a/src/app/core/suggestion-notifications/reciter-suggestions/models/suggestion.model.ts +++ b/src/app/core/suggestion-notifications/models/suggestion.model.ts @@ -1,13 +1,16 @@ import { autoserialize, autoserializeAs, deserialize } from 'cerialize'; import { SUGGESTION } from './suggestion-objects.resource-type'; -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 { MetadataMap, MetadataMapSerializer } from '../../../shared/metadata.models'; -import {CacheableObject} from '../../../cache/cacheable-object.model'; +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 { MetadataMap, MetadataMapSerializer } from '../../shared/metadata.models'; +import {CacheableObject} from '../../cache/cacheable-object.model'; +/** + * The interface representing Suggestion Evidences such as scores (authorScore, datescore) + */ export interface SuggestionEvidences { [sectionId: string]: { score: string; diff --git a/src/app/core/suggestion-notifications/reciter-suggestions/source/suggestion-source-data.service.ts b/src/app/core/suggestion-notifications/source/suggestion-source-data.service.ts similarity index 80% rename from src/app/core/suggestion-notifications/reciter-suggestions/source/suggestion-source-data.service.ts rename to src/app/core/suggestion-notifications/source/suggestion-source-data.service.ts index 15b067f2ed1..f00a84c95b5 100644 --- a/src/app/core/suggestion-notifications/reciter-suggestions/source/suggestion-source-data.service.ts +++ b/src/app/core/suggestion-notifications/source/suggestion-source-data.service.ts @@ -1,24 +1,27 @@ import { Injectable } from '@angular/core'; -import { dataService } from '../../../data/base/data-service.decorator'; +import { dataService } from '../../data/base/data-service.decorator'; import { SUGGESTION_SOURCE } from '../models/suggestion-source-object.resource-type'; -import { IdentifiableDataService } from '../../../data/base/identifiable-data.service'; +import { IdentifiableDataService } from '../../data/base/identifiable-data.service'; import { SuggestionSource } from '../models/suggestion-source.model'; -import { FindAllData, FindAllDataImpl } from '../../../data/base/find-all-data'; +import { FindAllData, FindAllDataImpl } from '../../data/base/find-all-data'; import { Store } from '@ngrx/store'; -import { RequestService } from '../../../data/request.service'; -import { RemoteDataBuildService } from '../../../cache/builders/remote-data-build.service'; -import { CoreState } from '../../../core-state.model'; -import { ObjectCacheService } from '../../../cache/object-cache.service'; -import { HALEndpointService } from '../../../shared/hal-endpoint.service'; -import { NotificationsService } from '../../../../shared/notifications/notifications.service'; +import { RequestService } from '../../data/request.service'; +import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service'; +import { CoreState } from '../../core-state.model'; +import { ObjectCacheService } from '../../cache/object-cache.service'; +import { HALEndpointService } from '../../shared/hal-endpoint.service'; +import { NotificationsService } from '../../../shared/notifications/notifications.service'; import { HttpClient } from '@angular/common/http'; -import { FindListOptions } from '../../../data/find-list-options.model'; -import { FollowLinkConfig } from '../../../../shared/utils/follow-link-config.model'; -import { PaginatedList } from '../../../data/paginated-list.model'; -import { RemoteData } from '../../../data/remote-data'; +import { FindListOptions } from '../../data/find-list-options.model'; +import { FollowLinkConfig } from '../../../shared/utils/follow-link-config.model'; +import { PaginatedList } from '../../data/paginated-list.model'; +import { RemoteData } from '../../data/remote-data'; import { Observable } from 'rxjs/internal/Observable'; -import { DefaultChangeAnalyzer } from '../../../data/default-change-analyzer.service'; +import { DefaultChangeAnalyzer } from '../../data/default-change-analyzer.service'; +/** + * Service that retrieves Suggestion Source data + */ @Injectable() @dataService(SUGGESTION_SOURCE) export class SuggestionSourceDataService extends IdentifiableDataService { diff --git a/src/app/core/suggestion-notifications/reciter-suggestions/source/suggestions-source-data.service.spec.ts b/src/app/core/suggestion-notifications/source/suggestions-source-data.service.spec.ts similarity index 76% rename from src/app/core/suggestion-notifications/reciter-suggestions/source/suggestions-source-data.service.spec.ts rename to src/app/core/suggestion-notifications/source/suggestions-source-data.service.spec.ts index 5f8d51a6410..28f34b863d8 100644 --- a/src/app/core/suggestion-notifications/reciter-suggestions/source/suggestions-source-data.service.spec.ts +++ b/src/app/core/suggestion-notifications/source/suggestions-source-data.service.spec.ts @@ -1,25 +1,25 @@ import { TestScheduler } from 'rxjs/testing'; -import { RequestService } from '../../../data/request.service'; -import { RemoteDataBuildService } from '../../../cache/builders/remote-data-build.service'; -import { ObjectCacheService } from '../../../cache/object-cache.service'; -import { HALEndpointService } from '../../../shared/hal-endpoint.service'; -import { RequestEntry } from '../../../data/request-entry.model'; +import { RequestService } from '../../data/request.service'; +import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service'; +import { ObjectCacheService } from '../../cache/object-cache.service'; +import { HALEndpointService } from '../../shared/hal-endpoint.service'; +import { RequestEntry } from '../../data/request-entry.model'; import { cold, getTestScheduler } from 'jasmine-marbles'; -import { RestResponse } from '../../../cache/response.models'; +import { RestResponse } from '../../cache/response.models'; import { of as observableOf } from 'rxjs'; import { Store } from '@ngrx/store'; -import { CoreState } from '../../../core-state.model'; +import { CoreState } from '../../core-state.model'; import { HttpClient } from '@angular/common/http'; -import { NotificationsService } from '../../../../shared/notifications/notifications.service'; -import { DefaultChangeAnalyzer } from '../../../data/default-change-analyzer.service'; -import { testFindAllDataImplementation } from '../../../data/base/find-all-data.spec'; -import { FindAllData } from '../../../data/base/find-all-data'; -import { GetRequest } from '../../../data/request.models'; +import { NotificationsService } from '../../../shared/notifications/notifications.service'; +import { DefaultChangeAnalyzer } from '../../data/default-change-analyzer.service'; +import { testFindAllDataImplementation } from '../../data/base/find-all-data.spec'; +import { FindAllData } from '../../data/base/find-all-data'; +import { GetRequest } from '../../data/request.models'; import { createSuccessfulRemoteDataObject$ -} from '../../../../shared/remote-data.utils'; -import { RemoteData } from '../../../data/remote-data'; -import { RequestEntryState } from '../../../data/request-entry-state.model'; +} from '../../../shared/remote-data.utils'; +import { RemoteData } from '../../data/remote-data'; +import { RequestEntryState } from '../../data/request-entry-state.model'; import { SuggestionSourceDataService } from './suggestion-source-data.service'; import { SuggestionSource } from '../models/suggestion-source.model'; diff --git a/src/app/core/suggestion-notifications/reciter-suggestions/suggestion-data.service.spec.ts b/src/app/core/suggestion-notifications/suggestion-data.service.spec.ts similarity index 87% rename from src/app/core/suggestion-notifications/reciter-suggestions/suggestion-data.service.spec.ts rename to src/app/core/suggestion-notifications/suggestion-data.service.spec.ts index 93b29eff068..a854514c896 100644 --- a/src/app/core/suggestion-notifications/reciter-suggestions/suggestion-data.service.spec.ts +++ b/src/app/core/suggestion-notifications/suggestion-data.service.spec.ts @@ -1,25 +1,25 @@ import { TestScheduler } from 'rxjs/testing'; import { SuggestionDataServiceImpl, SuggestionsDataService } from './suggestions-data.service'; -import { RequestService } from '../../data/request.service'; -import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service'; -import { ObjectCacheService } from '../../cache/object-cache.service'; -import { HALEndpointService } from '../../shared/hal-endpoint.service'; -import { NotificationsService } from '../../../shared/notifications/notifications.service'; +import { RequestService } from '../data/request.service'; +import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; +import { ObjectCacheService } from '../cache/object-cache.service'; +import { HALEndpointService } from '../shared/hal-endpoint.service'; +import { NotificationsService } from '../../shared/notifications/notifications.service'; import { HttpClient } from '@angular/common/http'; -import { DefaultChangeAnalyzer } from '../../data/default-change-analyzer.service'; +import { DefaultChangeAnalyzer } from '../data/default-change-analyzer.service'; import { Suggestion } from './models/suggestion.model'; import { cold, getTestScheduler } from 'jasmine-marbles'; -import { RequestEntry } from '../../data/request-entry.model'; -import { RestResponse } from '../../cache/response.models'; +import { RequestEntry } from '../data/request-entry.model'; +import { RestResponse } from '../cache/response.models'; import { of as observableOf } from 'rxjs'; -import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; -import { RemoteData } from '../../data/remote-data'; -import { RequestEntryState } from '../../data/request-entry-state.model'; +import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; +import { RemoteData } from '../data/remote-data'; +import { RequestEntryState } from '../data/request-entry-state.model'; import { SuggestionSource } from './models/suggestion-source.model'; import { SuggestionTarget } from './models/suggestion-target.model'; import { SuggestionSourceDataService } from './source/suggestion-source-data.service'; import { SuggestionTargetDataService } from './target/suggestion-target-data.service'; -import { RequestParam } from '../../cache/models/request-param.model'; +import { RequestParam } from '../cache/models/request-param.model'; describe('SuggestionDataService test', () => { let scheduler: TestScheduler; diff --git a/src/app/core/suggestion-notifications/reciter-suggestions/suggestions-data.service.ts b/src/app/core/suggestion-notifications/suggestions-data.service.ts similarity index 87% rename from src/app/core/suggestion-notifications/reciter-suggestions/suggestions-data.service.ts rename to src/app/core/suggestion-notifications/suggestions-data.service.ts index 58fb1650319..72476f6a156 100644 --- a/src/app/core/suggestion-notifications/reciter-suggestions/suggestions-data.service.ts +++ b/src/app/core/suggestion-notifications/suggestions-data.service.ts @@ -5,26 +5,26 @@ import { Store } from '@ngrx/store'; import { Observable } from 'rxjs'; -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 { 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 { 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 { 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 { SUGGESTION } from './models/suggestion-objects.resource-type'; -import { FollowLinkConfig } from '../../../shared/utils/follow-link-config.model'; -import { PaginatedList } from '../../data/paginated-list.model'; +import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; +import { PaginatedList } from '../data/paginated-list.model'; import { SuggestionSource } from './models/suggestion-source.model'; import { SuggestionTarget } from './models/suggestion-target.model'; import { Suggestion } from './models/suggestion.model'; -import { RequestParam } from '../../cache/models/request-param.model'; -import { NoContent } from '../../shared/NoContent.model'; -import {CoreState} from '../../core-state.model'; -import {FindListOptions} from '../../data/find-list-options.model'; +import { RequestParam } from '../cache/models/request-param.model'; +import { NoContent } from '../shared/NoContent.model'; +import {CoreState} from '../core-state.model'; +import {FindListOptions} from '../data/find-list-options.model'; import { SuggestionSourceDataService } from './source/suggestion-source-data.service'; import { SuggestionTargetDataService } from './target/suggestion-target-data.service'; diff --git a/src/app/core/suggestion-notifications/reciter-suggestions/target/suggestion-target-data.service.ts b/src/app/core/suggestion-notifications/target/suggestion-target-data.service.ts similarity index 83% rename from src/app/core/suggestion-notifications/reciter-suggestions/target/suggestion-target-data.service.ts rename to src/app/core/suggestion-notifications/target/suggestion-target-data.service.ts index 5a5f91a87b2..a2f1507b100 100644 --- a/src/app/core/suggestion-notifications/reciter-suggestions/target/suggestion-target-data.service.ts +++ b/src/app/core/suggestion-notifications/target/suggestion-target-data.service.ts @@ -1,25 +1,25 @@ import { Injectable } from '@angular/core'; -import { dataService } from '../../../data/base/data-service.decorator'; +import { dataService } from '../../data/base/data-service.decorator'; -import { IdentifiableDataService } from '../../../data/base/identifiable-data.service'; +import { IdentifiableDataService } from '../../data/base/identifiable-data.service'; import { SuggestionTarget } from '../models/suggestion-target.model'; -import { FindAllData, FindAllDataImpl } from '../../../data/base/find-all-data'; +import { FindAllData, FindAllDataImpl } from '../../data/base/find-all-data'; import { Store } from '@ngrx/store'; -import { RequestService } from '../../../data/request.service'; -import { RemoteDataBuildService } from '../../../cache/builders/remote-data-build.service'; -import { CoreState } from '../../../core-state.model'; -import { ObjectCacheService } from '../../../cache/object-cache.service'; -import { HALEndpointService } from '../../../shared/hal-endpoint.service'; -import { NotificationsService } from '../../../../shared/notifications/notifications.service'; +import { RequestService } from '../../data/request.service'; +import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service'; +import { CoreState } from '../../core-state.model'; +import { ObjectCacheService } from '../../cache/object-cache.service'; +import { HALEndpointService } from '../../shared/hal-endpoint.service'; +import { NotificationsService } from '../../../shared/notifications/notifications.service'; import { HttpClient } from '@angular/common/http'; -import { FindListOptions } from '../../../data/find-list-options.model'; -import { FollowLinkConfig } from '../../../../shared/utils/follow-link-config.model'; -import { PaginatedList } from '../../../data/paginated-list.model'; -import { RemoteData } from '../../../data/remote-data'; +import { FindListOptions } from '../../data/find-list-options.model'; +import { FollowLinkConfig } from '../../../shared/utils/follow-link-config.model'; +import { PaginatedList } from '../../data/paginated-list.model'; +import { RemoteData } from '../../data/remote-data'; import { Observable } from 'rxjs/internal/Observable'; -import { RequestParam } from '../../../cache/models/request-param.model'; -import { SearchData, SearchDataImpl } from '../../../data/base/search-data'; -import { DefaultChangeAnalyzer } from '../../../data/default-change-analyzer.service'; +import { RequestParam } from '../../cache/models/request-param.model'; +import { SearchData, SearchDataImpl } from '../../data/base/search-data'; +import { DefaultChangeAnalyzer } from '../../data/default-change-analyzer.service'; import { SUGGESTION_TARGET } from '../models/suggestion-target-object.resource-type'; @Injectable() diff --git a/src/app/core/suggestion-notifications/reciter-suggestions/target/suggestions-target-data.service.spec.ts b/src/app/core/suggestion-notifications/target/suggestions-target-data.service.spec.ts similarity index 77% rename from src/app/core/suggestion-notifications/reciter-suggestions/target/suggestions-target-data.service.spec.ts rename to src/app/core/suggestion-notifications/target/suggestions-target-data.service.spec.ts index ba5900b79fa..9207603a5ad 100644 --- a/src/app/core/suggestion-notifications/reciter-suggestions/target/suggestions-target-data.service.spec.ts +++ b/src/app/core/suggestion-notifications/target/suggestions-target-data.service.spec.ts @@ -1,30 +1,30 @@ import { TestScheduler } from 'rxjs/testing'; -import { RequestService } from '../../../data/request.service'; -import { RemoteDataBuildService } from '../../../cache/builders/remote-data-build.service'; -import { ObjectCacheService } from '../../../cache/object-cache.service'; -import { HALEndpointService } from '../../../shared/hal-endpoint.service'; -import { RequestEntry } from '../../../data/request-entry.model'; +import { RequestService } from '../../data/request.service'; +import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service'; +import { ObjectCacheService } from '../../cache/object-cache.service'; +import { HALEndpointService } from '../../shared/hal-endpoint.service'; +import { RequestEntry } from '../../data/request-entry.model'; import { cold, getTestScheduler } from 'jasmine-marbles'; -import { RestResponse } from '../../../cache/response.models'; +import { RestResponse } from '../../cache/response.models'; import { of as observableOf } from 'rxjs'; import { Store } from '@ngrx/store'; -import { CoreState } from '../../../core-state.model'; +import { CoreState } from '../../core-state.model'; import { HttpClient } from '@angular/common/http'; -import { NotificationsService } from '../../../../shared/notifications/notifications.service'; -import { SearchData } from '../../../data/base/search-data'; -import { testSearchDataImplementation } from '../../../data/base/search-data.spec'; +import { NotificationsService } from '../../../shared/notifications/notifications.service'; +import { SearchData } from '../../data/base/search-data'; +import { testSearchDataImplementation } from '../../data/base/search-data.spec'; import { SuggestionTargetDataService } from './suggestion-target-data.service'; -import { DefaultChangeAnalyzer } from '../../../data/default-change-analyzer.service'; +import { DefaultChangeAnalyzer } from '../../data/default-change-analyzer.service'; import { SuggestionTarget } from '../models/suggestion-target.model'; -import { testFindAllDataImplementation } from '../../../data/base/find-all-data.spec'; -import { FindAllData } from '../../../data/base/find-all-data'; -import { GetRequest } from '../../../data/request.models'; +import { testFindAllDataImplementation } from '../../data/base/find-all-data.spec'; +import { FindAllData } from '../../data/base/find-all-data'; +import { GetRequest } from '../../data/request.models'; import { createSuccessfulRemoteDataObject$ -} from '../../../../shared/remote-data.utils'; -import { RequestParam } from '../../../cache/models/request-param.model'; -import { RemoteData } from '../../../data/remote-data'; -import { RequestEntryState } from '../../../data/request-entry-state.model'; +} from '../../../shared/remote-data.utils'; +import { RequestParam } from '../../cache/models/request-param.model'; +import { RemoteData } from '../../data/remote-data'; +import { RequestEntryState } from '../../data/request-entry-state.model'; describe('SuggestionTargetDataService test', () => { let scheduler: TestScheduler; diff --git a/src/app/menu.resolver.ts b/src/app/menu.resolver.ts index b5d8608d061..fc6eb00195c 100644 --- a/src/app/menu.resolver.ts +++ b/src/app/menu.resolver.ts @@ -561,13 +561,13 @@ export class MenuResolver implements Resolve { } as LinkMenuItemModel, }, { - id: 'notifications_reciter', + id: 'notifications_publication-claim', parentID: 'notifications', active: false, visible: authorized, model: { type: MenuItemType.LINK, - text: 'menu.section.notifications_reciter', + text: 'menu.section.notifications_publication-claim', link: '/admin/notifications/' + PUBLICATION_CLAIMS_PATH } as LinkMenuItemModel, }, diff --git a/src/app/notifications/notifications-effects.ts b/src/app/notifications/notifications-effects.ts index 9f19058aab4..a73fd04fc0e 100644 --- a/src/app/notifications/notifications-effects.ts +++ b/src/app/notifications/notifications-effects.ts @@ -1,6 +1,6 @@ import { QualityAssuranceSourceEffects } from './qa/source/quality-assurance-source.effects'; import { QualityAssuranceTopicsEffects } from './qa/topics/quality-assurance-topics.effects'; -import { SuggestionTargetsEffects } from '../suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.effects'; +import { SuggestionTargetsEffects } from '../suggestion-notifications/suggestion-targets/suggestion-targets.effects'; export const notificationsEffects = [ QualityAssuranceTopicsEffects, diff --git a/src/app/notifications/notifications.module.ts b/src/app/notifications/notifications.module.ts index 98832b86b78..cd8cb99811f 100644 --- a/src/app/notifications/notifications.module.ts +++ b/src/app/notifications/notifications.module.ts @@ -26,29 +26,29 @@ import { QualityAssuranceSourceService } from './qa/source/quality-assurance-sou import { QualityAssuranceSourceDataService } from '../core/notifications/qa/source/quality-assurance-source-data.service'; -import { SuggestionTargetsComponent } from '../suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.component'; -import { SuggestionActionsComponent } from '../suggestion-notifications/reciter-suggestions/suggestion-actions/suggestion-actions.component'; +import { SuggestionTargetsComponent } from '../suggestion-notifications/suggestion-targets/suggestion-targets.component'; +import { SuggestionActionsComponent } from '../suggestion-notifications/suggestion-actions/suggestion-actions.component'; import { SuggestionListElementComponent -} from '../suggestion-notifications/reciter-suggestions/suggestion-list-element/suggestion-list-element.component'; +} from '../suggestion-notifications/suggestion-list-element/suggestion-list-element.component'; import { SuggestionEvidencesComponent -} from '../suggestion-notifications/reciter-suggestions/suggestion-list-element/suggestion-evidences/suggestion-evidences.component'; -import { SuggestionsPopupComponent } from '../suggestion-notifications/reciter-suggestions/suggestions-popup/suggestions-popup.component'; +} from '../suggestion-notifications/suggestion-list-element/suggestion-evidences/suggestion-evidences.component'; +import { SuggestionsPopupComponent } from '../suggestion-notifications/suggestions-popup/suggestions-popup.component'; import { SuggestionsNotificationComponent -} from '../suggestion-notifications/reciter-suggestions/suggestions-notification/suggestions-notification.component'; -import { SuggestionsService } from '../suggestion-notifications/reciter-suggestions/suggestions.service'; -import { SuggestionsDataService } from '../core/suggestion-notifications/reciter-suggestions/suggestions-data.service'; +} from '../suggestion-notifications/suggestions-notification/suggestions-notification.component'; +import { SuggestionsService } from '../suggestion-notifications/suggestions.service'; +import { SuggestionsDataService } from '../core/suggestion-notifications/suggestions-data.service'; import { SuggestionSourceDataService -} from '../core/suggestion-notifications/reciter-suggestions/source/suggestion-source-data.service'; +} from '../core/suggestion-notifications/source/suggestion-source-data.service'; import { SuggestionTargetDataService -} from '../core/suggestion-notifications/reciter-suggestions/target/suggestion-target-data.service'; +} from '../core/suggestion-notifications/target/suggestion-target-data.service'; import { SuggestionTargetsStateService -} from '../suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.state.service'; +} from '../suggestion-notifications/suggestion-targets/suggestion-targets.state.service'; const MODULES = [ diff --git a/src/app/notifications/notifications.reducer.ts b/src/app/notifications/notifications.reducer.ts index ba7fcc6c587..cced6755fa9 100644 --- a/src/app/notifications/notifications.reducer.ts +++ b/src/app/notifications/notifications.reducer.ts @@ -1,7 +1,7 @@ import { ActionReducerMap, createFeatureSelector } from '@ngrx/store'; import { qualityAssuranceSourceReducer, QualityAssuranceSourceState } from './qa/source/quality-assurance-source.reducer'; import { qualityAssuranceTopicsReducer, QualityAssuranceTopicState, } from './qa/topics/quality-assurance-topics.reducer'; -import { SuggestionTargetsReducer, SuggestionTargetState } from '../suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.reducer'; +import { SuggestionTargetsReducer, SuggestionTargetState } from '../suggestion-notifications/suggestion-targets/suggestion-targets.reducer'; /** * The OpenAIRE State diff --git a/src/app/shared/mocks/reciter-suggestion-targets.mock.ts b/src/app/shared/mocks/publication-claim-targets.mock.ts similarity index 96% rename from src/app/shared/mocks/reciter-suggestion-targets.mock.ts rename to src/app/shared/mocks/publication-claim-targets.mock.ts index 489bb7733d8..1d7688c1e3d 100644 --- a/src/app/shared/mocks/reciter-suggestion-targets.mock.ts +++ b/src/app/shared/mocks/publication-claim-targets.mock.ts @@ -1,5 +1,5 @@ import { ResourceType } from '../../core/shared/resource-type'; -import { SuggestionTarget } from '../../core/suggestion-notifications/reciter-suggestions/models/suggestion-target.model'; +import { SuggestionTarget } from '../../core/suggestion-notifications/models/suggestion-target.model'; // REST Mock --------------------------------------------------------------------- // ------------------------------------------------------------------------------- diff --git a/src/app/shared/mocks/reciter-suggestion.mock.ts b/src/app/shared/mocks/publication-claim.mock.ts similarity index 98% rename from src/app/shared/mocks/reciter-suggestion.mock.ts rename to src/app/shared/mocks/publication-claim.mock.ts index b2dd0e7b128..143b54d8dd7 100644 --- a/src/app/shared/mocks/reciter-suggestion.mock.ts +++ b/src/app/shared/mocks/publication-claim.mock.ts @@ -2,8 +2,8 @@ // REST Mock --------------------------------------------------------------------- // ------------------------------------------------------------------------------- -import { Suggestion } from '../../core/suggestion-notifications/reciter-suggestions/models/suggestion.model'; -import { SUGGESTION } from '../../core/suggestion-notifications/reciter-suggestions/models/suggestion-objects.resource-type'; +import { Suggestion } from '../../core/suggestion-notifications/models/suggestion.model'; +import { SUGGESTION } from '../../core/suggestion-notifications/models/suggestion-objects.resource-type'; export const mockSuggestionPublicationOne: Suggestion = { id: '24694773', diff --git a/src/app/shared/mocks/suggestion.mock.ts b/src/app/shared/mocks/suggestion.mock.ts index d4ada21bf8d..ed7f9045d56 100644 --- a/src/app/shared/mocks/suggestion.mock.ts +++ b/src/app/shared/mocks/suggestion.mock.ts @@ -1349,8 +1349,8 @@ export function getMockSuggestionsService(): any { deleteReviewedSuggestion: jasmine.createSpy('deleteReviewedSuggestion'), retrieveCurrentUserSuggestions: jasmine.createSpy('retrieveCurrentUserSuggestions'), getTargetUuid: jasmine.createSpy('getTargetUuid'), - notMine: observableOf(null), - notMineMultiple: observableOf({success: 1, fails: 0}), + ignoreSuggestion: observableOf(null), + ignoreSuggestionMultiple: observableOf({success: 1, fails: 0}), approveAndImportMultiple: observableOf({success: 1, fails: 0}), approveAndImport: observableOf({id: '1234'}), isCollectionFixed: false, diff --git a/src/app/suggestion-notifications/reciter-suggestions/selectors.ts b/src/app/suggestion-notifications/selectors.ts similarity index 50% rename from src/app/suggestion-notifications/reciter-suggestions/selectors.ts rename to src/app/suggestion-notifications/selectors.ts index f031b71711f..97ed3a83602 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/selectors.ts +++ b/src/app/suggestion-notifications/selectors.ts @@ -1,44 +1,44 @@ import {createFeatureSelector, createSelector, MemoizedSelector} from '@ngrx/store'; -import { suggestionNotificationsSelector, SuggestionNotificationsState } from '../../notifications/notifications.reducer'; -import { SuggestionTarget } from '../../core/suggestion-notifications/reciter-suggestions/models/suggestion-target.model'; +import { suggestionNotificationsSelector, SuggestionNotificationsState } from '../notifications/notifications.reducer'; +import { SuggestionTarget } from '../core/suggestion-notifications/models/suggestion-target.model'; import { SuggestionTargetState } from './suggestion-targets/suggestion-targets.reducer'; -import {subStateSelector} from '../../submission/selectors'; +import {subStateSelector} from '../submission/selectors'; /** * Returns the Reciter Suggestion Target state. - * @function _getReciterSuggestionTargetState + * @function _getSuggestionTargetState * @param {AppState} state Top level state. * @return {SuggestionNotificationsState} */ -const _getReciterSuggestionTargetState = createFeatureSelector('suggestionNotifications'); +const _getSuggestionTargetState = createFeatureSelector('suggestionNotifications'); // Reciter Suggestion Targets // ---------------------------------------------------------------------------- /** - * Returns the Reciter Suggestion Targets State. - * @function reciterSuggestionTargetStateSelector + * Returns the Suggestion Targets State. + * @function suggestionTargetStateSelector * @return {SuggestionNotificationsState} */ -export function reciterSuggestionTargetStateSelector(): MemoizedSelector { +export function suggestionTargetStateSelector(): MemoizedSelector { return subStateSelector(suggestionNotificationsSelector, 'suggestionTarget'); } /** - * Returns the Reciter Suggestion Targets list. - * @function reciterSuggestionTargetObjectSelector + * Returns the Suggestion Targets list. + * @function suggestionTargetObjectSelector * @return {SuggestionTarget[]} */ -export function reciterSuggestionTargetObjectSelector(): MemoizedSelector { - return subStateSelector(reciterSuggestionTargetStateSelector(), 'targets'); +export function suggestionTargetObjectSelector(): MemoizedSelector { + return subStateSelector(suggestionTargetStateSelector(), 'targets'); } /** - * Returns true if the Reciter Suggestion Targets are loaded. - * @function isReciterSuggestionTargetLoadedSelector + * Returns true if the Suggestion Targets are loaded. + * @function isSuggestionTargetLoadedSelector * @return {boolean} */ -export const isReciterSuggestionTargetLoadedSelector = createSelector(_getReciterSuggestionTargetState, +export const isSuggestionTargetLoadedSelector = createSelector(_getSuggestionTargetState, (state: SuggestionNotificationsState) => state.suggestionTarget.loaded ); @@ -47,51 +47,51 @@ export const isReciterSuggestionTargetLoadedSelector = createSelector(_getRecite * @function isDeduplicationSetsProcessingSelector * @return {boolean} */ -export const isReciterSuggestionTargetProcessingSelector = createSelector(_getReciterSuggestionTargetState, +export const isReciterSuggestionTargetProcessingSelector = createSelector(_getSuggestionTargetState, (state: SuggestionNotificationsState) => state.suggestionTarget.processing ); /** * Returns the total available pages of Reciter Suggestion Targets. - * @function getreciterSuggestionTargetTotalPagesSelector + * @function getSuggestionTargetTotalPagesSelector * @return {number} */ -export const getReciterSuggestionTargetTotalPagesSelector = createSelector(_getReciterSuggestionTargetState, +export const getSuggestionTargetTotalPagesSelector = createSelector(_getSuggestionTargetState, (state: SuggestionNotificationsState) => state.suggestionTarget.totalPages ); /** - * Returns the current page of Reciter Suggestion Targets. - * @function getreciterSuggestionTargetCurrentPageSelector + * Returns the current page of Suggestion Targets. + * @function getSuggestionTargetCurrentPageSelector * @return {number} */ -export const getReciterSuggestionTargetCurrentPageSelector = createSelector(_getReciterSuggestionTargetState, +export const getSuggestionTargetCurrentPageSelector = createSelector(_getSuggestionTargetState, (state: SuggestionNotificationsState) => state.suggestionTarget.currentPage ); /** - * Returns the total number of Reciter Suggestion Targets. - * @function getreciterSuggestionTargetTotalsSelector + * Returns the total number of Suggestion Targets. + * @function getSuggestionTargetTotalsSelector * @return {number} */ -export const getReciterSuggestionTargetTotalsSelector = createSelector(_getReciterSuggestionTargetState, +export const getSuggestionTargetTotalsSelector = createSelector(_getSuggestionTargetState, (state: SuggestionNotificationsState) => state.suggestionTarget.totalElements ); /** * Returns Suggestion Targets for the current user. - * @function getCurrentUserReciterSuggestionTargetSelector + * @function getCurrentUserSuggestionTargetSelector * @return {SuggestionTarget[]} */ -export const getCurrentUserSuggestionTargetsSelector = createSelector(_getReciterSuggestionTargetState, +export const getCurrentUserSuggestionTargetsSelector = createSelector(_getSuggestionTargetState, (state: SuggestionNotificationsState) => state.suggestionTarget.currentUserTargets ); /** * Returns whether or not the user has consulted their suggestions - * @function getCurrentUserReciterSuggestionTargetSelector + * @function getCurrentUserSuggestionTargetSelector * @return {boolean} */ -export const getCurrentUserSuggestionTargetsVisitedSelector = createSelector(_getReciterSuggestionTargetState, +export const getCurrentUserSuggestionTargetsVisitedSelector = createSelector(_getSuggestionTargetState, (state: SuggestionNotificationsState) => state.suggestionTarget.currentUserTargetsVisited ); diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestion-actions/suggestion-actions.component.html b/src/app/suggestion-notifications/suggestion-actions/suggestion-actions.component.html similarity index 89% rename from src/app/suggestion-notifications/reciter-suggestions/suggestion-actions/suggestion-actions.component.html rename to src/app/suggestion-notifications/suggestion-actions/suggestion-actions.component.html index 4d9fdb8b8e2..24e970a8182 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/suggestion-actions/suggestion-actions.component.html +++ b/src/app/suggestion-notifications/suggestion-actions/suggestion-actions.component.html @@ -19,8 +19,8 @@
- +
diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestion-list-element/suggestion-list-element.component.scss b/src/app/suggestion-notifications/suggestion-list-element/suggestion-list-element.component.scss similarity index 100% rename from src/app/suggestion-notifications/reciter-suggestions/suggestion-list-element/suggestion-list-element.component.scss rename to src/app/suggestion-notifications/suggestion-list-element/suggestion-list-element.component.scss diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestion-list-element/suggestion-list-element.component.spec.ts b/src/app/suggestion-notifications/suggestion-list-element/suggestion-list-element.component.spec.ts similarity index 88% rename from src/app/suggestion-notifications/reciter-suggestions/suggestion-list-element/suggestion-list-element.component.spec.ts rename to src/app/suggestion-notifications/suggestion-list-element/suggestion-list-element.component.spec.ts index 544b53774e7..61d1a19996c 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/suggestion-list-element/suggestion-list-element.component.spec.ts +++ b/src/app/suggestion-notifications/suggestion-list-element/suggestion-list-element.component.spec.ts @@ -5,8 +5,8 @@ import { NO_ERRORS_SCHEMA } from '@angular/core'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { TestScheduler } from 'rxjs/testing'; import { getTestScheduler } from 'jasmine-marbles'; -import { mockSuggestionPublicationOne } from '../../../shared/mocks/reciter-suggestion.mock'; -import { Item } from '../../../core/shared/item.model'; +import { mockSuggestionPublicationOne } from '../../shared/mocks/publication-claim.mock'; +import { Item } from '../../core/shared/item.model'; describe('SuggestionListElementComponent', () => { @@ -66,9 +66,9 @@ describe('SuggestionListElementComponent', () => { }); it('should emit for deletion', () => { - spyOn(component.notMineClicked, 'emit'); - component.onNotMine('1234'); - expect(component.notMineClicked.emit).toHaveBeenCalledWith('1234'); + spyOn(component.ignoreSuggestionClicked, 'emit'); + component.onIgnoreSuggestion('1234'); + expect(component.ignoreSuggestionClicked.emit).toHaveBeenCalledWith('1234'); }); it('should emit for approve and import', () => { diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestion-list-element/suggestion-list-element.component.ts b/src/app/suggestion-notifications/suggestion-list-element/suggestion-list-element.component.ts similarity index 77% rename from src/app/suggestion-notifications/reciter-suggestions/suggestion-list-element/suggestion-list-element.component.ts rename to src/app/suggestion-notifications/suggestion-list-element/suggestion-list-element.component.ts index a1ef454037b..f569054bb0d 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/suggestion-list-element/suggestion-list-element.component.ts +++ b/src/app/suggestion-notifications/suggestion-list-element/suggestion-list-element.component.ts @@ -2,16 +2,22 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; -import { fadeIn } from '../../../shared/animations/fade'; -import { Suggestion } from '../../../core/suggestion-notifications/reciter-suggestions/models/suggestion.model'; -import { Item } from '../../../core/shared/item.model'; -import { isNotEmpty } from '../../../shared/empty.util'; - +import { fadeIn } from '../../shared/animations/fade'; +import { Suggestion } from '../../core/suggestion-notifications/models/suggestion.model'; +import { Item } from '../../core/shared/item.model'; +import { isNotEmpty } from '../../shared/empty.util'; + +/** + * A simple interface to unite a specific suggestion and the id of the chosen collection + */ export interface SuggestionApproveAndImport { suggestion: Suggestion; collectionId: string; } +/** + * Show all the suggestions by researcher + */ @Component({ selector: 'ds-suggestion-list-item', styleUrls: ['./suggestion-list-element.component.scss'], @@ -33,7 +39,7 @@ export class SuggestionListElementComponent implements OnInit { /** * The component is used to Delete suggestion */ - @Output() notMineClicked = new EventEmitter(); + @Output() ignoreSuggestionClicked = new EventEmitter(); /** * The component is used to approve & import @@ -69,8 +75,8 @@ export class SuggestionListElementComponent implements OnInit { /** * Delete the suggestion */ - onNotMine(suggestionId: string) { - this.notMineClicked.emit(suggestionId); + onIgnoreSuggestion(suggestionId: string) { + this.ignoreSuggestionClicked.emit(suggestionId); } /** diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.actions.ts b/src/app/suggestion-notifications/suggestion-targets/suggestion-targets.actions.ts similarity index 96% rename from src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.actions.ts rename to src/app/suggestion-notifications/suggestion-targets/suggestion-targets.actions.ts index 4e5b88d65c4..058fa367518 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.actions.ts +++ b/src/app/suggestion-notifications/suggestion-targets/suggestion-targets.actions.ts @@ -1,7 +1,7 @@ /* eslint-disable max-classes-per-file */ import { Action } from '@ngrx/store'; -import { type } from '../../../shared/ngrx/type'; -import { SuggestionTarget } from '../../../core/suggestion-notifications/reciter-suggestions/models/suggestion-target.model'; +import { type } from '../../shared/ngrx/type'; +import { SuggestionTarget } from '../../core/suggestion-notifications/models/suggestion-target.model'; /** * For each action type in an action group, make a simple diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.component.html b/src/app/suggestion-notifications/suggestion-targets/suggestion-targets.component.html similarity index 87% rename from src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.component.html rename to src/app/suggestion-notifications/suggestion-targets/suggestion-targets.component.html index 04e967c3d54..3d98082ba6e 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.component.html +++ b/src/app/suggestion-notifications/suggestion-targets/suggestion-targets.component.html @@ -33,8 +33,9 @@

{{'reciter.suggestion.title'| transla
diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.component.scss b/src/app/suggestion-notifications/suggestion-targets/suggestion-targets.component.scss similarity index 100% rename from src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.component.scss rename to src/app/suggestion-notifications/suggestion-targets/suggestion-targets.component.scss diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.component.ts b/src/app/suggestion-notifications/suggestion-targets/suggestion-targets.component.ts similarity index 80% rename from src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.component.ts rename to src/app/suggestion-notifications/suggestion-targets/suggestion-targets.component.ts index cfa168e2b83..e469ac13869 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.component.ts +++ b/src/app/suggestion-notifications/suggestion-targets/suggestion-targets.component.ts @@ -4,13 +4,13 @@ import { Router } from '@angular/router'; import { Observable, Subscription } from 'rxjs'; import { distinctUntilChanged, take } from 'rxjs/operators'; -import { SuggestionTarget } from '../../../core/suggestion-notifications/reciter-suggestions/models/suggestion-target.model'; -import { hasValue } from '../../../shared/empty.util'; -import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; +import { SuggestionTarget } from '../../core/suggestion-notifications/models/suggestion-target.model'; +import { hasValue } from '../../shared/empty.util'; +import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; import { SuggestionTargetsStateService } from './suggestion-targets.state.service'; -import { getSuggestionPageRoute } from '../../../suggestions-page/suggestions-page-routing-paths'; +import { getSuggestionPageRoute } from '../../suggestions-page/suggestions-page-routing-paths'; import { SuggestionsService } from '../suggestions.service'; -import { PaginationService } from '../../../core/pagination/pagination.service'; +import { PaginationService } from '../../core/pagination/pagination.service'; /** * Component to display the Suggestion Target list. @@ -69,8 +69,8 @@ export class SuggestionTargetsComponent implements OnInit { * Component initialization. */ ngOnInit(): void { - this.targets$ = this.suggestionTargetsStateService.getReciterSuggestionTargets(); - this.totalElements$ = this.suggestionTargetsStateService.getReciterSuggestionTargetsTotals(); + this.targets$ = this.suggestionTargetsStateService.getSuggestionTargets(); + this.totalElements$ = this.suggestionTargetsStateService.getSuggestionTargetsTotals(); } /** @@ -78,7 +78,7 @@ export class SuggestionTargetsComponent implements OnInit { */ ngAfterViewInit(): void { this.subs.push( - this.suggestionTargetsStateService.isReciterSuggestionTargetsLoaded().pipe( + this.suggestionTargetsStateService.isSuggestionTargetsLoaded().pipe( take(1) ).subscribe(() => { this.getSuggestionTargets(); @@ -93,7 +93,7 @@ export class SuggestionTargetsComponent implements OnInit { * 'true' if the targets are loading, 'false' otherwise. */ public isTargetsLoading(): Observable { - return this.suggestionTargetsStateService.isReciterSuggestionTargetsLoading(); + return this.suggestionTargetsStateService.isSuggestionTargetsLoading(); } /** @@ -103,7 +103,7 @@ export class SuggestionTargetsComponent implements OnInit { * 'true' if there are operations running on the targets (ex.: a REST call), 'false' otherwise. */ public isTargetsProcessing(): Observable { - return this.suggestionTargetsStateService.isReciterSuggestionTargetsProcessing(); + return this.suggestionTargetsStateService.isSuggestionTargetsProcessing(); } /** @@ -136,7 +136,7 @@ export class SuggestionTargetsComponent implements OnInit { distinctUntilChanged(), take(1) ).subscribe((options: PaginationComponentOptions) => { - this.suggestionTargetsStateService.dispatchRetrieveReciterSuggestionTargets( + this.suggestionTargetsStateService.dispatchRetrieveSuggestionTargets( this.source, options.pageSize, options.currentPage diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.effects.ts b/src/app/suggestion-notifications/suggestion-targets/suggestion-targets.effects.ts similarity index 91% rename from src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.effects.ts rename to src/app/suggestion-notifications/suggestion-targets/suggestion-targets.effects.ts index 850c82972e1..63964b77214 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.effects.ts +++ b/src/app/suggestion-notifications/suggestion-targets/suggestion-targets.effects.ts @@ -14,10 +14,10 @@ import { RetrieveTargetsBySourceAction, SuggestionTargetActionTypes, } from './suggestion-targets.actions'; -import { PaginatedList } from '../../../core/data/paginated-list.model'; +import { PaginatedList } from '../../core/data/paginated-list.model'; import { SuggestionsService } from '../suggestions.service'; -import { NotificationsService } from '../../../shared/notifications/notifications.service'; -import { SuggestionTarget } from '../../../core/suggestion-notifications/reciter-suggestions/models/suggestion-target.model'; +import { NotificationsService } from '../../shared/notifications/notifications.service'; +import { SuggestionTarget } from '../../core/suggestion-notifications/models/suggestion-target.model'; /** * Provides effect methods for the Suggestion Targets actions. diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.reducer.ts b/src/app/suggestion-notifications/suggestion-targets/suggestion-targets.reducer.ts similarity index 95% rename from src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.reducer.ts rename to src/app/suggestion-notifications/suggestion-targets/suggestion-targets.reducer.ts index 2d315e69956..09ead019d11 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.reducer.ts +++ b/src/app/suggestion-notifications/suggestion-targets/suggestion-targets.reducer.ts @@ -1,5 +1,5 @@ import { SuggestionTargetActionTypes, SuggestionTargetsActions } from './suggestion-targets.actions'; -import { SuggestionTarget } from '../../../core/suggestion-notifications/reciter-suggestions/models/suggestion-target.model'; +import { SuggestionTarget } from '../../core/suggestion-notifications/models/suggestion-target.model'; /** * The interface representing the OpenAIRE suggestion targets state. diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.state.service.ts b/src/app/suggestion-notifications/suggestion-targets/suggestion-targets.state.service.ts similarity index 54% rename from src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.state.service.ts rename to src/app/suggestion-notifications/suggestion-targets/suggestion-targets.state.service.ts index da347a1e8b5..7e0e6ad8368 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.state.service.ts +++ b/src/app/suggestion-notifications/suggestion-targets/suggestion-targets.state.service.ts @@ -7,20 +7,20 @@ import { map } from 'rxjs/operators'; import { getCurrentUserSuggestionTargetsSelector, getCurrentUserSuggestionTargetsVisitedSelector, - getReciterSuggestionTargetCurrentPageSelector, - getReciterSuggestionTargetTotalsSelector, - isReciterSuggestionTargetLoadedSelector, + getSuggestionTargetCurrentPageSelector, + getSuggestionTargetTotalsSelector, + isSuggestionTargetLoadedSelector, isReciterSuggestionTargetProcessingSelector, - reciterSuggestionTargetObjectSelector + suggestionTargetObjectSelector } from '../selectors'; -import { SuggestionTarget } from '../../../core/suggestion-notifications/reciter-suggestions/models/suggestion-target.model'; +import { SuggestionTarget } from '../../core/suggestion-notifications/models/suggestion-target.model'; import { ClearSuggestionTargetsAction, MarkUserSuggestionsAsVisitedAction, RefreshUserSuggestionsAction, RetrieveTargetsBySourceAction } from './suggestion-targets.actions'; -import { SuggestionNotificationsState } from '../../../notifications/notifications.reducer'; +import { SuggestionNotificationsState } from '../../notifications/notifications.reducer'; /** * The service handling the Suggestion targets State. @@ -35,80 +35,80 @@ export class SuggestionTargetsStateService { constructor(private store: Store) { } /** - * Returns the list of Reciter Suggestion Targets from the state. + * Returns the list of Suggestion Targets from the state. * - * @return Observable - * The list of Reciter Suggestion Targets. + * @return Observable + * The list of Suggestion Targets. */ - public getReciterSuggestionTargets(): Observable { - return this.store.pipe(select(reciterSuggestionTargetObjectSelector())); + public getSuggestionTargets(): Observable { + return this.store.pipe(select(suggestionTargetObjectSelector())); } /** - * Returns the information about the loading status of the Reciter Suggestion Targets (if it's running or not). + * Returns the information about the loading status of the Suggestion Targets (if it's running or not). * * @return Observable * 'true' if the targets are loading, 'false' otherwise. */ - public isReciterSuggestionTargetsLoading(): Observable { + public isSuggestionTargetsLoading(): Observable { return this.store.pipe( - select(isReciterSuggestionTargetLoadedSelector), + select(isSuggestionTargetLoadedSelector), map((loaded: boolean) => !loaded) ); } /** - * Returns the information about the loading status of the Reciter Suggestion Targets (whether or not they were loaded). + * Returns the information about the loading status of the Suggestion Targets (whether or not they were loaded). * * @return Observable * 'true' if the targets are loaded, 'false' otherwise. */ - public isReciterSuggestionTargetsLoaded(): Observable { - return this.store.pipe(select(isReciterSuggestionTargetLoadedSelector)); + public isSuggestionTargetsLoaded(): Observable { + return this.store.pipe(select(isSuggestionTargetLoadedSelector)); } /** - * Returns the information about the processing status of the Reciter Suggestion Targets (if it's running or not). + * Returns the information about the processing status of the Suggestion Targets (if it's running or not). * * @return Observable * 'true' if there are operations running on the targets (ex.: a REST call), 'false' otherwise. */ - public isReciterSuggestionTargetsProcessing(): Observable { + public isSuggestionTargetsProcessing(): Observable { return this.store.pipe(select(isReciterSuggestionTargetProcessingSelector)); } /** - * Returns, from the state, the total available pages of the Reciter Suggestion Targets. + * Returns, from the state, the total available pages of the Suggestion Targets. * * @return Observable - * The number of the Reciter Suggestion Targets pages. + * The number of the Suggestion Targets pages. */ - public getReciterSuggestionTargetsTotalPages(): Observable { - return this.store.pipe(select(getReciterSuggestionTargetTotalsSelector)); + public getSuggestionTargetsTotalPages(): Observable { + return this.store.pipe(select(getSuggestionTargetTotalsSelector)); } /** - * Returns the current page of the Reciter Suggestion Targets, from the state. + * Returns the current page of the Suggestion Targets, from the state. * * @return Observable - * The number of the current Reciter Suggestion Targets page. + * The number of the current Suggestion Targets page. */ - public getReciterSuggestionTargetsCurrentPage(): Observable { - return this.store.pipe(select(getReciterSuggestionTargetCurrentPageSelector)); + public getSuggestionTargetsCurrentPage(): Observable { + return this.store.pipe(select(getSuggestionTargetCurrentPageSelector)); } /** - * Returns the total number of the Reciter Suggestion Targets. + * Returns the total number of the Suggestion Targets. * * @return Observable - * The number of the Reciter Suggestion Targets. + * The number of the Suggestion Targets. */ - public getReciterSuggestionTargetsTotals(): Observable { - return this.store.pipe(select(getReciterSuggestionTargetTotalsSelector)); + public getSuggestionTargetsTotals(): Observable { + return this.store.pipe(select(getSuggestionTargetTotalsSelector)); } /** - * Dispatch a request to change the Reciter Suggestion Targets state, retrieving the targets from the server. + * Dispatch a request to change the Suggestion Targets state, retrieving the targets from the server. * * @param source * the source for which to retrieve suggestion targets @@ -117,15 +117,15 @@ export class SuggestionTargetsStateService { * @param currentPage * The number of the current page. */ - public dispatchRetrieveReciterSuggestionTargets(source: string, elementsPerPage: number, currentPage: number): void { + public dispatchRetrieveSuggestionTargets(source: string, elementsPerPage: number, currentPage: number): void { this.store.dispatch(new RetrieveTargetsBySourceAction(source, elementsPerPage, currentPage)); } /** - * Returns, from the state, the reciter suggestion targets for the current user. + * Returns, from the state, the suggestion targets for the current user. * * @return Observable - * The Reciter Suggestion Targets object. + * The Suggestion Targets object. */ public getCurrentUserSuggestionTargets(): Observable { return this.store.pipe(select(getCurrentUserSuggestionTargetsSelector)); diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestion.service.spec.ts b/src/app/suggestion-notifications/suggestion.service.spec.ts similarity index 83% rename from src/app/suggestion-notifications/reciter-suggestions/suggestion.service.spec.ts rename to src/app/suggestion-notifications/suggestion.service.spec.ts index 6abed7a1ce1..c7d0c7a4bf4 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/suggestion.service.spec.ts +++ b/src/app/suggestion-notifications/suggestion.service.spec.ts @@ -1,30 +1,30 @@ import { SuggestionsService } from './suggestions.service'; -import { AuthService } from '../../core/auth/auth.service'; -import { ResearcherProfileDataService } from '../../core/profile/researcher-profile-data.service'; +import { AuthService } from '../core/auth/auth.service'; +import { ResearcherProfileDataService } from '../core/profile/researcher-profile-data.service'; import { SuggestionsDataService -} from '../../core/suggestion-notifications/reciter-suggestions/suggestions-data.service'; +} from '../core/suggestion-notifications/suggestions-data.service'; import { SuggestionSourceDataService -} from '../../core/suggestion-notifications/reciter-suggestions/source/suggestion-source-data.service'; +} from '../core/suggestion-notifications/source/suggestion-source-data.service'; import { SuggestionTargetDataService -} from '../../core/suggestion-notifications/reciter-suggestions/target/suggestion-target-data.service'; +} from '../core/suggestion-notifications/target/suggestion-target-data.service'; import { TestScheduler } from 'rxjs/testing'; import { getTestScheduler } from 'jasmine-marbles'; import { of as observableOf } from 'rxjs'; -import { FindListOptions } from '../../core/data/find-list-options.model'; -import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; -import { ResearcherProfile } from '../../core/profile/model/researcher-profile.model'; -import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; -import { WorkspaceitemDataService } from '../../core/submission/workspaceitem-data.service'; -import { mockSuggestionPublicationOne } from '../../shared/mocks/reciter-suggestion.mock'; -import { ResourceType } from '../../core/shared/resource-type'; +import { FindListOptions } from '../core/data/find-list-options.model'; +import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model'; +import { ResearcherProfile } from '../core/profile/model/researcher-profile.model'; +import { createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils'; +import { WorkspaceitemDataService } from '../core/submission/workspaceitem-data.service'; +import { mockSuggestionPublicationOne } from '../shared/mocks/publication-claim.mock'; +import { ResourceType } from '../core/shared/resource-type'; import { SuggestionTarget -} from '../../core/suggestion-notifications/reciter-suggestions/models/suggestion-target.model'; +} from '../core/suggestion-notifications/models/suggestion-target.model'; describe('SuggestionsService test', () => { let scheduler: TestScheduler; @@ -151,14 +151,14 @@ describe('SuggestionsService test', () => { it('should delete suggestion', () => { spyOn(service, 'deleteReviewedSuggestion').and.returnValue(createSuccessfulRemoteDataObject$({})); - service.notMine('1234'); + service.ignoreSuggestion('1234'); expect(service.deleteReviewedSuggestion).toHaveBeenCalledWith('1234'); }); it('should delete suggestions', () => { spyOn(service, 'notMine'); - service.notMineMultiple([mockSuggestionPublicationOne]); - expect(service.notMine).toHaveBeenCalledWith(mockSuggestionPublicationOne.id); + service.ignoreSuggestionMultiple([mockSuggestionPublicationOne]); + expect(service.ignoreSuggestion).toHaveBeenCalledWith(mockSuggestionPublicationOne.id); }); it('should get target Uuid', () => { diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestions-notification/suggestions-notification.component.html b/src/app/suggestion-notifications/suggestions-notification/suggestions-notification.component.html similarity index 100% rename from src/app/suggestion-notifications/reciter-suggestions/suggestions-notification/suggestions-notification.component.html rename to src/app/suggestion-notifications/suggestions-notification/suggestions-notification.component.html diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestions-notification/suggestions-notification.component.scss b/src/app/suggestion-notifications/suggestions-notification/suggestions-notification.component.scss similarity index 100% rename from src/app/suggestion-notifications/reciter-suggestions/suggestions-notification/suggestions-notification.component.scss rename to src/app/suggestion-notifications/suggestions-notification/suggestions-notification.component.scss diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestions-notification/suggestions-notification.component.ts b/src/app/suggestion-notifications/suggestions-notification/suggestions-notification.component.ts similarity index 75% rename from src/app/suggestion-notifications/reciter-suggestions/suggestions-notification/suggestions-notification.component.ts rename to src/app/suggestion-notifications/suggestions-notification/suggestions-notification.component.ts index 23b34c4de99..dfa1e187e3b 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/suggestions-notification/suggestions-notification.component.ts +++ b/src/app/suggestion-notifications/suggestions-notification/suggestions-notification.component.ts @@ -1,11 +1,14 @@ import { Component, OnInit } from '@angular/core'; -import { SuggestionTarget } from '../../../core/suggestion-notifications/reciter-suggestions/models/suggestion-target.model'; +import { SuggestionTarget } from '../../core/suggestion-notifications/models/suggestion-target.model'; import { TranslateService } from '@ngx-translate/core'; import { SuggestionTargetsStateService } from '../suggestion-targets/suggestion-targets.state.service'; -import { NotificationsService } from '../../../shared/notifications/notifications.service'; +import { NotificationsService } from '../../shared/notifications/notifications.service'; import { SuggestionsService } from '../suggestions.service'; import { Observable } from 'rxjs'; +/** + * Show suggestions notification, used on myDSpace and Profile pages + */ @Component({ selector: 'ds-suggestions-notification', templateUrl: './suggestions-notification.component.html', @@ -22,13 +25,13 @@ export class SuggestionsNotificationComponent implements OnInit { constructor( private translateService: TranslateService, - private reciterSuggestionStateService: SuggestionTargetsStateService, + private suggestionTargetsStateService: SuggestionTargetsStateService, private notificationsService: NotificationsService, private suggestionsService: SuggestionsService ) { } ngOnInit() { - this.suggestionsRD$ = this.reciterSuggestionStateService.getCurrentUserSuggestionTargets(); + this.suggestionsRD$ = this.suggestionTargetsStateService.getCurrentUserSuggestionTargets(); } /** diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestions-popup/suggestions-popup.component.html b/src/app/suggestion-notifications/suggestions-popup/suggestions-popup.component.html similarity index 100% rename from src/app/suggestion-notifications/reciter-suggestions/suggestions-popup/suggestions-popup.component.html rename to src/app/suggestion-notifications/suggestions-popup/suggestions-popup.component.html diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestions-popup/suggestions-popup.component.scss b/src/app/suggestion-notifications/suggestions-popup/suggestions-popup.component.scss similarity index 100% rename from src/app/suggestion-notifications/reciter-suggestions/suggestions-popup/suggestions-popup.component.scss rename to src/app/suggestion-notifications/suggestions-popup/suggestions-popup.component.scss diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestions-popup/suggestions-popup.component.spec.ts b/src/app/suggestion-notifications/suggestions-popup/suggestions-popup.component.spec.ts similarity index 92% rename from src/app/suggestion-notifications/reciter-suggestions/suggestions-popup/suggestions-popup.component.spec.ts rename to src/app/suggestion-notifications/suggestions-popup/suggestions-popup.component.spec.ts index aade2c78fa1..8a5721968bc 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/suggestions-popup/suggestions-popup.component.spec.ts +++ b/src/app/suggestion-notifications/suggestions-popup/suggestions-popup.component.spec.ts @@ -3,11 +3,11 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { SuggestionsPopupComponent } from './suggestions-popup.component'; import { TranslateModule } from '@ngx-translate/core'; import { SuggestionTargetsStateService } from '../suggestion-targets/suggestion-targets.state.service'; -import { NotificationsService } from '../../../shared/notifications/notifications.service'; -import { NotificationsServiceStub } from '../../../shared/testing/notifications-service.stub'; +import { NotificationsService } from '../../shared/notifications/notifications.service'; +import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { of as observableOf } from 'rxjs'; -import { mockSuggestionTargetsObjectOne } from '../../../shared/mocks/reciter-suggestion-targets.mock'; +import { mockSuggestionTargetsObjectOne } from '../../shared/mocks/publication-claim-targets.mock'; import { SuggestionsService } from '../suggestions.service'; describe('SuggestionsPopupComponent', () => { diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestions-popup/suggestions-popup.component.ts b/src/app/suggestion-notifications/suggestions-popup/suggestions-popup.component.ts similarity index 75% rename from src/app/suggestion-notifications/reciter-suggestions/suggestions-popup/suggestions-popup.component.ts rename to src/app/suggestion-notifications/suggestions-popup/suggestions-popup.component.ts index 39be112bd28..2eb5cec6dae 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/suggestions-popup/suggestions-popup.component.ts +++ b/src/app/suggestion-notifications/suggestions-popup/suggestions-popup.component.ts @@ -1,13 +1,16 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { SuggestionTargetsStateService } from '../suggestion-targets/suggestion-targets.state.service'; -import { NotificationsService } from '../../../shared/notifications/notifications.service'; +import { NotificationsService } from '../../shared/notifications/notifications.service'; import { SuggestionsService } from '../suggestions.service'; import { takeUntil } from 'rxjs/operators'; -import { SuggestionTarget } from '../../../core/suggestion-notifications/reciter-suggestions/models/suggestion-target.model'; -import { isNotEmpty } from '../../../shared/empty.util'; +import { SuggestionTarget } from '../../core/suggestion-notifications/models/suggestion-target.model'; +import { isNotEmpty } from '../../shared/empty.util'; import { combineLatest, Subject } from 'rxjs'; +/** + * Show suggestions on a popover window, used on the homepage + */ @Component({ selector: 'ds-suggestions-popup', templateUrl: './suggestions-popup.component.html', @@ -21,7 +24,7 @@ export class SuggestionsPopupComponent implements OnInit, OnDestroy { constructor( private translateService: TranslateService, - private reciterSuggestionStateService: SuggestionTargetsStateService, + private suggestionTargetsStateService: SuggestionTargetsStateService, private notificationsService: NotificationsService, private suggestionsService: SuggestionsService ) { } @@ -33,14 +36,14 @@ export class SuggestionsPopupComponent implements OnInit, OnDestroy { public initializePopup() { const notifier = new Subject(); this.subscription = combineLatest([ - this.reciterSuggestionStateService.getCurrentUserSuggestionTargets(), - this.reciterSuggestionStateService.hasUserVisitedSuggestions() + this.suggestionTargetsStateService.getCurrentUserSuggestionTargets(), + this.suggestionTargetsStateService.hasUserVisitedSuggestions() ]).pipe(takeUntil(notifier)).subscribe(([suggestions, visited]) => { - this.reciterSuggestionStateService.dispatchRefreshUserSuggestionsAction(); + this.suggestionTargetsStateService.dispatchRefreshUserSuggestionsAction(); if (isNotEmpty(suggestions)) { if (!visited) { suggestions.forEach((suggestionTarget: SuggestionTarget) => this.showNotificationForNewSuggestions(suggestionTarget)); - this.reciterSuggestionStateService.dispatchMarkUserSuggestionsAsVisitedAction(); + this.suggestionTargetsStateService.dispatchMarkUserSuggestionsAsVisitedAction(); notifier.next(null); notifier.complete(); } diff --git a/src/app/suggestion-notifications/reciter-suggestions/suggestions.service.ts b/src/app/suggestion-notifications/suggestions.service.ts similarity index 84% rename from src/app/suggestion-notifications/reciter-suggestions/suggestions.service.ts rename to src/app/suggestion-notifications/suggestions.service.ts index b760ddf34db..d4e8825f63e 100644 --- a/src/app/suggestion-notifications/reciter-suggestions/suggestions.service.ts +++ b/src/app/suggestion-notifications/suggestions.service.ts @@ -3,38 +3,41 @@ import { Injectable } from '@angular/core'; import { of, forkJoin, Observable } from 'rxjs'; import { catchError, map, mergeMap, take } from 'rxjs/operators'; -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 { SuggestionTarget } from '../../core/suggestion-notifications/reciter-suggestions/models/suggestion-target.model'; -import { AuthService } from '../../core/auth/auth.service'; -import { hasValue, isNotEmpty } from '../../shared/empty.util'; -import { ResearcherProfile } from '../../core/profile/model/researcher-profile.model'; +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 { SuggestionTarget } from '../core/suggestion-notifications/models/suggestion-target.model'; +import { AuthService } from '../core/auth/auth.service'; +import { hasValue, isNotEmpty } from '../shared/empty.util'; +import { ResearcherProfile } from '../core/profile/model/researcher-profile.model'; import { getAllSucceededRemoteDataPayload, getFinishedRemoteData, getFirstSucceededRemoteDataPayload, getFirstSucceededRemoteListPayload -} from '../../core/shared/operators'; -import { Suggestion } from '../../core/suggestion-notifications/reciter-suggestions/models/suggestion.model'; -import { WorkspaceitemDataService } from '../../core/submission/workspaceitem-data.service'; +} from '../core/shared/operators'; +import { Suggestion } from '../core/suggestion-notifications/models/suggestion.model'; +import { WorkspaceitemDataService } from '../core/submission/workspaceitem-data.service'; import { TranslateService } from '@ngx-translate/core'; -import { NoContent } from '../../core/shared/NoContent.model'; -import { environment } from '../../../environments/environment'; -import { WorkspaceItem } from '../../core/submission/models/workspaceitem.model'; -import {FindListOptions} from '../../core/data/find-list-options.model'; -import {SuggestionConfig} from '../../../config/layout-config.interfaces'; -import { ResearcherProfileDataService } from '../../core/profile/researcher-profile-data.service'; +import { NoContent } from '../core/shared/NoContent.model'; +import { environment } from '../../environments/environment'; +import { WorkspaceItem } from '../core/submission/models/workspaceitem.model'; +import {FindListOptions} from '../core/data/find-list-options.model'; +import {SuggestionConfig} from '../../config/layout-config.interfaces'; +import { ResearcherProfileDataService } from '../core/profile/researcher-profile-data.service'; import { SuggestionSourceDataService -} from '../../core/suggestion-notifications/reciter-suggestions/source/suggestion-source-data.service'; +} from '../core/suggestion-notifications/source/suggestion-source-data.service'; import { SuggestionTargetDataService -} from '../../core/suggestion-notifications/reciter-suggestions/target/suggestion-target-data.service'; +} from '../core/suggestion-notifications/target/suggestion-target-data.service'; import { SuggestionsDataService -} from '../../core/suggestion-notifications/reciter-suggestions/suggestions-data.service'; +} from '../core/suggestion-notifications/suggestions-data.service'; +/** + * useful for multiple approvals and ignores operation + * */ export interface SuggestionBulkResult { success: number; fails: number; @@ -199,7 +202,7 @@ export class SuggestionsService { * Perform the delete operation over a single suggestion. * @param suggestionId */ - public notMine(suggestionId): Observable> { + public ignoreSuggestion(suggestionId): Observable> { return this.deleteReviewedSuggestion(suggestionId).pipe( catchError((error) => of(null)) ); @@ -229,8 +232,8 @@ export class SuggestionsService { * Perform a bulk notMine operation. * @param suggestions the array containing the suggestions */ - public notMineMultiple(suggestions: Suggestion[]): Observable { - return forkJoin(suggestions.map((suggestion: Suggestion) => this.notMine(suggestion.id))) + public ignoreSuggestionMultiple(suggestions: Suggestion[]): Observable { + return forkJoin(suggestions.map((suggestion: Suggestion) => this.ignoreSuggestion(suggestion.id))) .pipe(map((results: RemoteData[]) => { return { success: results.filter((result) => result != null).length, diff --git a/src/app/suggestions-page/suggestions-page-routing.module.ts b/src/app/suggestions-page/suggestions-page-routing.module.ts index 05dc6321b70..0a7d9bd90c5 100644 --- a/src/app/suggestions-page/suggestions-page-routing.module.ts +++ b/src/app/suggestions-page/suggestions-page-routing.module.ts @@ -4,7 +4,7 @@ import { RouterModule } from '@angular/router'; import { SuggestionsPageResolver } from './suggestions-page.resolver'; import { SuggestionsPageComponent } from './suggestions-page.component'; import { AuthenticatedGuard } from '../core/auth/authenticated.guard'; -import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver'; +import { ItemBreadcrumbResolver } from '../core/breadcrumbs/item-breadcrumb.resolver'; @NgModule({ imports: [ @@ -13,11 +13,11 @@ import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.reso path: ':targetId', resolve: { suggestionTargets: SuggestionsPageResolver, - breadcrumb: I18nBreadcrumbResolver + breadcrumb: ItemBreadcrumbResolver//I18nBreadcrumbResolver }, data: { - title: 'admin.notifications.recitersuggestion.page.title', - breadcrumbKey: 'admin.notifications.recitersuggestion', + title: 'admin.notifications.publicationclaim.page.title', + breadcrumbKey: 'admin.notifications.publicationclaim', showBreadcrumbsFluid: false }, canActivate: [AuthenticatedGuard], @@ -27,7 +27,8 @@ import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.reso ]) ], providers: [ - SuggestionsPageResolver + SuggestionsPageResolver, + ItemBreadcrumbResolver ] }) export class SuggestionsPageRoutingModule { diff --git a/src/app/suggestions-page/suggestions-page.component.html b/src/app/suggestions-page/suggestions-page.component.html index 59b6f58c5fc..f8b14f67286 100644 --- a/src/app/suggestions-page/suggestions-page.component.html +++ b/src/app/suggestions-page/suggestions-page.component.html @@ -19,7 +19,7 @@

[isBulk]="true" [isCollectionFixed]="isCollectionFixed(suggestionsRD.page)" (approveAndImport)="approveAndImportAllSelected($event)" - (notMineClicked)="notMineAllSelected()"> + (ignoreSuggestionClicked)="ignoreSuggestionAllSelected()">

@@ -35,7 +35,7 @@

[object]="object" [isSelected]="selectedSuggestions[object.id]" [isCollectionFixed]="isCollectionFixed([object])" - (notMineClicked)="notMine($event)" + (ignoreSuggestionClicked)="ignoreSuggestion($event)" (selected)="onSelected(object, $event)" (approveAndImport)="approveAndImport($event)"> diff --git a/src/app/suggestions-page/suggestions-page.component.spec.ts b/src/app/suggestions-page/suggestions-page.component.spec.ts index 0b0e235082d..78cf0873628 100644 --- a/src/app/suggestions-page/suggestions-page.component.spec.ts +++ b/src/app/suggestions-page/suggestions-page.component.spec.ts @@ -10,23 +10,23 @@ import { SuggestionsPageComponent } from './suggestions-page.component'; import { SuggestionApproveAndImport, SuggestionListElementComponent -} from '../suggestion-notifications/reciter-suggestions/suggestion-list-element/suggestion-list-element.component'; -import { SuggestionsService } from '../suggestion-notifications/reciter-suggestions/suggestions.service'; +} from '../suggestion-notifications/suggestion-list-element/suggestion-list-element.component'; +import { SuggestionsService } from '../suggestion-notifications/suggestions.service'; import { getMockSuggestionNotificationsStateService, getMockSuggestionsService } from '../shared/mocks/suggestion.mock'; import { buildPaginatedList, PaginatedList } from '../core/data/paginated-list.model'; -import { Suggestion } from '../core/suggestion-notifications/reciter-suggestions/models/suggestion.model'; -import { mockSuggestionPublicationOne, mockSuggestionPublicationTwo } from '../shared/mocks/reciter-suggestion.mock'; -import { SuggestionEvidencesComponent } from '../suggestion-notifications/reciter-suggestions/suggestion-list-element/suggestion-evidences/suggestion-evidences.component'; +import { Suggestion } from '../core/suggestion-notifications/models/suggestion.model'; +import { mockSuggestionPublicationOne, mockSuggestionPublicationTwo } from '../shared/mocks/publication-claim.mock'; +import { SuggestionEvidencesComponent } from '../suggestion-notifications/suggestion-list-element/suggestion-evidences/suggestion-evidences.component'; import { ObjectKeysPipe } from '../shared/utils/object-keys-pipe'; import { VarDirective } from '../shared/utils/var.directive'; import { ActivatedRoute, Router } from '@angular/router'; import { RouterStub } from '../shared/testing/router.stub'; -import { mockSuggestionTargetsObjectOne } from '../shared/mocks/reciter-suggestion-targets.mock'; +import { mockSuggestionTargetsObjectOne } from '../shared/mocks/publication-claim-targets.mock'; import { AuthService } from '../core/auth/auth.service'; import { NotificationsService } from '../shared/notifications/notifications.service'; import { NotificationsServiceStub } from '../shared/testing/notifications-service.stub'; import { getMockTranslateService } from '../shared/mocks/translate.service.mock'; -import { SuggestionTargetsStateService } from '../suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.state.service'; +import { SuggestionTargetsStateService } from '../suggestion-notifications/suggestion-targets/suggestion-targets.state.service'; import { WorkspaceitemDataService } from '../core/submission/workspaceitem-data.service'; import { createSuccessfulRemoteDataObject } from '../shared/remote-data.utils'; import { PageInfo } from '../core/shared/page-info.model'; @@ -138,7 +138,7 @@ describe('SuggestionPageComponent', () => { scheduler.schedule(() => fixture.detectChanges()); scheduler.flush(); - component.notMine('1'); + component.ignoreSuggestion('1'); expect(mockSuggestionsService.notMine).toHaveBeenCalledWith('1'); expect(mockSuggestionsTargetStateService.dispatchRefreshUserSuggestionsAction).toHaveBeenCalled(); expect(component.updatePage).toHaveBeenCalled(); @@ -149,7 +149,7 @@ describe('SuggestionPageComponent', () => { scheduler.schedule(() => fixture.detectChanges()); scheduler.flush(); - component.notMineAllSelected(); + component.ignoreSuggestionAllSelected(); expect(mockSuggestionsService.notMineMultiple).toHaveBeenCalled(); expect(mockSuggestionsTargetStateService.dispatchRefreshUserSuggestionsAction).toHaveBeenCalled(); expect(component.updatePage).toHaveBeenCalled(); diff --git a/src/app/suggestions-page/suggestions-page.component.ts b/src/app/suggestions-page/suggestions-page.component.ts index b4ca0108d5a..4883ed8406e 100644 --- a/src/app/suggestions-page/suggestions-page.component.ts +++ b/src/app/suggestions-page/suggestions-page.component.ts @@ -9,14 +9,14 @@ import { SortDirection, SortOptions, } from '../core/cache/models/sort-options.m import { PaginatedList } from '../core/data/paginated-list.model'; import { RemoteData } from '../core/data/remote-data'; import { getFirstSucceededRemoteDataPayload } from '../core/shared/operators'; -import { SuggestionBulkResult, SuggestionsService } from '../suggestion-notifications/reciter-suggestions/suggestions.service'; +import { SuggestionBulkResult, SuggestionsService } from '../suggestion-notifications/suggestions.service'; import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model'; -import { Suggestion } from '../core/suggestion-notifications/reciter-suggestions/models/suggestion.model'; -import { SuggestionTarget } from '../core/suggestion-notifications/reciter-suggestions/models/suggestion-target.model'; +import { Suggestion } from '../core/suggestion-notifications/models/suggestion.model'; +import { SuggestionTarget } from '../core/suggestion-notifications/models/suggestion-target.model'; import { AuthService } from '../core/auth/auth.service'; -import { SuggestionApproveAndImport } from '../suggestion-notifications/reciter-suggestions/suggestion-list-element/suggestion-list-element.component'; +import { SuggestionApproveAndImport } from '../suggestion-notifications/suggestion-list-element/suggestion-list-element.component'; import { NotificationsService } from '../shared/notifications/notifications.service'; -import { SuggestionTargetsStateService } from '../suggestion-notifications/reciter-suggestions/suggestion-targets/suggestion-targets.state.service'; +import { SuggestionTargetsStateService } from '../suggestion-notifications/suggestion-targets/suggestion-targets.state.service'; import { WorkspaceitemDataService } from '../core/submission/workspaceitem-data.service'; import { PaginationService } from '../core/pagination/pagination.service'; import { WorkspaceItem } from '../core/submission/models/workspaceitem.model'; @@ -155,8 +155,8 @@ export class SuggestionsPageComponent implements OnInit { * Used to delete a suggestion. * @suggestionId */ - notMine(suggestionId) { - this.suggestionService.notMine(suggestionId).subscribe((res) => { + ignoreSuggestion(suggestionId) { + this.suggestionService.ignoreSuggestion(suggestionId).subscribe((res) => { this.suggestionTargetsStateService.dispatchRefreshUserSuggestionsAction(); this.updatePage(); }); @@ -165,10 +165,10 @@ export class SuggestionsPageComponent implements OnInit { /** * Used to delete all selected suggestions. */ - notMineAllSelected() { + ignoreSuggestionAllSelected() { this.isBulkOperationPending = true; this.suggestionService - .notMineMultiple(Object.values(this.selectedSuggestions)) + .ignoreSuggestionMultiple(Object.values(this.selectedSuggestions)) .subscribe((results: SuggestionBulkResult) => { this.suggestionTargetsStateService.dispatchRefreshUserSuggestionsAction(); this.updatePage(); diff --git a/src/app/suggestions-page/suggestions-page.module.ts b/src/app/suggestions-page/suggestions-page.module.ts index f5397cbdce4..e25e483e39a 100644 --- a/src/app/suggestions-page/suggestions-page.module.ts +++ b/src/app/suggestions-page/suggestions-page.module.ts @@ -4,8 +4,8 @@ import { CommonModule } from '@angular/common'; import { SuggestionsPageComponent } from './suggestions-page.component'; import { SharedModule } from '../shared/shared.module'; import { SuggestionsPageRoutingModule } from './suggestions-page-routing.module'; -import { SuggestionsService } from '../suggestion-notifications/reciter-suggestions/suggestions.service'; -import { SuggestionsDataService } from '../core/suggestion-notifications/reciter-suggestions/suggestions-data.service'; +import { SuggestionsService } from '../suggestion-notifications/suggestions.service'; +import { SuggestionsDataService } from '../core/suggestion-notifications/suggestions-data.service'; import { NotificationsModule } from '../notifications/notifications.module'; @NgModule({ diff --git a/src/app/suggestions-page/suggestions-page.resolver.ts b/src/app/suggestions-page/suggestions-page.resolver.ts index 6d05c0bc84a..dde5b847f5d 100644 --- a/src/app/suggestions-page/suggestions-page.resolver.ts +++ b/src/app/suggestions-page/suggestions-page.resolver.ts @@ -6,10 +6,10 @@ import { find } from 'rxjs/operators'; import { RemoteData } from '../core/data/remote-data'; import { hasValue } from '../shared/empty.util'; -import { SuggestionTarget } from '../core/suggestion-notifications/reciter-suggestions/models/suggestion-target.model'; +import { SuggestionTarget } from '../core/suggestion-notifications/models/suggestion-target.model'; import { SuggestionTargetDataService -} from '../core/suggestion-notifications/reciter-suggestions/target/suggestion-target-data.service'; +} from '../core/suggestion-notifications/target/suggestion-target-data.service'; /** * This class represents a resolver that requests a specific collection before the route is activated diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 0e5ea850740..989d2ea9fbc 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -24,9 +24,9 @@ "404.page-not-found": "page not found", - "admin.notifications.recitersuggestion.breadcrumbs": "Suggestions", + "admin.notifications.publicationclaim.breadcrumbs": "Suggestions", - "admin.notifications.recitersuggestion.page.title": "Suggestions", + "admin.notifications.publicationclaim.page.title": "Suggestions", "error-page.description.401": "unauthorized", @@ -2950,7 +2950,7 @@ "menu.section.quality-assurance": "Quality Assurance", - "menu.section.notifications_reciter": "Publication Claim", + "menu.section.notifications_publication-claim": "Publication Claim", "menu.section.pin": "Pin sidebar", @@ -3562,63 +3562,67 @@ "media-viewer.playlist": "Playlist", - "reciter.suggestion.loading": "Loading ...", + "suggestion.loading": "Loading ...", - "reciter.suggestion.title": "Suggestions", + "suggestion.title": "Publication Claims", - "reciter.suggestion.targets.description": "Below you can see all the suggestions ", + "suggestion.title.breadcrumbs": "Publication Claims", - "reciter.suggestion.targets": "Current Suggestions", + "suggestion.targets.description": "Below you can see all the suggestions ", - "reciter.suggestion.table.name": "Researcher Name", + "suggestion.targets": "Current Suggestions", - "reciter.suggestion.table.actions": "Actions", + "suggestion.table.name": "Researcher Name", - "reciter.suggestion.button.review": "Review {{ total }} suggestion(s)", + "suggestion.table.actions": "Actions", - "reciter.suggestion.noTargets": "No target found.", + "suggestion.button.review": "Review {{ total }} suggestion(s)", - "reciter.suggestion.target.error.service.retrieve": "An error occurred while loading the Suggestion targets", + "suggestion.button.review.title": "Review {{ total }} suggestion(s) for ", - "reciter.suggestion.evidence.type": "Type", + "suggestion.noTargets": "No target found.", - "reciter.suggestion.evidence.score": "Score", + "suggestion.target.error.service.retrieve": "An error occurred while loading the Suggestion targets", - "reciter.suggestion.evidence.notes": "Notes", + "suggestion.evidence.type": "Type", - "reciter.suggestion.approveAndImport": "Approve & import", + "suggestion.evidence.score": "Score", - "reciter.suggestion.approveAndImport.success": "The suggestion has been imported successfully. View.", + "suggestion.evidence.notes": "Notes", - "reciter.suggestion.approveAndImport.bulk": "Approve & import Selected", + "suggestion.approveAndImport": "Approve & import", - "reciter.suggestion.approveAndImport.bulk.success": "{{ count }} suggestions have been imported successfully ", + "suggestion.approveAndImport.success": "The suggestion has been imported successfully. View.", - "reciter.suggestion.approveAndImport.bulk.error": "{{ count }} suggestions haven't been imported due to unexpected server errors", + "suggestion.approveAndImport.bulk": "Approve & import Selected", - "reciter.suggestion.notMine": "Not mine", + ".suggestion.approveAndImport.bulk.success": "{{ count }} suggestions have been imported successfully ", - "reciter.suggestion.notMine.success": "The suggestion has been discarded", + "suggestion.approveAndImport.bulk.error": "{{ count }} suggestions haven't been imported due to unexpected server errors", - "reciter.suggestion.notMine.bulk": "Not mine Selected", + "suggestion.ignoreSuggestion": "Ignore Suggestion", - "reciter.suggestion.notMine.bulk.success": "{{ count }} suggestions have been discarded ", + "suggestion.ignoreSuggestion.success": "The suggestion has been discarded", - "reciter.suggestion.notMine.bulk.error": "{{ count }} suggestions haven't been discarded due to unexpected server errors", + "suggestion.ignoreSuggestion.bulk": "Ignore Suggestion Selected", - "reciter.suggestion.seeEvidence": "See evidence", + "suggestion.ignoreSuggestion.bulk.success": "{{ count }} suggestions have been discarded ", - "reciter.suggestion.hideEvidence": "Hide evidence", + "suggestion.ignoreSuggestion.bulk.error": "{{ count }} suggestions haven't been discarded due to unexpected server errors", - "reciter.suggestion.suggestionFor": "Suggestion for", + "suggestion.seeEvidence": "See evidence", - "reciter.suggestion.source.openaire": "OpenAIRE Graph", + "suggestion.hideEvidence": "Hide evidence", - "reciter.suggestion.from.source": "from the ", + "suggestion.suggestionFor": "Suggestion for", - "reciter.suggestion.totalScore": "Total Score", + "suggestion.source.openaire": "OpenAIRE Graph", - "reciter.suggestion.type.openaire": "OpenAIRE", + "suggestion.from.source": "from the ", + + "suggestion.totalScore": "Total Score", + + "suggestion.type.openaire": "OpenAIRE", "register-email.title": "New user registration", From 66cd035f87a2f777fe3d5d1e9ac52e254f6ee159 Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Thu, 25 Jan 2024 11:50:02 +0100 Subject: [PATCH 16/34] refactor, fix tests --- ...ions-publication-claim-page.component.html | 2 +- ...s-publication-claim-page.component.spec.ts | 14 +- ...ations-publication-claim-page.component.ts | 2 +- .../admin-notifications-routing.module.ts | 4 +- .../admin-notifications.module.ts | 4 +- src/app/core/data/base/base-data.service.ts | 8 +- src/app/core/data/base/create-data.ts | 2 +- src/app/core/data/base/find-all-data.ts | 2 +- src/app/core/data/base/patch-data.ts | 2 +- src/app/core/data/base/put-data.ts | 2 +- src/app/core/data/base/search-data.ts | 2 +- src/app/core/data/href-only-data.service.ts | 4 +- ...data.service.ts => update-data-service.ts} | 385 +++--------------- src/app/core/data/update-data.service.ts | 13 - .../core/data/version-data.service.spec.ts | 2 +- .../core/eperson/eperson-data.service.spec.ts | 2 +- .../workflowitem-data.service.spec.ts | 2 +- .../workspaceitem-data.service.spec.ts | 2 +- .../suggestions-data.service.ts | 28 +- .../dso-edit-metadata.component.ts | 2 +- .../themed-dso-edit-metadata.component.ts | 2 +- src/app/menu.resolver.ts | 2 +- src/app/notifications/notifications.module.ts | 4 +- .../eperson-group-list.component.ts | 2 +- .../mydspace-actions-service.factory.ts | 2 +- .../mydspace-actions/mydspace-actions.ts | 2 +- .../suggestion-actions.component.html | 4 +- .../publication-claim.component.html} | 16 +- .../publication-claim.component.scss} | 0 .../publication-claim.component.ts} | 22 +- .../suggestion.service.spec.ts | 2 +- .../suggestions-popup.component.ts | 4 +- .../suggestions.service.ts | 4 +- .../suggestions-page.component.spec.ts | 5 +- .../suggestions-page.component.ts | 14 +- src/assets/i18n/en.json5 | 4 +- src/config/app-config.interface.ts | 4 +- src/config/default-app-config.ts | 4 +- src/config/layout-config.interfaces.ts | 46 --- src/config/suggestion-config.interfaces.ts | 6 + 40 files changed, 143 insertions(+), 490 deletions(-) rename src/app/core/data/{data.service.ts => update-data-service.ts} (52%) delete mode 100644 src/app/core/data/update-data.service.ts rename src/app/suggestion-notifications/suggestion-targets/{suggestion-targets.component.html => publication-claim/publication-claim.component.html} (72%) rename src/app/suggestion-notifications/suggestion-targets/{suggestion-targets.component.scss => publication-claim/publication-claim.component.scss} (100%) rename src/app/suggestion-notifications/suggestion-targets/{suggestion-targets.component.ts => publication-claim/publication-claim.component.ts} (83%) delete mode 100644 src/config/layout-config.interfaces.ts create mode 100644 src/config/suggestion-config.interfaces.ts diff --git a/src/app/admin/admin-notifications/admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component.html b/src/app/admin/admin-notifications/admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component.html index 965b9e08c90..b04e7132f17 100644 --- a/src/app/admin/admin-notifications/admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component.html +++ b/src/app/admin/admin-notifications/admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component.html @@ -1 +1 @@ - + diff --git a/src/app/admin/admin-notifications/admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component.spec.ts b/src/app/admin/admin-notifications/admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component.spec.ts index c0209da8988..c5406023ce9 100644 --- a/src/app/admin/admin-notifications/admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component.spec.ts +++ b/src/app/admin/admin-notifications/admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component.spec.ts @@ -1,13 +1,13 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { AdminNotificationsSuggestionTargetsPageComponent } from './admin-notifications-publication-claim-page.component'; +import { AdminNotificationsPublicationClaimPageComponent } from './admin-notifications-publication-claim-page.component'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { CommonModule } from '@angular/common'; import { TranslateModule } from '@ngx-translate/core'; -describe('AdminNotificationsSuggestionTargetsPageComponent', () => { - let component: AdminNotificationsSuggestionTargetsPageComponent; - let fixture: ComponentFixture; +describe('AdminNotificationsPublicationClaimPageComponent', () => { + let component: AdminNotificationsPublicationClaimPageComponent; + let fixture: ComponentFixture; beforeEach(async(() => { TestBed.configureTestingModule({ @@ -16,10 +16,10 @@ describe('AdminNotificationsSuggestionTargetsPageComponent', () => { TranslateModule.forRoot() ], declarations: [ - AdminNotificationsSuggestionTargetsPageComponent + AdminNotificationsPublicationClaimPageComponent ], providers: [ - AdminNotificationsSuggestionTargetsPageComponent + AdminNotificationsPublicationClaimPageComponent ], schemas: [NO_ERRORS_SCHEMA] }) @@ -27,7 +27,7 @@ describe('AdminNotificationsSuggestionTargetsPageComponent', () => { })); beforeEach(() => { - fixture = TestBed.createComponent(AdminNotificationsSuggestionTargetsPageComponent); + fixture = TestBed.createComponent(AdminNotificationsPublicationClaimPageComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/src/app/admin/admin-notifications/admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component.ts b/src/app/admin/admin-notifications/admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component.ts index 10023ead934..2256a1bc36a 100644 --- a/src/app/admin/admin-notifications/admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component.ts +++ b/src/app/admin/admin-notifications/admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component.ts @@ -5,6 +5,6 @@ import { Component } from '@angular/core'; templateUrl: './admin-notifications-publication-claim-page.component.html', styleUrls: ['./admin-notifications-publication-claim-page.component.scss'] }) -export class AdminNotificationsSuggestionTargetsPageComponent { +export class AdminNotificationsPublicationClaimPageComponent { } diff --git a/src/app/admin/admin-notifications/admin-notifications-routing.module.ts b/src/app/admin/admin-notifications/admin-notifications-routing.module.ts index 648bdc0a1f1..07a98aa080d 100644 --- a/src/app/admin/admin-notifications/admin-notifications-routing.module.ts +++ b/src/app/admin/admin-notifications/admin-notifications-routing.module.ts @@ -5,7 +5,7 @@ import { AuthenticatedGuard } from '../../core/auth/authenticated.guard'; import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver'; import { I18nBreadcrumbsService } from '../../core/breadcrumbs/i18n-breadcrumbs.service'; import { PUBLICATION_CLAIMS_PATH } from './admin-notifications-routing-paths'; -import { AdminNotificationsSuggestionTargetsPageComponent } from './admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component'; +import { AdminNotificationsPublicationClaimPageComponent } from './admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component'; import { AdminNotificationsPublicationClaimPageResolver } from './admin-notifications-publication-claim-page/admin-notifications-publication-claim-page-resolver.service'; import { QUALITY_ASSURANCE_EDIT_PATH } from './admin-notifications-routing-paths'; import { AdminQualityAssuranceTopicsPageComponent } from './admin-quality-assurance-topics-page/admin-quality-assurance-topics-page.component'; @@ -26,7 +26,7 @@ import { { canActivate: [ AuthenticatedGuard ], path: `${PUBLICATION_CLAIMS_PATH}`, - component: AdminNotificationsSuggestionTargetsPageComponent, + component: AdminNotificationsPublicationClaimPageComponent, pathMatch: 'full', resolve: { breadcrumb: I18nBreadcrumbResolver, diff --git a/src/app/admin/admin-notifications/admin-notifications.module.ts b/src/app/admin/admin-notifications/admin-notifications.module.ts index 3566bdd91ad..d9efb4c2886 100644 --- a/src/app/admin/admin-notifications/admin-notifications.module.ts +++ b/src/app/admin/admin-notifications/admin-notifications.module.ts @@ -3,7 +3,7 @@ import { NgModule } from '@angular/core'; import { CoreModule } from '../../core/core.module'; import { SharedModule } from '../../shared/shared.module'; import { AdminNotificationsRoutingModule } from './admin-notifications-routing.module'; -import { AdminNotificationsSuggestionTargetsPageComponent } from './admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component'; +import { AdminNotificationsPublicationClaimPageComponent } from './admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component'; import { AdminQualityAssuranceTopicsPageComponent } from './admin-quality-assurance-topics-page/admin-quality-assurance-topics-page.component'; import { AdminQualityAssuranceEventsPageComponent } from './admin-quality-assurance-events-page/admin-quality-assurance-events-page.component'; import { AdminQualityAssuranceSourcePageComponent } from './admin-quality-assurance-source-page-component/admin-quality-assurance-source-page.component'; @@ -18,7 +18,7 @@ import { NotificationsModule } from '../../notifications/notifications.module'; NotificationsModule ], declarations: [ - AdminNotificationsSuggestionTargetsPageComponent, + AdminNotificationsPublicationClaimPageComponent, AdminQualityAssuranceTopicsPageComponent, AdminQualityAssuranceEventsPageComponent, AdminQualityAssuranceSourcePageComponent diff --git a/src/app/core/data/base/base-data.service.ts b/src/app/core/data/base/base-data.service.ts index c7cd5b0a705..84b1686024b 100644 --- a/src/app/core/data/base/base-data.service.ts +++ b/src/app/core/data/base/base-data.service.ts @@ -29,11 +29,11 @@ export const EMBED_SEPARATOR = '%2F'; /** * Common functionality for data services. * Specific functionality that not all services would need - * is implemented in "DataService feature" classes (e.g. {@link CreateData} + * is implemented in "UpdateDataServiceImpl feature" classes (e.g. {@link CreateData} * - * All DataService (or DataService feature) classes must + * All UpdateDataServiceImpl (or UpdateDataServiceImpl feature) classes must * - extend this class (or {@link IdentifiableDataService}) - * - implement any DataService features it requires in order to forward calls to it + * - implement any UpdateDataServiceImpl features it requires in order to forward calls to it * * ``` * export class SomeDataService extends BaseDataService implements CreateData, SearchData { @@ -385,7 +385,7 @@ export class BaseDataService implements HALDataServic /** * Return the links to traverse from the root of the api to the - * endpoint this DataService represents + * endpoint this UpdateDataServiceImpl represents * * e.g. if the api root links to 'foo', and the endpoint at 'foo' * links to 'bar' the linkPath for the BarDataService would be diff --git a/src/app/core/data/base/create-data.ts b/src/app/core/data/base/create-data.ts index 3ffcd9adf20..d2e009f6698 100644 --- a/src/app/core/data/base/create-data.ts +++ b/src/app/core/data/base/create-data.ts @@ -37,7 +37,7 @@ export interface CreateData { } /** - * A DataService feature to create objects. + * A UpdateDataServiceImpl feature to create objects. * * Concrete data services can use this feature by implementing {@link CreateData} * and delegating its method to an inner instance of this class. diff --git a/src/app/core/data/base/find-all-data.ts b/src/app/core/data/base/find-all-data.ts index 57884e537e5..bc0c1fb613e 100644 --- a/src/app/core/data/base/find-all-data.ts +++ b/src/app/core/data/base/find-all-data.ts @@ -42,7 +42,7 @@ export interface FindAllData { } /** - * A DataService feature to list all objects. + * A UpdateDataServiceImpl feature to list all objects. * * Concrete data services can use this feature by implementing {@link FindAllData} * and delegating its method to an inner instance of this class. diff --git a/src/app/core/data/base/patch-data.ts b/src/app/core/data/base/patch-data.ts index e30c394a347..1f93671458f 100644 --- a/src/app/core/data/base/patch-data.ts +++ b/src/app/core/data/base/patch-data.ts @@ -54,7 +54,7 @@ export interface PatchData { } /** - * A DataService feature to patch and update objects. + * A UpdateDataServiceImpl feature to patch and update objects. * * Concrete data services can use this feature by implementing {@link PatchData} * and delegating its method to an inner instance of this class. diff --git a/src/app/core/data/base/put-data.ts b/src/app/core/data/base/put-data.ts index bd2a8d29299..66ae73405eb 100644 --- a/src/app/core/data/base/put-data.ts +++ b/src/app/core/data/base/put-data.ts @@ -31,7 +31,7 @@ export interface PutData { } /** - * A DataService feature to send PUT requests. + * A UpdateDataServiceImpl feature to send PUT requests. * * Concrete data services can use this feature by implementing {@link PutData} * and delegating its method to an inner instance of this class. diff --git a/src/app/core/data/base/search-data.ts b/src/app/core/data/base/search-data.ts index 536d6d6e254..ff0b4929458 100644 --- a/src/app/core/data/base/search-data.ts +++ b/src/app/core/data/base/search-data.ts @@ -51,7 +51,7 @@ export interface SearchData { } /** - * A DataService feature to search for objects. + * A UpdateDataServiceImpl feature to search for objects. * * Concrete data services can use this feature by implementing {@link SearchData} * and delegating its method to an inner instance of this class. diff --git a/src/app/core/data/href-only-data.service.ts b/src/app/core/data/href-only-data.service.ts index 0a765de101b..8393d714605 100644 --- a/src/app/core/data/href-only-data.service.ts +++ b/src/app/core/data/href-only-data.service.ts @@ -17,8 +17,8 @@ import { HALDataService } from './base/hal-data-service.interface'; import { dataService } from './base/data-service.decorator'; /** - * A DataService with only findByHref methods. Its purpose is to be used for resources that don't - * need to be retrieved by ID, or have any way to update them, but require a DataService in order + * A UpdateDataServiceImpl with only findByHref methods. Its purpose is to be used for resources that don't + * need to be retrieved by ID, or have any way to update them, but require a UpdateDataServiceImpl in order * for their links to be resolved by the LinkService. * * an @dataService annotation can be added for any number of these resource types diff --git a/src/app/core/data/data.service.ts b/src/app/core/data/update-data-service.ts similarity index 52% rename from src/app/core/data/data.service.ts rename to src/app/core/data/update-data-service.ts index 43b60f874d0..994cd0ee87b 100644 --- a/src/app/core/data/data.service.ts +++ b/src/app/core/data/update-data-service.ts @@ -42,46 +42,61 @@ import { } from './request.models'; import { RequestService } from './request.service'; import { RestRequestMethod } from './rest-request-method'; -import { UpdateDataService } from './update-data.service'; import { GenericConstructor } from '../shared/generic-constructor'; import { NoContent } from '../shared/NoContent.model'; import { CacheableObject } from '../cache/cacheable-object.model'; import { CoreState } from '../core-state.model'; import { FindListOptions } from './find-list-options.model'; +import { BaseDataService } from "./base/base-data.service"; +import { FindAllData, FindAllDataImpl } from "./base/find-all-data"; +import { SearchData, SearchDataImpl } from "./base/search-data"; +import { CreateData, CreateDataImpl } from "./base/create-data"; + +export interface UpdateDataService { + patch(dso: T, operations: Operation[]): Observable>; + update(object: T): Observable>; + commitUpdates(method?: RestRequestMethod); +} + + +/** + * Specific functionalities that not all services would need. + * Goal of the class is to update remote objects, handling custom methods that don't belong to BaseDataService + * + * Custom methods are: + * + * patch - Sends a patch request to modify current object + * update - Add a new patch to the object cache, diff between given object and cache + * commitUpdates - Sends the updates to the server + */ -export abstract class DataService implements UpdateDataService { - protected abstract requestService: RequestService; - protected abstract rdbService: RemoteDataBuildService; + +export abstract class UpdateDataServiceImpl extends BaseDataService implements UpdateDataService, FindAllData, SearchData, CreateData { protected abstract store: Store; - protected abstract linkPath: string; - protected abstract halService: HALEndpointService; - protected abstract objectCache: ObjectCacheService; - protected abstract notificationsService: NotificationsService; protected abstract http: HttpClient; protected abstract comparator: ChangeAnalyzer; - /** - * Allows subclasses to reset the response cache time. - */ - protected responseMsToLive: number; + private findAllData: FindAllDataImpl; + private searchData: SearchDataImpl; + private createData: CreateData; - /** - * Get the endpoint for browsing - * @param options The [[FindListOptions]] object - * @param linkPath The link path for the object - * @returns {Observable} - */ - getBrowseEndpoint(options: FindListOptions = {}, linkPath?: string): Observable { - return this.getEndpoint(); - } - /** - * Get the base endpoint for all requests - */ - protected getEndpoint(): Observable { - return this.halService.getEndpoint(this.linkPath); + constructor( + protected linkPath: string, + protected requestService: RequestService, + protected rdbService: RemoteDataBuildService, + protected objectCache: ObjectCacheService, + protected halService: HALEndpointService, + protected notificationsService: NotificationsService, + protected responseMsToLive: number, + ) { + super(linkPath, requestService, rdbService, objectCache, halService, responseMsToLive); + this.findAllData = new FindAllDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive); + this.searchData = new SearchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive); + this.createData = new CreateDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService ,this.responseMsToLive); } + /** * Create the HREF with given options object * @@ -92,16 +107,7 @@ export abstract class DataService implements UpdateDa * @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved */ public getFindAllHref(options: FindListOptions = {}, linkPath?: string, ...linksToFollow: FollowLinkConfig[]): Observable { - let endpoint$: Observable; - const args = []; - - endpoint$ = this.getBrowseEndpoint(options).pipe( - filter((href: string) => isNotEmpty(href)), - map((href: string) => isNotEmpty(linkPath) ? `${href}/${linkPath}` : href), - distinctUntilChanged() - ); - - return endpoint$.pipe(map((result: string) => this.buildHrefFromFindOptions(result, options, args, ...linksToFollow))); + return this.findAllData.getFindAllHref(options, linkPath, ...linksToFollow) } /** @@ -114,149 +120,7 @@ export abstract class DataService implements UpdateDa * @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved */ public getSearchByHref(searchMethod: string, options: FindListOptions = {}, ...linksToFollow: FollowLinkConfig[]): Observable { - let result$: Observable; - const args = []; - - result$ = this.getSearchEndpoint(searchMethod); - - return result$.pipe(map((result: string) => this.buildHrefFromFindOptions(result, options, args, ...linksToFollow))); - } - - /** - * Turn an options object into a query string and combine it with the given HREF - * - * @param href The HREF to which the query string should be appended - * @param options The [[FindListOptions]] object - * @param extraArgs Array with additional params to combine with query string - * @return {Observable} - * Return an observable that emits created HREF - * @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved - */ - public buildHrefFromFindOptions(href: string, options: FindListOptions, extraArgs: string[] = [], ...linksToFollow: FollowLinkConfig[]): string { - let args = [...extraArgs]; - - if (hasValue(options.currentPage) && typeof options.currentPage === 'number') { - /* TODO: this is a temporary fix for the pagination start index (0 or 1) discrepancy between the rest and the frontend respectively */ - args = this.addHrefArg(href, args, `page=${options.currentPage - 1}`); - } - if (hasValue(options.elementsPerPage)) { - args = this.addHrefArg(href, args, `size=${options.elementsPerPage}`); - } - if (hasValue(options.sort)) { - args = this.addHrefArg(href, args, `sort=${options.sort.field},${options.sort.direction}`); - } - if (hasValue(options.startsWith)) { - args = this.addHrefArg(href, args, `startsWith=${options.startsWith}`); - } - if (hasValue(options.searchParams)) { - options.searchParams.forEach((param: RequestParam) => { - args = this.addHrefArg(href, args, `${param.fieldName}=${param.fieldValue}`); - }); - } - args = this.addEmbedParams(href, args, ...linksToFollow); - if (isNotEmpty(args)) { - return new URLCombiner(href, `?${args.join('&')}`).toString(); - } else { - return href; - } - } - - /** - * Turn an array of RequestParam into a query string and combine it with the given HREF - * - * @param href The HREF to which the query string should be appended - * @param params Array with additional params to combine with query string - * @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved - * - * @return {Observable} - * Return an observable that emits created HREF - */ - buildHrefWithParams(href: string, params: RequestParam[], ...linksToFollow: FollowLinkConfig[]): string { - - let args = []; - if (hasValue(params)) { - params.forEach((param: RequestParam) => { - args = this.addHrefArg(href, args, `${param.fieldName}=${param.fieldValue}`); - }); - } - - args = this.addEmbedParams(href, args, ...linksToFollow); - - if (isNotEmpty(args)) { - return new URLCombiner(href, `?${args.join('&')}`).toString(); - } else { - return href; - } - } - /** - * Adds the embed options to the link for the request - * @param href The href the params are to be added to - * @param args params for the query string - * @param linksToFollow links we want to embed in query string if shouldEmbed is true - */ - protected addEmbedParams(href: string, args: string[], ...linksToFollow: FollowLinkConfig[]) { - linksToFollow.forEach((linkToFollow: FollowLinkConfig) => { - if (hasValue(linkToFollow) && linkToFollow.shouldEmbed) { - const embedString = 'embed=' + String(linkToFollow.name); - // Add the embeds size if given in the FollowLinkConfig.FindListOptions - if (hasValue(linkToFollow.findListOptions) && hasValue(linkToFollow.findListOptions.elementsPerPage)) { - args = this.addHrefArg(href, args, - 'embed.size=' + String(linkToFollow.name) + '=' + linkToFollow.findListOptions.elementsPerPage); - } - // Adds the nested embeds and their size if given - if (isNotEmpty(linkToFollow.linksToFollow)) { - args = this.addNestedEmbeds(embedString, href, args, ...linkToFollow.linksToFollow); - } else { - args = this.addHrefArg(href, args, embedString); - } - } - }); - return args; - } - - /** - * Add a new argument to the list of arguments, only if it doesn't already exist in the given href, - * or the current list of arguments - * - * @param href The href the arguments are to be added to - * @param currentArgs The current list of arguments - * @param newArg The new argument to add - * @return The next list of arguments, with newArg included if it wasn't already. - * Note this function will not modify any of the input params. - */ - protected addHrefArg(href: string, currentArgs: string[], newArg: string): string[] { - if (href.includes(newArg) || currentArgs.includes(newArg)) { - return [...currentArgs]; - } else { - return [...currentArgs, newArg]; - } - } - - /** - * Add the nested followLinks to the embed param, separated by a /, and their sizes, recursively - * @param embedString embedString so far (recursive) - * @param href The href the params are to be added to - * @param args params for the query string - * @param linksToFollow links we want to embed in query string if shouldEmbed is true - */ - protected addNestedEmbeds(embedString: string, href: string, args: string[], ...linksToFollow: FollowLinkConfig[]): string[] { - let nestEmbed = embedString; - linksToFollow.forEach((linkToFollow: FollowLinkConfig) => { - if (hasValue(linkToFollow) && linkToFollow.shouldEmbed) { - nestEmbed = nestEmbed + '/' + String(linkToFollow.name); - // Add the nested embeds size if given in the FollowLinkConfig.FindListOptions - if (hasValue(linkToFollow.findListOptions) && hasValue(linkToFollow.findListOptions.elementsPerPage)) { - const nestedEmbedSize = 'embed.size=' + nestEmbed.split('=')[1] + '=' + linkToFollow.findListOptions.elementsPerPage; - args = this.addHrefArg(href, args, nestedEmbedSize); - } - if (hasValue(linkToFollow.linksToFollow) && isNotEmpty(linkToFollow.linksToFollow)) { - args = this.addNestedEmbeds(nestEmbed, href, args, ...linkToFollow.linksToFollow); - } else { - args = this.addHrefArg(href, args, nestEmbed); - } - } - }); - return args; + return this.searchData.getSearchByHref(searchMethod, options, ...linksToFollow) } /** @@ -274,7 +138,7 @@ export abstract class DataService implements UpdateDa * Return an observable that emits object list */ findAll(options: FindListOptions = {}, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig[]): Observable>> { - return this.findAllByHref(this.getFindAllHref(options), options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow); + return this.findAllData.findAll(options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow) } /** @@ -337,118 +201,6 @@ export abstract class DataService implements UpdateDa }; } - /** - * Returns an observable of {@link RemoteData} of an object, based on an href, with a list of - * {@link FollowLinkConfig}, to automatically resolve {@link HALLink}s of the object - * @param href$ The url of object we want to retrieve. Can be a string or - * an Observable - * @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's - * no valid cached version. Defaults to true - * @param reRequestOnStale Whether or not the request should automatically be re- - * requested after the response becomes stale - * @param linksToFollow List of {@link FollowLinkConfig} that indicate which - * {@link HALLink}s should be automatically resolved - */ - findByHref(href$: string | Observable, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig[]): Observable> { - if (typeof href$ === 'string') { - href$ = observableOf(href$); - } - - const requestHref$ = href$.pipe( - isNotEmptyOperator(), - take(1), - map((href: string) => this.buildHrefFromFindOptions(href, {}, [], ...linksToFollow)) - ); - - this.createAndSendGetRequest(requestHref$, useCachedVersionIfAvailable); - - return this.rdbService.buildSingle(requestHref$, ...linksToFollow).pipe( - // This skip ensures that if a stale object is present in the cache when you do a - // call it isn't immediately returned, but we wait until the remote data for the new request - // is created. If useCachedVersionIfAvailable is false it also ensures you don't get a - // cached completed object - skipWhile((rd: RemoteData) => useCachedVersionIfAvailable ? rd.isStale : rd.hasCompleted), - this.reRequestStaleRemoteData(reRequestOnStale, () => - this.findByHref(href$, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow)) - ); - } - - /** - * Returns a list of observables of {@link RemoteData} of objects, based on an href, with a list - * of {@link FollowLinkConfig}, to automatically resolve {@link HALLink}s of the object - * @param href$ The url of object we want to retrieve. Can be a string or - * an Observable - * @param findListOptions Find list options object - * @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's - * no valid cached version. Defaults to true - * @param reRequestOnStale Whether or not the request should automatically be re- - * requested after the response becomes stale - * @param linksToFollow List of {@link FollowLinkConfig} that indicate which - * {@link HALLink}s should be automatically resolved - */ - findAllByHref(href$: string | Observable, findListOptions: FindListOptions = {}, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig[]): Observable>> { - if (typeof href$ === 'string') { - href$ = observableOf(href$); - } - - const requestHref$ = href$.pipe( - isNotEmptyOperator(), - take(1), - map((href: string) => this.buildHrefFromFindOptions(href, findListOptions, [], ...linksToFollow)) - ); - - this.createAndSendGetRequest(requestHref$, useCachedVersionIfAvailable); - - return this.rdbService.buildList(requestHref$, ...linksToFollow).pipe( - // This skip ensures that if a stale object is present in the cache when you do a - // call it isn't immediately returned, but we wait until the remote data for the new request - // is created. If useCachedVersionIfAvailable is false it also ensures you don't get a - // cached completed object - skipWhile((rd: RemoteData>) => useCachedVersionIfAvailable ? rd.isStale : rd.hasCompleted), - this.reRequestStaleRemoteData(reRequestOnStale, () => - this.findAllByHref(href$, findListOptions, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow)) - ); - } - - /** - * Create a GET request for the given href, and send it. - * - * @param href$ The url of object we want to retrieve. Can be a string or - * an Observable - * @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's - * no valid cached version. Defaults to true - */ - protected createAndSendGetRequest(href$: string | Observable, useCachedVersionIfAvailable = true): void { - if (isNotEmpty(href$)) { - if (typeof href$ === 'string') { - href$ = observableOf(href$); - } - - href$.pipe( - isNotEmptyOperator(), - take(1) - ).subscribe((href: string) => { - const requestId = this.requestService.generateRequestId(); - const request = new GetRequest(requestId, href); - if (hasValue(this.responseMsToLive)) { - request.responseMsToLive = this.responseMsToLive; - } - this.requestService.send(request, useCachedVersionIfAvailable); - }); - } - } - - /** - * Return object search endpoint by given search method - * - * @param searchMethod The search method for the object - */ - protected getSearchEndpoint(searchMethod: string): Observable { - return this.halService.getEndpoint(this.linkPath).pipe( - filter((href: string) => isNotEmpty(href)), - map((href: string) => `${href}/search/${searchMethod}`)); - } - /** * Make a new FindListRequest with given search method * @@ -464,9 +216,7 @@ export abstract class DataService implements UpdateDa * Return an observable that emits response from the server */ searchBy(searchMethod: string, options: FindListOptions = {}, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig[]): Observable>> { - const hrefObs = this.getSearchByHref(searchMethod, options, ...linksToFollow); - - return this.findAllByHref(hrefObs, undefined, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow); + return this.searchData.searchBy(searchMethod, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow) } /** @@ -548,38 +298,7 @@ export abstract class DataService implements UpdateDa * Array with additional params to combine with query string */ create(object: T, ...params: RequestParam[]): Observable> { - const requestId = this.requestService.generateRequestId(); - const endpoint$ = this.getEndpoint().pipe( - isNotEmptyOperator(), - distinctUntilChanged(), - map((endpoint: string) => this.buildHrefWithParams(endpoint, params)) - ); - - const serializedObject = new DSpaceSerializer(getClassForType(object.type)).serialize(object); - - endpoint$.pipe( - take(1) - ).subscribe((endpoint: string) => { - const request = new CreateRequest(requestId, endpoint, JSON.stringify(serializedObject)); - if (hasValue(this.responseMsToLive)) { - request.responseMsToLive = this.responseMsToLive; - } - this.requestService.send(request); - }); - - const result$ = this.rdbService.buildFromRequestUUID(requestId); - - // TODO a dataservice is not the best place to show a notification, - // this should move up to the components that use this method - result$.pipe( - takeWhile((rd: RemoteData) => rd.isLoading, true) - ).subscribe((rd: RemoteData) => { - if (rd.hasFailed) { - this.notificationsService.error('Server Error:', rd.errorMessage, new NotificationOptions(-1)); - } - }); - - return result$; + return this.createData.create(object, ...params) } /** @@ -732,16 +451,4 @@ export abstract class DataService implements UpdateDa commitUpdates(method?: RestRequestMethod) { this.requestService.commit(method); } - - /** - * Return the links to traverse from the root of the api to the - * endpoint this DataService represents - * - * e.g. if the api root links to 'foo', and the endpoint at 'foo' - * links to 'bar' the linkPath for the BarDataService would be - * 'foo/bar' - */ - getLinkPath(): string { - return this.linkPath; - } } diff --git a/src/app/core/data/update-data.service.ts b/src/app/core/data/update-data.service.ts deleted file mode 100644 index 9f707a82da9..00000000000 --- a/src/app/core/data/update-data.service.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Observable } from 'rxjs'; -import { RemoteData } from './remote-data'; -import { RestRequestMethod } from './rest-request-method'; -import { Operation } from 'fast-json-patch'; - -/** - * Represents a data service to update a given object - */ -export interface UpdateDataService { - patch(dso: T, operations: Operation[]): Observable>; - update(object: T): Observable>; - commitUpdates(method?: RestRequestMethod); -} diff --git a/src/app/core/data/version-data.service.spec.ts b/src/app/core/data/version-data.service.spec.ts index dd3f9eec945..0579b998b87 100644 --- a/src/app/core/data/version-data.service.spec.ts +++ b/src/app/core/data/version-data.service.spec.ts @@ -128,7 +128,7 @@ describe('VersionDataService test', () => { }); describe('getHistoryFromVersion', () => { - it('should proxy the call to DataService.findByHref', () => { + it('should proxy the call to UpdateDataServiceImpl.findByHref', () => { scheduler.schedule(() => service.getHistoryFromVersion(mockVersion, true, true)); scheduler.flush(); diff --git a/src/app/core/eperson/eperson-data.service.spec.ts b/src/app/core/eperson/eperson-data.service.spec.ts index c1bc3563a32..cbddf1e6c3e 100644 --- a/src/app/core/eperson/eperson-data.service.spec.ts +++ b/src/app/core/eperson/eperson-data.service.spec.ts @@ -315,7 +315,7 @@ describe('EPersonDataService', () => { service.deleteEPerson(EPersonMock).subscribe(); }); - it('should call DataService.delete with the EPerson\'s UUID', () => { + it('should call UpdateDataServiceImpl.delete with the EPerson\'s UUID', () => { expect(service.delete).toHaveBeenCalledWith(EPersonMock.id); }); }); diff --git a/src/app/core/submission/workflowitem-data.service.spec.ts b/src/app/core/submission/workflowitem-data.service.spec.ts index 3f6ec54fdad..64ffbe57180 100644 --- a/src/app/core/submission/workflowitem-data.service.spec.ts +++ b/src/app/core/submission/workflowitem-data.service.spec.ts @@ -126,7 +126,7 @@ describe('WorkflowItemDataService test', () => { }); describe('findByItem', () => { - it('should proxy the call to DataService.findByHref', () => { + it('should proxy the call to UpdateDataServiceImpl.findByHref', () => { scheduler.schedule(() => service.findByItem('1234-1234', true, true, pageInfo)); scheduler.flush(); diff --git a/src/app/core/submission/workspaceitem-data.service.spec.ts b/src/app/core/submission/workspaceitem-data.service.spec.ts index 34548c1e53f..25a849baa28 100644 --- a/src/app/core/submission/workspaceitem-data.service.spec.ts +++ b/src/app/core/submission/workspaceitem-data.service.spec.ts @@ -141,7 +141,7 @@ describe('WorkspaceitemDataService test', () => { }); describe('findByItem', () => { - it('should proxy the call to DataService.findByHref', () => { + it('should proxy the call to UpdateDataServiceImpl.findByHref', () => { scheduler.schedule(() => service.findByItem('1234-1234', true, true, pageInfo)); scheduler.flush(); const searchUrl = service.getIDHref('item', [new RequestParam('uuid', encodeURIComponent('1234-1234'))]); diff --git a/src/app/core/suggestion-notifications/suggestions-data.service.ts b/src/app/core/suggestion-notifications/suggestions-data.service.ts index 72476f6a156..945cc8b083f 100644 --- a/src/app/core/suggestion-notifications/suggestions-data.service.ts +++ b/src/app/core/suggestion-notifications/suggestions-data.service.ts @@ -11,7 +11,7 @@ import { RemoteDataBuildService } from '../cache/builders/remote-data-build.serv import { ObjectCacheService } from '../cache/object-cache.service'; import { dataService } from '../cache/builders/build-decorators'; import { RequestService } from '../data/request.service'; -import { DataService } from '../data/data.service'; +import { UpdateDataServiceImpl } from '../data/update-data-service'; import { ChangeAnalyzer } from '../data/change-analyzer'; import { DefaultChangeAnalyzer } from '../data/default-change-analyzer.service'; import { RemoteData } from '../data/remote-data'; @@ -31,13 +31,9 @@ import { SuggestionTargetDataService } from './target/suggestion-target-data.ser /* tslint:disable:max-classes-per-file */ /** - * A private DataService implementation to delegate specific methods to. + * A private UpdateDataServiceImpl implementation to delegate specific methods to. */ -export class SuggestionDataServiceImpl extends DataService { - /** - * The REST endpoint. - */ - protected linkPath = 'suggestions'; +export class SuggestionDataServiceImpl extends UpdateDataServiceImpl { /** * Initialize service variables @@ -49,6 +45,7 @@ export class SuggestionDataServiceImpl extends DataService { * @param {NotificationsService} notificationsService * @param {HttpClient} http * @param {ChangeAnalyzer} comparator + * @param responseMsToLive */ constructor( protected requestService: RequestService, @@ -58,8 +55,10 @@ export class SuggestionDataServiceImpl extends DataService { protected halService: HALEndpointService, protected notificationsService: NotificationsService, protected http: HttpClient, - protected comparator: ChangeAnalyzer) { - super(); + protected comparator: ChangeAnalyzer, + protected responseMsToLive: number, + ) { + super('suggestions', requestService, rdbService, objectCache, halService, notificationsService ,responseMsToLive); } } @@ -73,20 +72,22 @@ export class SuggestionsDataService { protected searchFindByTargetAndSourceMethod = 'findByTargetAndSource'; /** - * A private DataService implementation to delegate specific methods to. + * A private UpdateDataServiceImpl implementation to delegate specific methods to. */ private suggestionsDataService: SuggestionDataServiceImpl; /** - * A private DataService implementation to delegate specific methods to. + * A private UpdateDataServiceImpl implementation to delegate specific methods to. */ private suggestionSourcesDataService: SuggestionSourceDataService; /** - * A private DataService implementation to delegate specific methods to. + * A private UpdateDataServiceImpl implementation to delegate specific methods to. */ private suggestionTargetsDataService: SuggestionTargetDataService; + private responseMsToLive = 10 * 1000; + /** * Initialize service variables * @param {RequestService} requestService @@ -98,6 +99,7 @@ export class SuggestionsDataService { * @param {DefaultChangeAnalyzer} comparatorSuggestions * @param {DefaultChangeAnalyzer} comparatorSources * @param {DefaultChangeAnalyzer} comparatorTargets + * @param responseMsToLive */ constructor( protected requestService: RequestService, @@ -110,7 +112,7 @@ export class SuggestionsDataService { protected comparatorSources: DefaultChangeAnalyzer, protected comparatorTargets: DefaultChangeAnalyzer, ) { - this.suggestionsDataService = new SuggestionDataServiceImpl(requestService, rdbService, null, objectCache, halService, notificationsService, http, comparatorSuggestions); + this.suggestionsDataService = new SuggestionDataServiceImpl(requestService, rdbService, null, objectCache, halService, notificationsService, http, comparatorSuggestions, this.responseMsToLive); this.suggestionSourcesDataService = new SuggestionSourceDataService(requestService, rdbService, null, objectCache, halService, notificationsService, http, comparatorSources); this.suggestionTargetsDataService = new SuggestionTargetDataService(requestService, rdbService, null, objectCache, halService, notificationsService, http, comparatorTargets); } diff --git a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.ts b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.ts index d44817be842..ad3d6680a17 100644 --- a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.ts +++ b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.ts @@ -12,7 +12,6 @@ import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject'; import { getFirstCompletedRemoteData, } from '../../core/shared/operators'; -import { UpdateDataService } from '../../core/data/update-data.service'; import { ResourceType } from '../../core/shared/resource-type'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { TranslateService } from '@ngx-translate/core'; @@ -22,6 +21,7 @@ import { ArrayMoveChangeAnalyzer } from '../../core/data/array-move-change-analy import { DATA_SERVICE_FACTORY } from '../../core/data/base/data-service.decorator'; import { GenericConstructor } from '../../core/shared/generic-constructor'; import { HALDataService } from '../../core/data/base/hal-data-service.interface'; +import { UpdateDataService } from "../../core/data/update-data-service"; @Component({ selector: 'ds-dso-edit-metadata', diff --git a/src/app/dso-shared/dso-edit-metadata/themed-dso-edit-metadata.component.ts b/src/app/dso-shared/dso-edit-metadata/themed-dso-edit-metadata.component.ts index ba21907c99e..51eec3ac230 100644 --- a/src/app/dso-shared/dso-edit-metadata/themed-dso-edit-metadata.component.ts +++ b/src/app/dso-shared/dso-edit-metadata/themed-dso-edit-metadata.component.ts @@ -2,7 +2,7 @@ import { ThemedComponent } from '../../shared/theme-support/themed.component'; import { DsoEditMetadataComponent } from './dso-edit-metadata.component'; import { Component, Input } from '@angular/core'; import { DSpaceObject } from '../../core/shared/dspace-object.model'; -import { UpdateDataService } from '../../core/data/update-data.service'; +import { UpdateDataService } from "../../core/data/update-data-service"; @Component({ selector: 'ds-themed-dso-edit-metadata', diff --git a/src/app/menu.resolver.ts b/src/app/menu.resolver.ts index fc6eb00195c..f2a7e2dbe19 100644 --- a/src/app/menu.resolver.ts +++ b/src/app/menu.resolver.ts @@ -541,7 +541,7 @@ export class MenuResolver implements Resolve { { id: 'notifications', active: false, - visible: authorized && canSeeQA, + visible: authorized && true, model: { type: MenuItemType.TEXT, text: 'menu.section.notifications' diff --git a/src/app/notifications/notifications.module.ts b/src/app/notifications/notifications.module.ts index cd8cb99811f..00c7582b2f0 100644 --- a/src/app/notifications/notifications.module.ts +++ b/src/app/notifications/notifications.module.ts @@ -26,7 +26,7 @@ import { QualityAssuranceSourceService } from './qa/source/quality-assurance-sou import { QualityAssuranceSourceDataService } from '../core/notifications/qa/source/quality-assurance-source-data.service'; -import { SuggestionTargetsComponent } from '../suggestion-notifications/suggestion-targets/suggestion-targets.component'; +import { PublicationClaimComponent } from '../suggestion-notifications/suggestion-targets/publication-claim/publication-claim.component'; import { SuggestionActionsComponent } from '../suggestion-notifications/suggestion-actions/suggestion-actions.component'; import { SuggestionListElementComponent @@ -65,7 +65,7 @@ const COMPONENTS = [ QualityAssuranceTopicsComponent, QualityAssuranceEventsComponent, QualityAssuranceSourceComponent, - SuggestionTargetsComponent, + PublicationClaimComponent, SuggestionActionsComponent, SuggestionListElementComponent, SuggestionEvidencesComponent, diff --git a/src/app/shared/eperson-group-list/eperson-group-list.component.ts b/src/app/shared/eperson-group-list/eperson-group-list.component.ts index 7cad7a97830..1a08740a946 100644 --- a/src/app/shared/eperson-group-list/eperson-group-list.component.ts +++ b/src/app/shared/eperson-group-list/eperson-group-list.component.ts @@ -96,7 +96,7 @@ export class EpersonGroupListComponent implements OnInit, OnDestroy { private pageConfigSub: Subscription; /** - * Initialize instance variables and inject the properly DataService + * Initialize instance variables and inject the properly UpdateDataServiceImpl * * @param {DSONameService} dsoNameService * @param {Injector} parentInjector diff --git a/src/app/shared/mydspace-actions/mydspace-actions-service.factory.ts b/src/app/shared/mydspace-actions/mydspace-actions-service.factory.ts index 6408dddac6e..987ca1411e1 100644 --- a/src/app/shared/mydspace-actions/mydspace-actions-service.factory.ts +++ b/src/app/shared/mydspace-actions/mydspace-actions-service.factory.ts @@ -13,7 +13,7 @@ import { CacheableObject } from '../../core/cache/cacheable-object.model'; import { IdentifiableDataService } from '../../core/data/base/identifiable-data.service'; /** - * Class to return DataService for given ResourceType + * Class to return UpdateDataServiceImpl for given ResourceType */ export class MyDSpaceActionsServiceFactory> { public getConstructor(type: ResourceType): TService { diff --git a/src/app/shared/mydspace-actions/mydspace-actions.ts b/src/app/shared/mydspace-actions/mydspace-actions.ts index 931929ccce5..184f059bd80 100644 --- a/src/app/shared/mydspace-actions/mydspace-actions.ts +++ b/src/app/shared/mydspace-actions/mydspace-actions.ts @@ -47,7 +47,7 @@ export abstract class MyDSpaceActionsComponent(false); /** - * Instance of DataService related to mydspace object + * Instance of UpdateDataServiceImpl related to mydspace object */ protected objectDataService: TService; diff --git a/src/app/suggestion-notifications/suggestion-actions/suggestion-actions.component.html b/src/app/suggestion-notifications/suggestion-actions/suggestion-actions.component.html index 24e970a8182..2a46191deea 100644 --- a/src/app/suggestion-notifications/suggestion-actions/suggestion-actions.component.html +++ b/src/app/suggestion-notifications/suggestion-actions/suggestion-actions.component.html @@ -23,7 +23,7 @@ {{ ignoreSuggestionLabel() | translate}}

diff --git a/src/app/suggestion-notifications/suggestion-targets/suggestion-targets.component.html b/src/app/suggestion-notifications/suggestion-targets/publication-claim/publication-claim.component.html similarity index 72% rename from src/app/suggestion-notifications/suggestion-targets/suggestion-targets.component.html rename to src/app/suggestion-notifications/suggestion-targets/publication-claim/publication-claim.component.html index 3d98082ba6e..45068cd12c8 100644 --- a/src/app/suggestion-notifications/suggestion-targets/suggestion-targets.component.html +++ b/src/app/suggestion-notifications/suggestion-targets/publication-claim/publication-claim.component.html @@ -1,9 +1,9 @@
-

{{'reciter.suggestion.title'| translate}}

+

{{'suggestion.title'| translate}}

- + {{'reciter.suggestion.title'| transla [retainScrollPosition]="false" (paginationChange)="getSuggestionTargets()"> - +
- - + + @@ -33,9 +33,9 @@

{{'reciter.suggestion.title'| transla
diff --git a/src/app/suggestion-notifications/suggestion-targets/suggestion-targets.component.scss b/src/app/suggestion-notifications/suggestion-targets/publication-claim/publication-claim.component.scss similarity index 100% rename from src/app/suggestion-notifications/suggestion-targets/suggestion-targets.component.scss rename to src/app/suggestion-notifications/suggestion-targets/publication-claim/publication-claim.component.scss diff --git a/src/app/suggestion-notifications/suggestion-targets/suggestion-targets.component.ts b/src/app/suggestion-notifications/suggestion-targets/publication-claim/publication-claim.component.ts similarity index 83% rename from src/app/suggestion-notifications/suggestion-targets/suggestion-targets.component.ts rename to src/app/suggestion-notifications/suggestion-targets/publication-claim/publication-claim.component.ts index e469ac13869..aef25f40756 100644 --- a/src/app/suggestion-notifications/suggestion-targets/suggestion-targets.component.ts +++ b/src/app/suggestion-notifications/suggestion-targets/publication-claim/publication-claim.component.ts @@ -4,23 +4,23 @@ import { Router } from '@angular/router'; import { Observable, Subscription } from 'rxjs'; import { distinctUntilChanged, take } from 'rxjs/operators'; -import { SuggestionTarget } from '../../core/suggestion-notifications/models/suggestion-target.model'; -import { hasValue } from '../../shared/empty.util'; -import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; -import { SuggestionTargetsStateService } from './suggestion-targets.state.service'; -import { getSuggestionPageRoute } from '../../suggestions-page/suggestions-page-routing-paths'; -import { SuggestionsService } from '../suggestions.service'; -import { PaginationService } from '../../core/pagination/pagination.service'; +import { SuggestionTarget } from '../../../core/suggestion-notifications/models/suggestion-target.model'; +import { hasValue } from '../../../shared/empty.util'; +import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; +import { SuggestionTargetsStateService } from '../suggestion-targets.state.service'; +import { getSuggestionPageRoute } from '../../../suggestions-page/suggestions-page-routing-paths'; +import { SuggestionsService } from '../../suggestions.service'; +import { PaginationService } from '../../../core/pagination/pagination.service'; /** * Component to display the Suggestion Target list. */ @Component({ - selector: 'ds-suggestion-target', - templateUrl: './suggestion-targets.component.html', - styleUrls: ['./suggestion-targets.component.scss'], + selector: 'ds-publication-claim', + templateUrl: './publication-claim.component.html', + styleUrls: ['./publication-claim.component.scss'], }) -export class SuggestionTargetsComponent implements OnInit { +export class PublicationClaimComponent implements OnInit { /** * The source for which to list targets diff --git a/src/app/suggestion-notifications/suggestion.service.spec.ts b/src/app/suggestion-notifications/suggestion.service.spec.ts index c7d0c7a4bf4..e6ca83d5fe3 100644 --- a/src/app/suggestion-notifications/suggestion.service.spec.ts +++ b/src/app/suggestion-notifications/suggestion.service.spec.ts @@ -156,7 +156,7 @@ describe('SuggestionsService test', () => { }); it('should delete suggestions', () => { - spyOn(service, 'notMine'); + spyOn(service, 'ignoreSuggestion'); service.ignoreSuggestionMultiple([mockSuggestionPublicationOne]); expect(service.ignoreSuggestion).toHaveBeenCalledWith(mockSuggestionPublicationOne.id); }); diff --git a/src/app/suggestion-notifications/suggestions-popup/suggestions-popup.component.ts b/src/app/suggestion-notifications/suggestions-popup/suggestions-popup.component.ts index 2eb5cec6dae..762f80085a8 100644 --- a/src/app/suggestion-notifications/suggestions-popup/suggestions-popup.component.ts +++ b/src/app/suggestion-notifications/suggestions-popup/suggestions-popup.component.ts @@ -18,7 +18,7 @@ import { combineLatest, Subject } from 'rxjs'; }) export class SuggestionsPopupComponent implements OnInit, OnDestroy { - labelPrefix = 'mydspace.'; + labelPrefix = 'notification.'; subscription; @@ -57,7 +57,7 @@ export class SuggestionsPopupComponent implements OnInit, OnDestroy { * @private */ private showNotificationForNewSuggestions(suggestionTarget: SuggestionTarget): void { - const content = this.translateService.instant(this.labelPrefix + 'notification.suggestion', + const content = this.translateService.instant(this.labelPrefix + 'suggestion', this.suggestionsService.getNotificationSuggestionInterpolation(suggestionTarget)); this.notificationsService.success('', content, {timeOut:0}, true); } diff --git a/src/app/suggestion-notifications/suggestions.service.ts b/src/app/suggestion-notifications/suggestions.service.ts index d4e8825f63e..47d4124a47e 100644 --- a/src/app/suggestion-notifications/suggestions.service.ts +++ b/src/app/suggestion-notifications/suggestions.service.ts @@ -23,7 +23,7 @@ import { NoContent } from '../core/shared/NoContent.model'; import { environment } from '../../environments/environment'; import { WorkspaceItem } from '../core/submission/models/workspaceitem.model'; import {FindListOptions} from '../core/data/find-list-options.model'; -import {SuggestionConfig} from '../../config/layout-config.interfaces'; +import {SuggestionConfig} from '../../config/suggestion-config.interfaces'; import { ResearcherProfileDataService } from '../core/profile/researcher-profile-data.service'; import { SuggestionSourceDataService @@ -229,7 +229,7 @@ export class SuggestionsService { } /** - * Perform a bulk notMine operation. + * Perform a bulk ignoreSuggestion operation. * @param suggestions the array containing the suggestions */ public ignoreSuggestionMultiple(suggestions: Suggestion[]): Observable { diff --git a/src/app/suggestions-page/suggestions-page.component.spec.ts b/src/app/suggestions-page/suggestions-page.component.spec.ts index 78cf0873628..2f6cfb7278f 100644 --- a/src/app/suggestions-page/suggestions-page.component.spec.ts +++ b/src/app/suggestions-page/suggestions-page.component.spec.ts @@ -41,7 +41,6 @@ describe('SuggestionPageComponent', () => { let scheduler: TestScheduler; const mockSuggestionsService = getMockSuggestionsService(); const mockSuggestionsTargetStateService = getMockSuggestionNotificationsStateService(); - const suggestionTargetsList: PaginatedList = buildPaginatedList(new PageInfo(), [mockSuggestionPublicationOne, mockSuggestionPublicationTwo]); const router = new RouterStub(); const routeStub = { data: observableOf({ @@ -139,7 +138,7 @@ describe('SuggestionPageComponent', () => { scheduler.schedule(() => fixture.detectChanges()); scheduler.flush(); component.ignoreSuggestion('1'); - expect(mockSuggestionsService.notMine).toHaveBeenCalledWith('1'); + expect(mockSuggestionsService.ignoreSuggestion).toHaveBeenCalledWith('1'); expect(mockSuggestionsTargetStateService.dispatchRefreshUserSuggestionsAction).toHaveBeenCalled(); expect(component.updatePage).toHaveBeenCalled(); }); @@ -150,7 +149,7 @@ describe('SuggestionPageComponent', () => { scheduler.schedule(() => fixture.detectChanges()); scheduler.flush(); component.ignoreSuggestionAllSelected(); - expect(mockSuggestionsService.notMineMultiple).toHaveBeenCalled(); + expect(mockSuggestionsService.ignoreSuggestionMultiple).toHaveBeenCalled(); expect(mockSuggestionsTargetStateService.dispatchRefreshUserSuggestionsAction).toHaveBeenCalled(); expect(component.updatePage).toHaveBeenCalled(); }); diff --git a/src/app/suggestions-page/suggestions-page.component.ts b/src/app/suggestions-page/suggestions-page.component.ts index 4883ed8406e..09051441f4a 100644 --- a/src/app/suggestions-page/suggestions-page.component.ts +++ b/src/app/suggestions-page/suggestions-page.component.ts @@ -28,6 +28,11 @@ import {redirectOn4xx} from '../core/shared/authorized.operators'; templateUrl: './suggestions-page.component.html', styleUrls: ['./suggestions-page.component.scss'], }) + +/** + * Component used to visualize one of the suggestions from the publication claim page or from the notification pop up + */ + export class SuggestionsPageComponent implements OnInit { /** @@ -139,15 +144,6 @@ export class SuggestionsPageComponent implements OnInit { this.processing$.next(false); this.suggestionsRD$.next(results); this.suggestionService.clearSuggestionRequests(); - // navigate to the mydspace if no suggestions remains - - // if (results.totalElements === 0) { - // const content = this.translateService.instant('reciter.suggestion.empty', - // this.suggestionService.getNotificationSuggestionInterpolation(this.suggestionTarget)); - // this.notificationService.success('', content, {timeOut:0}, true); - // TODO if the target is not the current use route to the suggestion target page - // this.router.navigate(['/mydspace']); - // } }); } diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 989d2ea9fbc..006fbdb5d49 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -3084,9 +3084,9 @@ "mydspace.import": "Import", - "mydspace.notification.suggestion": "We found {{count}} publications
in the {{source}} that seems to be related to your profile.
Please review the suggestions", + "notification.suggestion": "We found {{count}} publications
in the {{source}} that seems to be related to your profile.
Please review the suggestions", - "mydspace.notification.suggestion.page": "We found {{count}} {{type}} in the {{source}} that seems to be related to your profile. Please review the suggestions.", + "notification.suggestion.page": "We found {{count}} {{type}} in the {{source}} that seems to be related to your profile. Please review the suggestions.", "nav.browse.header": "All of DSpace", diff --git a/src/config/app-config.interface.ts b/src/config/app-config.interface.ts index f89623e0184..6c4b99cb0f4 100644 --- a/src/config/app-config.interface.ts +++ b/src/config/app-config.interface.ts @@ -14,7 +14,7 @@ import { AuthConfig } from './auth-config.interfaces'; import { UIServerConfig } from './ui-server-config.interface'; import { MediaViewerConfig } from './media-viewer-config.interface'; import { BrowseByConfig } from './browse-by-config.interface'; -import { SuggestionConfig } from './layout-config.interfaces'; +import { SuggestionConfig } from './suggestion-config.interfaces'; import { BundleConfig } from './bundle-config.interface'; import { ActuatorsConfig } from './actuators.config'; import { InfoConfig } from './info-config.interface'; @@ -23,7 +23,7 @@ import { HomeConfig } from './homepage-config.interface'; import { MarkdownConfig } from './markdown-config.interface'; import { FilterVocabularyConfig } from './filter-vocabulary-config'; import { DiscoverySortConfig } from './discovery-sort.config'; -import {QualityAssuranceConfig} from './quality-assurance.config'; +import { QualityAssuranceConfig } from './quality-assurance.config'; interface AppConfig extends Config { ui: UIServerConfig; diff --git a/src/config/default-app-config.ts b/src/config/default-app-config.ts index 902ab68dc3f..be9306c893c 100644 --- a/src/config/default-app-config.ts +++ b/src/config/default-app-config.ts @@ -14,7 +14,7 @@ import { ServerConfig } from './server-config.interface'; import { SubmissionConfig } from './submission-config.interface'; import { ThemeConfig } from './theme.config'; import { UIServerConfig } from './ui-server-config.interface'; -import {SuggestionConfig} from './layout-config.interfaces'; +import {SuggestionConfig} from './suggestion-config.interfaces'; import { BundleConfig } from './bundle-config.interface'; import { ActuatorsConfig } from './actuators.config'; import { InfoConfig } from './info-config.interface'; @@ -304,6 +304,8 @@ export class DefaultAppConfig implements AppConfig { // source: 'suggestionSource', // collectionId: 'collectionUUID' // } + // If not mapped the suggestion service won't be able to approve and import the related suggestion + // or load the fixed suggestions collections that can be configured here adding the source and the UUID of the collection ]; // Theme Config diff --git a/src/config/layout-config.interfaces.ts b/src/config/layout-config.interfaces.ts deleted file mode 100644 index 0b15a06aa92..00000000000 --- a/src/config/layout-config.interfaces.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { Config } from './config.interface'; - -export interface UrnConfig extends Config { - name: string; - baseUrl: string; -} - -export interface CrisRefConfig extends Config { - entityType: string; - icon: string; -} - -export interface CrisLayoutMetadataBoxConfig extends Config { - defaultMetadataLabelColStyle: string; - defaultMetadataValueColStyle: string; -} - -export interface CrisLayoutTypeConfig { - orientation: string; -} - -export interface NavbarConfig extends Config { - showCommunityCollection: boolean; -} - -export interface CrisItemPageConfig extends Config { - [entity: string]: CrisLayoutTypeConfig; - default: CrisLayoutTypeConfig; -} - - -export interface CrisLayoutConfig extends Config { - urn: UrnConfig[]; - crisRef: CrisRefConfig[]; - itemPage: CrisItemPageConfig; - metadataBox: CrisLayoutMetadataBoxConfig; -} - -export interface LayoutConfig extends Config { - navbar: NavbarConfig; -} - -export interface SuggestionConfig extends Config { - source: string; - collectionId: string; -} diff --git a/src/config/suggestion-config.interfaces.ts b/src/config/suggestion-config.interfaces.ts new file mode 100644 index 00000000000..e99ffa25f70 --- /dev/null +++ b/src/config/suggestion-config.interfaces.ts @@ -0,0 +1,6 @@ +import { Config } from "./config.interface"; + +export interface SuggestionConfig extends Config { + source: string; + collectionId: string; +} From d4bf3a519a352ef67dce884120a4c3f3efcb2289 Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Thu, 25 Jan 2024 13:22:17 +0100 Subject: [PATCH 17/34] refactor, clean up --- src/app/core/data/update-data-service.ts | 210 ++++-------------- .../suggestions-data.service.ts | 3 +- .../dso-edit-metadata.component.ts | 2 +- .../themed-dso-edit-metadata.component.ts | 2 +- src/app/menu.resolver.ts | 2 +- .../suggestion-actions.component.ts | 4 +- .../suggestion-targets.effects.ts | 4 +- .../suggestion.service.spec.ts | 8 +- .../suggestions.service.ts | 13 +- .../suggestions-page.component.spec.ts | 3 - .../suggestions-page.component.ts | 12 +- src/config/suggestion-config.interfaces.ts | 2 +- 12 files changed, 72 insertions(+), 193 deletions(-) diff --git a/src/app/core/data/update-data-service.ts b/src/app/core/data/update-data-service.ts index 994cd0ee87b..9a31067f88c 100644 --- a/src/app/core/data/update-data-service.ts +++ b/src/app/core/data/update-data-service.ts @@ -1,84 +1,85 @@ import { HttpClient } from '@angular/common/http'; import { Store } from '@ngrx/store'; import { Operation } from 'fast-json-patch'; -import { AsyncSubject, combineLatest, from as observableFrom, Observable, of as observableOf } from 'rxjs'; +import { AsyncSubject, from as observableFrom, Observable } from 'rxjs'; import { - distinctUntilChanged, - filter, find, map, mergeMap, - skipWhile, switchMap, take, - takeWhile, - tap, toArray } from 'rxjs/operators'; -import { hasValue, isNotEmpty, isNotEmptyOperator } from '../../shared/empty.util'; -import { NotificationOptions } from '../../shared/notifications/models/notification-options.model'; +import { hasValue } from '../../shared/empty.util'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; -import { getClassForType } from '../cache/builders/build-decorators'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RequestParam } from '../cache/models/request-param.model'; import { ObjectCacheEntry } from '../cache/object-cache.reducer'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { DSpaceSerializer } from '../dspace-rest/dspace.serializer'; import { HALEndpointService } from '../shared/hal-endpoint.service'; -import { getFirstCompletedRemoteData, getFirstSucceededRemoteData, getRemoteDataPayload } from '../shared/operators'; -import { URLCombiner } from '../url-combiner/url-combiner'; import { ChangeAnalyzer } from './change-analyzer'; import { PaginatedList } from './paginated-list.model'; import { RemoteData } from './remote-data'; import { - CreateRequest, DeleteByIDRequest, - DeleteRequest, - GetRequest, - PatchRequest, - PostRequest, - PutRequest + PostRequest } from './request.models'; import { RequestService } from './request.service'; import { RestRequestMethod } from './rest-request-method'; -import { GenericConstructor } from '../shared/generic-constructor'; import { NoContent } from '../shared/NoContent.model'; import { CacheableObject } from '../cache/cacheable-object.model'; import { CoreState } from '../core-state.model'; import { FindListOptions } from './find-list-options.model'; -import { BaseDataService } from "./base/base-data.service"; -import { FindAllData, FindAllDataImpl } from "./base/find-all-data"; -import { SearchData, SearchDataImpl } from "./base/search-data"; -import { CreateData, CreateDataImpl } from "./base/create-data"; +import { FindAllData, FindAllDataImpl } from './base/find-all-data'; +import { SearchData, SearchDataImpl } from './base/search-data'; +import { CreateData, CreateDataImpl } from './base/create-data'; +import { PatchData, PatchDataImpl } from './base/patch-data'; +import { IdentifiableDataService } from './base/identifiable-data.service'; +import { PutData, PutDataImpl } from './base/put-data'; +import { DeleteData, DeleteDataImpl } from './base/delete-data'; + +/** + * Interface to list the methods used by the injected service in components + */ export interface UpdateDataService { patch(dso: T, operations: Operation[]): Observable>; update(object: T): Observable>; - commitUpdates(method?: RestRequestMethod); + commitUpdates(method?: RestRequestMethod): void; } /** * Specific functionalities that not all services would need. * Goal of the class is to update remote objects, handling custom methods that don't belong to BaseDataService + * The class implements also the following common interfaces + * + * findAllData: FindAllData; + * searchData: SearchData; + * createData: CreateData; + * patchData: PatchData; + * putData: PutData; + * deleteData: DeleteData; * * Custom methods are: * - * patch - Sends a patch request to modify current object - * update - Add a new patch to the object cache, diff between given object and cache - * commitUpdates - Sends the updates to the server + * deleteOnRelated - delete all related objects to the given one + * postOnRelated - post all the related objects to the given one + * invalidate - invalidate the DSpaceObject making all requests as stale + * invalidateByHref - invalidate the href making all requests as stale */ - -export abstract class UpdateDataServiceImpl extends BaseDataService implements UpdateDataService, FindAllData, SearchData, CreateData { +export abstract class UpdateDataServiceImpl extends IdentifiableDataService implements FindAllData, SearchData, CreateData, PatchData, PutData, DeleteData { protected abstract store: Store; protected abstract http: HttpClient; - protected abstract comparator: ChangeAnalyzer; private findAllData: FindAllDataImpl; private searchData: SearchDataImpl; - private createData: CreateData; + private createData: CreateDataImpl; + private patchData: PatchDataImpl; + private putData: PutDataImpl; + private deleteData: DeleteDataImpl; constructor( @@ -88,12 +89,16 @@ export abstract class UpdateDataServiceImpl extends B protected objectCache: ObjectCacheService, protected halService: HALEndpointService, protected notificationsService: NotificationsService, + protected comparator: ChangeAnalyzer, protected responseMsToLive: number, ) { super(linkPath, requestService, rdbService, objectCache, halService, responseMsToLive); this.findAllData = new FindAllDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive); this.searchData = new SearchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive); this.createData = new CreateDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService ,this.responseMsToLive); + this.patchData = new PatchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, comparator ,this.responseMsToLive, this.constructIdEndpoint); + this.putData = new PutDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive); + this.deleteData = new DeleteDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService ,this.responseMsToLive, this.constructIdEndpoint); } @@ -107,7 +112,7 @@ export abstract class UpdateDataServiceImpl extends B * @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved */ public getFindAllHref(options: FindListOptions = {}, linkPath?: string, ...linksToFollow: FollowLinkConfig[]): Observable { - return this.findAllData.getFindAllHref(options, linkPath, ...linksToFollow) + return this.findAllData.getFindAllHref(options, linkPath, ...linksToFollow); } /** @@ -120,7 +125,7 @@ export abstract class UpdateDataServiceImpl extends B * @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved */ public getSearchByHref(searchMethod: string, options: FindListOptions = {}, ...linksToFollow: FollowLinkConfig[]): Observable { - return this.searchData.getSearchByHref(searchMethod, options, ...linksToFollow) + return this.searchData.getSearchByHref(searchMethod, options, ...linksToFollow); } /** @@ -138,27 +143,7 @@ export abstract class UpdateDataServiceImpl extends B * Return an observable that emits object list */ findAll(options: FindListOptions = {}, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig[]): Observable>> { - return this.findAllData.findAll(options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow) - } - - /** - * Create the HREF for a specific object based on its identifier; with possible embed query params based on linksToFollow - * @param endpoint The base endpoint for the type of object - * @param resourceID The identifier for the object - * @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved - */ - getIDHref(endpoint, resourceID, ...linksToFollow: FollowLinkConfig[]): string { - return this.buildHrefFromFindOptions(endpoint + '/' + resourceID, {}, [], ...linksToFollow); - } - - /** - * Create an observable for the HREF of a specific object based on its identifier - * @param resourceID The identifier for the object - * @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved - */ - getIDHrefObs(resourceID: string, ...linksToFollow: FollowLinkConfig[]): Observable { - return this.getEndpoint().pipe( - map((endpoint: string) => this.getIDHref(endpoint, resourceID, ...linksToFollow))); + return this.findAllData.findAll(options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow); } /** @@ -177,29 +162,6 @@ export abstract class UpdateDataServiceImpl extends B return this.findByHref(href$, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow); } - /** - * An operator that will call the given function if the incoming RemoteData is stale and - * shouldReRequest is true - * - * @param shouldReRequest Whether or not to call the re-request function if the RemoteData is stale - * @param requestFn The function to call if the RemoteData is stale and shouldReRequest is - * true - */ - protected reRequestStaleRemoteData(shouldReRequest: boolean, requestFn: () => Observable>) { - return (source: Observable>): Observable> => { - if (shouldReRequest === true) { - return source.pipe( - tap((remoteData: RemoteData) => { - if (hasValue(remoteData) && remoteData.isStale) { - requestFn(); - } - }) - ); - } else { - return source; - } - }; - } /** * Make a new FindListRequest with given search method @@ -216,7 +178,7 @@ export abstract class UpdateDataServiceImpl extends B * Return an observable that emits response from the server */ searchBy(searchMethod: string, options: FindListOptions = {}, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig[]): Observable>> { - return this.searchData.searchBy(searchMethod, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow) + return this.searchData.searchBy(searchMethod, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow); } /** @@ -225,30 +187,11 @@ export abstract class UpdateDataServiceImpl extends B * @param {Operation[]} operations The patch operations to be performed */ patch(object: T, operations: Operation[]): Observable> { - const requestId = this.requestService.generateRequestId(); - - const hrefObs = this.halService.getEndpoint(this.linkPath).pipe( - map((endpoint: string) => this.getIDHref(endpoint, object.uuid))); - - hrefObs.pipe( - find((href: string) => hasValue(href)), - ).subscribe((href: string) => { - const request = new PatchRequest(requestId, href, operations); - if (hasValue(this.responseMsToLive)) { - request.responseMsToLive = this.responseMsToLive; - } - this.requestService.send(request); - }); - - return this.rdbService.buildFromRequestUUID(requestId); + return this.patchData.patch(object, operations); } createPatchFromCache(object: T): Observable { - const oldVersion$ = this.findByHref(object._links.self.href, true, false); - return oldVersion$.pipe( - getFirstSucceededRemoteData(), - getRemoteDataPayload(), - map((oldVersion: T) => this.comparator.diff(oldVersion, object))); + return this.patchData.createPatchFromCache(object); } /** @@ -257,17 +200,7 @@ export abstract class UpdateDataServiceImpl extends B * @param object The object to send a put request for. */ put(object: T): Observable> { - const requestId = this.requestService.generateRequestId(); - const serializedObject = new DSpaceSerializer(object.constructor as GenericConstructor<{}>).serialize(object); - const request = new PutRequest(requestId, object._links.self.href, serializedObject); - - if (hasValue(this.responseMsToLive)) { - request.responseMsToLive = this.responseMsToLive; - } - - this.requestService.send(request); - - return this.rdbService.buildFromRequestUUID(requestId); + return this.putData.put(object); } /** @@ -276,16 +209,7 @@ export abstract class UpdateDataServiceImpl extends B * @param {DSpaceObject} object The given object */ update(object: T): Observable> { - return this.createPatchFromCache(object) - .pipe( - mergeMap((operations: Operation[]) => { - if (isNotEmpty(operations)) { - this.objectCache.addPatch(object._links.self.href, operations); - } - return this.findByHref(object._links.self.href, true, true); - } - ) - ); + return this.patchData.update(object); } /** @@ -298,7 +222,7 @@ export abstract class UpdateDataServiceImpl extends B * Array with additional params to combine with query string */ create(object: T, ...params: RequestParam[]): Observable> { - return this.createData.create(object, ...params) + return this.createData.create(object, ...params); } /** @@ -390,9 +314,7 @@ export abstract class UpdateDataServiceImpl extends B * errorMessage, timeCompleted, etc */ delete(objectId: string, copyVirtualMetadata?: string[]): Observable> { - return this.getIDHrefObs(objectId).pipe( - switchMap((href: string) => this.deleteByHref(href, copyVirtualMetadata)) - ); + return this.deleteData.delete(objectId, copyVirtualMetadata); } /** @@ -405,43 +327,7 @@ export abstract class UpdateDataServiceImpl extends B * Only emits once all request related to the DSO has been invalidated. */ deleteByHref(href: string, copyVirtualMetadata?: string[]): Observable> { - const requestId = this.requestService.generateRequestId(); - - if (copyVirtualMetadata) { - copyVirtualMetadata.forEach((id) => - href += (href.includes('?') ? '&' : '?') - + 'copyVirtualMetadata=' - + id - ); - } - - const request = new DeleteRequest(requestId, href); - if (hasValue(this.responseMsToLive)) { - request.responseMsToLive = this.responseMsToLive; - } - this.requestService.send(request); - - const response$ = this.rdbService.buildFromRequestUUID(requestId); - - const invalidated$ = new AsyncSubject(); - response$.pipe( - getFirstCompletedRemoteData(), - switchMap((rd: RemoteData) => { - if (rd.hasSucceeded) { - return this.invalidateByHref(href); - } else { - return [true]; - } - }) - ).subscribe(() => { - invalidated$.next(true); - invalidated$.complete(); - }); - - return combineLatest([response$, invalidated$]).pipe( - filter(([_, invalidated]) => invalidated), - map(([response, _]) => response), - ); + return this.deleteData.deleteByHref(href, copyVirtualMetadata); } /** @@ -449,6 +335,6 @@ export abstract class UpdateDataServiceImpl extends B * @param method The RestRequestMethod for which de server sync buffer should be committed */ commitUpdates(method?: RestRequestMethod) { - this.requestService.commit(method); + this.patchData.commitUpdates(method); } } diff --git a/src/app/core/suggestion-notifications/suggestions-data.service.ts b/src/app/core/suggestion-notifications/suggestions-data.service.ts index 945cc8b083f..2078fdb75e2 100644 --- a/src/app/core/suggestion-notifications/suggestions-data.service.ts +++ b/src/app/core/suggestion-notifications/suggestions-data.service.ts @@ -58,7 +58,7 @@ export class SuggestionDataServiceImpl extends UpdateDataServiceImpl protected comparator: ChangeAnalyzer, protected responseMsToLive: number, ) { - super('suggestions', requestService, rdbService, objectCache, halService, notificationsService ,responseMsToLive); + super('suggestions', requestService, rdbService, objectCache, halService, notificationsService, comparator ,responseMsToLive); } } @@ -99,7 +99,6 @@ export class SuggestionsDataService { * @param {DefaultChangeAnalyzer} comparatorSuggestions * @param {DefaultChangeAnalyzer} comparatorSources * @param {DefaultChangeAnalyzer} comparatorTargets - * @param responseMsToLive */ constructor( protected requestService: RequestService, diff --git a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.ts b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.ts index ad3d6680a17..22d23e84020 100644 --- a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.ts +++ b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.ts @@ -21,7 +21,7 @@ import { ArrayMoveChangeAnalyzer } from '../../core/data/array-move-change-analy import { DATA_SERVICE_FACTORY } from '../../core/data/base/data-service.decorator'; import { GenericConstructor } from '../../core/shared/generic-constructor'; import { HALDataService } from '../../core/data/base/hal-data-service.interface'; -import { UpdateDataService } from "../../core/data/update-data-service"; +import { UpdateDataService } from '../../core/data/update-data-service'; @Component({ selector: 'ds-dso-edit-metadata', diff --git a/src/app/dso-shared/dso-edit-metadata/themed-dso-edit-metadata.component.ts b/src/app/dso-shared/dso-edit-metadata/themed-dso-edit-metadata.component.ts index 51eec3ac230..5d7ccdd87d2 100644 --- a/src/app/dso-shared/dso-edit-metadata/themed-dso-edit-metadata.component.ts +++ b/src/app/dso-shared/dso-edit-metadata/themed-dso-edit-metadata.component.ts @@ -2,7 +2,7 @@ import { ThemedComponent } from '../../shared/theme-support/themed.component'; import { DsoEditMetadataComponent } from './dso-edit-metadata.component'; import { Component, Input } from '@angular/core'; import { DSpaceObject } from '../../core/shared/dspace-object.model'; -import { UpdateDataService } from "../../core/data/update-data-service"; +import { UpdateDataService } from '../../core/data/update-data-service'; @Component({ selector: 'ds-themed-dso-edit-metadata', diff --git a/src/app/menu.resolver.ts b/src/app/menu.resolver.ts index f2a7e2dbe19..fc6eb00195c 100644 --- a/src/app/menu.resolver.ts +++ b/src/app/menu.resolver.ts @@ -541,7 +541,7 @@ export class MenuResolver implements Resolve { { id: 'notifications', active: false, - visible: authorized && true, + visible: authorized && canSeeQA, model: { type: MenuItemType.TEXT, text: 'menu.section.notifications' diff --git a/src/app/suggestion-notifications/suggestion-actions/suggestion-actions.component.ts b/src/app/suggestion-notifications/suggestion-actions/suggestion-actions.component.ts index 5a4c6ebe3f9..f6e2452738e 100644 --- a/src/app/suggestion-notifications/suggestion-actions/suggestion-actions.component.ts +++ b/src/app/suggestion-notifications/suggestion-actions/suggestion-actions.component.ts @@ -86,10 +86,10 @@ export class SuggestionActionsComponent { } ignoreSuggestionLabel(): string { - return this.isBulk ? 'reciter.suggestion.ignoreSuggestion.bulk' : 'reciter.suggestion.ignoreSuggestion' ; + return this.isBulk ? 'suggestion.ignoreSuggestion.bulk' : 'suggestion.ignoreSuggestion' ; } approveAndImportLabel(): string { - return this.isBulk ? 'reciter.suggestion.approveAndImport.bulk' : 'reciter.suggestion.approveAndImport'; + return this.isBulk ? 'suggestion.approveAndImport.bulk' : 'suggestion.approveAndImport'; } } diff --git a/src/app/suggestion-notifications/suggestion-targets/suggestion-targets.effects.ts b/src/app/suggestion-notifications/suggestion-targets/suggestion-targets.effects.ts index 63964b77214..7a431bf4c2c 100644 --- a/src/app/suggestion-notifications/suggestion-targets/suggestion-targets.effects.ts +++ b/src/app/suggestion-notifications/suggestion-targets/suggestion-targets.effects.ts @@ -55,7 +55,7 @@ export class SuggestionTargetsEffects { retrieveAllTargetsErrorAction$ = createEffect(() => this.actions$.pipe( ofType(SuggestionTargetActionTypes.RETRIEVE_TARGETS_BY_SOURCE_ERROR), tap(() => { - this.notificationsService.error(null, this.translate.get('reciter.suggestion.target.error.service.retrieve')); + this.notificationsService.error(null, this.translate.get('suggestion.target.error.service.retrieve')); }) ), { dispatch: false }); @@ -64,7 +64,7 @@ export class SuggestionTargetsEffects { */ refreshUserSuggestionsAction$ = createEffect(() => this.actions$.pipe( ofType(SuggestionTargetActionTypes.REFRESH_USER_SUGGESTIONS), - switchMap((action: RefreshUserSuggestionsAction) => { + switchMap(() => { return this.store$.select((state: any) => state.core.auth.userId) .pipe( switchMap((userId: string) => { diff --git a/src/app/suggestion-notifications/suggestion.service.spec.ts b/src/app/suggestion-notifications/suggestion.service.spec.ts index e6ca83d5fe3..ad2cb8fcc4a 100644 --- a/src/app/suggestion-notifications/suggestion.service.spec.ts +++ b/src/app/suggestion-notifications/suggestion.service.spec.ts @@ -169,18 +169,18 @@ describe('SuggestionsService test', () => { it('should get suggestion interpolation', () => { const result = service.getNotificationSuggestionInterpolation(suggestionTarget as SuggestionTarget); expect(result.count).toEqual(suggestionTarget.total); - expect(result.source).toEqual('reciter.suggestion.source.' + suggestionTarget.source); - expect(result.type).toEqual('reciter.suggestion.type.' + suggestionTarget.source); + expect(result.source).toEqual('suggestion.source.' + suggestionTarget.source); + expect(result.type).toEqual('suggestion.type.' + suggestionTarget.source); expect(result.suggestionId).toEqual(suggestionTarget.id); expect(result.displayName).toEqual(suggestionTarget.display); }); it('should translate suggestion type', () => { - expect(service.translateSuggestionType('source')).toEqual('reciter.suggestion.type.source'); + expect(service.translateSuggestionType('source')).toEqual('suggestion.type.source'); }); it('should translate suggestion source', () => { - expect(service.translateSuggestionSource('source')).toEqual('reciter.suggestion.source.source'); + expect(service.translateSuggestionSource('source')).toEqual('suggestion.source.source'); }); it('should resolve collection id', () => { diff --git a/src/app/suggestion-notifications/suggestions.service.ts b/src/app/suggestion-notifications/suggestions.service.ts index 47d4124a47e..599215d5297 100644 --- a/src/app/suggestion-notifications/suggestions.service.ts +++ b/src/app/suggestion-notifications/suggestions.service.ts @@ -51,17 +51,14 @@ export class SuggestionsService { /** * Initialize the service variables. - * @param {AuthService} authService * @param {ResearcherProfileDataService} researcherProfileService - * @param {SuggestionSourceDataService} suggestionSourceDataService * @param {SuggestionTargetDataService} suggestionTargetDataService * @param {SuggestionsDataService} suggestionsDataService + * @param translateService */ constructor( - private authService: AuthService, private researcherProfileService: ResearcherProfileDataService, private suggestionsDataService: SuggestionsDataService, - private suggestionSourceDataService: SuggestionSourceDataService, private suggestionTargetDataService: SuggestionTargetDataService, private translateService: TranslateService ) { @@ -194,7 +191,7 @@ export class SuggestionsService { return workspaceitemService.importExternalSourceEntry(suggestion.externalSourceUri, resolvedCollectionId) .pipe( getFirstSucceededRemoteDataPayload(), - catchError((error) => of(null)) + catchError(() => of(null)) ); } @@ -204,7 +201,7 @@ export class SuggestionsService { */ public ignoreSuggestion(suggestionId): Observable> { return this.deleteReviewedSuggestion(suggestionId).pipe( - catchError((error) => of(null)) + catchError(() => of(null)) ); } @@ -268,11 +265,11 @@ export class SuggestionsService { } public translateSuggestionType(source: string): string { - return 'reciter.suggestion.type.' + source; + return 'suggestion.type.' + source; } public translateSuggestionSource(source: string): string { - return 'reciter.suggestion.source.' + source; + return 'suggestion.source.' + source; } /** diff --git a/src/app/suggestions-page/suggestions-page.component.spec.ts b/src/app/suggestions-page/suggestions-page.component.spec.ts index 2f6cfb7278f..6abd28386f1 100644 --- a/src/app/suggestions-page/suggestions-page.component.spec.ts +++ b/src/app/suggestions-page/suggestions-page.component.spec.ts @@ -13,8 +13,6 @@ import { } from '../suggestion-notifications/suggestion-list-element/suggestion-list-element.component'; import { SuggestionsService } from '../suggestion-notifications/suggestions.service'; import { getMockSuggestionNotificationsStateService, getMockSuggestionsService } from '../shared/mocks/suggestion.mock'; -import { buildPaginatedList, PaginatedList } from '../core/data/paginated-list.model'; -import { Suggestion } from '../core/suggestion-notifications/models/suggestion.model'; import { mockSuggestionPublicationOne, mockSuggestionPublicationTwo } from '../shared/mocks/publication-claim.mock'; import { SuggestionEvidencesComponent } from '../suggestion-notifications/suggestion-list-element/suggestion-evidences/suggestion-evidences.component'; import { ObjectKeysPipe } from '../shared/utils/object-keys-pipe'; @@ -29,7 +27,6 @@ import { getMockTranslateService } from '../shared/mocks/translate.service.mock' import { SuggestionTargetsStateService } from '../suggestion-notifications/suggestion-targets/suggestion-targets.state.service'; import { WorkspaceitemDataService } from '../core/submission/workspaceitem-data.service'; import { createSuccessfulRemoteDataObject } from '../shared/remote-data.utils'; -import { PageInfo } from '../core/shared/page-info.model'; import { TestScheduler } from 'rxjs/testing'; import { getTestScheduler } from 'jasmine-marbles'; import { PaginationServiceStub } from '../shared/testing/pagination-service.stub'; diff --git a/src/app/suggestions-page/suggestions-page.component.ts b/src/app/suggestions-page/suggestions-page.component.ts index 09051441f4a..48245b60b3b 100644 --- a/src/app/suggestions-page/suggestions-page.component.ts +++ b/src/app/suggestions-page/suggestions-page.component.ts @@ -152,7 +152,7 @@ export class SuggestionsPageComponent implements OnInit { * @suggestionId */ ignoreSuggestion(suggestionId) { - this.suggestionService.ignoreSuggestion(suggestionId).subscribe((res) => { + this.suggestionService.ignoreSuggestion(suggestionId).subscribe(() => { this.suggestionTargetsStateService.dispatchRefreshUserSuggestionsAction(); this.updatePage(); }); @@ -172,12 +172,12 @@ export class SuggestionsPageComponent implements OnInit { this.selectedSuggestions = {}; if (results.success > 0) { this.notificationService.success( - this.translateService.get('reciter.suggestion.notMine.bulk.success', + this.translateService.get('suggestion.notMine.bulk.success', {count: results.success})); } if (results.fails > 0) { this.notificationService.error( - this.translateService.get('reciter.suggestion.notMine.bulk.error', + this.translateService.get('suggestion.notMine.bulk.error', {count: results.fails})); } }); @@ -190,7 +190,7 @@ export class SuggestionsPageComponent implements OnInit { approveAndImport(event: SuggestionApproveAndImport) { this.suggestionService.approveAndImport(this.workspaceItemService, event.suggestion, event.collectionId) .subscribe((workspaceitem: WorkspaceItem) => { - const content = this.translateService.instant('reciter.suggestion.approveAndImport.success', { workspaceItemId: workspaceitem.id }); + const content = this.translateService.instant('suggestion.approveAndImport.success', { workspaceItemId: workspaceitem.id }); this.notificationService.success('', content, {timeOut:0}, true); this.suggestionTargetsStateService.dispatchRefreshUserSuggestionsAction(); this.updatePage(); @@ -212,12 +212,12 @@ export class SuggestionsPageComponent implements OnInit { this.selectedSuggestions = {}; if (results.success > 0) { this.notificationService.success( - this.translateService.get('reciter.suggestion.approveAndImport.bulk.success', + this.translateService.get('suggestion.approveAndImport.bulk.success', {count: results.success})); } if (results.fails > 0) { this.notificationService.error( - this.translateService.get('reciter.suggestion.approveAndImport.bulk.error', + this.translateService.get('suggestion.approveAndImport.bulk.error', {count: results.fails})); } }); diff --git a/src/config/suggestion-config.interfaces.ts b/src/config/suggestion-config.interfaces.ts index e99ffa25f70..afd3a38c581 100644 --- a/src/config/suggestion-config.interfaces.ts +++ b/src/config/suggestion-config.interfaces.ts @@ -1,4 +1,4 @@ -import { Config } from "./config.interface"; +import { Config } from './config.interface'; export interface SuggestionConfig extends Config { source: string; From 4cca9015a1fab16d78ad689c5024b7782a2af7ad Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Thu, 25 Jan 2024 13:23:01 +0100 Subject: [PATCH 18/34] remove unused imports --- .../suggestion-targets/suggestion-targets.effects.ts | 1 - src/app/suggestion-notifications/suggestions.service.ts | 4 ---- 2 files changed, 5 deletions(-) diff --git a/src/app/suggestion-notifications/suggestion-targets/suggestion-targets.effects.ts b/src/app/suggestion-notifications/suggestion-targets/suggestion-targets.effects.ts index 7a431bf4c2c..1358e6f8b54 100644 --- a/src/app/suggestion-notifications/suggestion-targets/suggestion-targets.effects.ts +++ b/src/app/suggestion-notifications/suggestion-targets/suggestion-targets.effects.ts @@ -9,7 +9,6 @@ import { of } from 'rxjs'; import { AddTargetAction, AddUserSuggestionsAction, - RefreshUserSuggestionsAction, RetrieveAllTargetsErrorAction, RetrieveTargetsBySourceAction, SuggestionTargetActionTypes, diff --git a/src/app/suggestion-notifications/suggestions.service.ts b/src/app/suggestion-notifications/suggestions.service.ts index 599215d5297..bf21b5ae2f4 100644 --- a/src/app/suggestion-notifications/suggestions.service.ts +++ b/src/app/suggestion-notifications/suggestions.service.ts @@ -7,7 +7,6 @@ import { SortDirection, SortOptions } from '../core/cache/models/sort-options.mo import { RemoteData } from '../core/data/remote-data'; import { PaginatedList } from '../core/data/paginated-list.model'; import { SuggestionTarget } from '../core/suggestion-notifications/models/suggestion-target.model'; -import { AuthService } from '../core/auth/auth.service'; import { hasValue, isNotEmpty } from '../shared/empty.util'; import { ResearcherProfile } from '../core/profile/model/researcher-profile.model'; import { @@ -25,9 +24,6 @@ import { WorkspaceItem } from '../core/submission/models/workspaceitem.model'; import {FindListOptions} from '../core/data/find-list-options.model'; import {SuggestionConfig} from '../../config/suggestion-config.interfaces'; import { ResearcherProfileDataService } from '../core/profile/researcher-profile-data.service'; -import { - SuggestionSourceDataService -} from '../core/suggestion-notifications/source/suggestion-source-data.service'; import { SuggestionTargetDataService } from '../core/suggestion-notifications/target/suggestion-target-data.service'; From e77343604e2288c0310c7c1565fce47e781cf11a Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Thu, 25 Jan 2024 13:50:23 +0100 Subject: [PATCH 19/34] refactor and add tests --- src/app/core/data/update-data-service.spec.ts | 144 ++++++++++++++++++ src/app/core/data/update-data-service.ts | 25 +-- .../suggestion.service.spec.ts | 15 +- 3 files changed, 147 insertions(+), 37 deletions(-) create mode 100644 src/app/core/data/update-data-service.spec.ts diff --git a/src/app/core/data/update-data-service.spec.ts b/src/app/core/data/update-data-service.spec.ts new file mode 100644 index 00000000000..6912527b918 --- /dev/null +++ b/src/app/core/data/update-data-service.spec.ts @@ -0,0 +1,144 @@ +import { of as observableOf } from 'rxjs'; +import { TestScheduler } from 'rxjs/testing'; +import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; +import { ObjectCacheService } from '../cache/object-cache.service'; +import { HALEndpointService } from '../shared/hal-endpoint.service'; +import { RequestService } from './request.service'; +import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; +import { HrefOnlyDataService } from './href-only-data.service'; +import { getMockHrefOnlyDataService } from '../../shared/mocks/href-only-data.service.mock'; +import { RestResponse } from '../cache/response.models'; +import { cold, getTestScheduler, hot } from 'jasmine-marbles'; +import { Item } from '../shared/item.model'; +import { Version } from '../shared/version.model'; +import { VersionHistory } from '../shared/version-history.model'; +import { RequestEntry } from './request-entry.model'; +import { testPatchDataImplementation } from './base/patch-data.spec'; +import { UpdateDataServiceImpl } from './update-data-service'; +import { testSearchDataImplementation } from './base/search-data.spec'; +import { testDeleteDataImplementation } from './base/delete-data.spec'; +import { testCreateDataImplementation } from './base/create-data.spec'; +import { testFindAllDataImplementation } from './base/find-all-data.spec'; +import { testPutDataImplementation } from './base/put-data.spec'; +import { NotificationsService } from '../../shared/notifications/notifications.service'; + +describe('VersionDataService test', () => { + let scheduler: TestScheduler; + let service: UpdateDataServiceImpl; + let requestService: RequestService; + let rdbService: RemoteDataBuildService; + let objectCache: ObjectCacheService; + let halService: HALEndpointService; + let hrefOnlyDataService: HrefOnlyDataService; + let responseCacheEntry: RequestEntry; + + const notificationsService = {} as NotificationsService; + + const item = Object.assign(new Item(), { + id: '1234-1234', + uuid: '1234-1234', + bundles: observableOf({}), + metadata: { + 'dc.title': [ + { + language: 'en_US', + value: 'This is just another title' + } + ], + 'dc.type': [ + { + language: null, + value: 'Article' + } + ], + 'dc.contributor.author': [ + { + language: 'en_US', + value: 'Smith, Donald' + } + ], + 'dc.date.issued': [ + { + language: null, + value: '2015-06-26' + } + ] + } + }); + + const versionHistory = Object.assign(new VersionHistory(), { + id: '1', + draftVersion: true, + }); + + const mockVersion: Version = Object.assign(new Version(), { + item: createSuccessfulRemoteDataObject$(item), + versionhistory: createSuccessfulRemoteDataObject$(versionHistory), + version: 1, + }); + const mockVersionRD = createSuccessfulRemoteDataObject(mockVersion); + + const endpointURL = `https://rest.api/rest/api/versioning/versions`; + const requestUUID = '8b3c613a-5a4b-438b-9686-be1d5b4a1c5a'; + + objectCache = {} as ObjectCacheService; + const comparatorEntry = {} as any; + function initTestService() { + hrefOnlyDataService = getMockHrefOnlyDataService(); + return new UpdateDataServiceImpl( + 'testLinkPath', + requestService, + rdbService, + objectCache, + halService, + notificationsService, + comparatorEntry, + 10 * 1000 + ); + } + + beforeEach(() => { + + scheduler = getTestScheduler(); + + halService = jasmine.createSpyObj('halService', { + getEndpoint: cold('a', { a: endpointURL }) + }); + responseCacheEntry = new RequestEntry(); + responseCacheEntry.request = { href: 'https://rest.api/' } as any; + responseCacheEntry.response = new RestResponse(true, 200, 'Success'); + + requestService = jasmine.createSpyObj('requestService', { + generateRequestId: requestUUID, + send: true, + removeByHrefSubstring: {}, + getByHref: observableOf(responseCacheEntry), + getByUUID: observableOf(responseCacheEntry), + }); + rdbService = jasmine.createSpyObj('rdbService', { + buildSingle: hot('(a|)', { + a: mockVersionRD + }) + }); + + service = initTestService(); + + spyOn((service as any), 'findById').and.callThrough(); + }); + + afterEach(() => { + service = null; + }); + + describe('composition', () => { + const initService = () => new UpdateDataServiceImpl(null, null, null, null, null, null, null, null); + + testPatchDataImplementation(initService); + testSearchDataImplementation(initService); + testDeleteDataImplementation(initService); + testCreateDataImplementation(initService); + testFindAllDataImplementation(initService); + testPutDataImplementation(initService); + }); + +}); diff --git a/src/app/core/data/update-data-service.ts b/src/app/core/data/update-data-service.ts index 9a31067f88c..715b2ee413c 100644 --- a/src/app/core/data/update-data-service.ts +++ b/src/app/core/data/update-data-service.ts @@ -1,5 +1,3 @@ -import { HttpClient } from '@angular/common/http'; -import { Store } from '@ngrx/store'; import { Operation } from 'fast-json-patch'; import { AsyncSubject, from as observableFrom, Observable } from 'rxjs'; import { @@ -29,7 +27,6 @@ import { RequestService } from './request.service'; import { RestRequestMethod } from './rest-request-method'; import { NoContent } from '../shared/NoContent.model'; import { CacheableObject } from '../cache/cacheable-object.model'; -import { CoreState } from '../core-state.model'; import { FindListOptions } from './find-list-options.model'; import { FindAllData, FindAllDataImpl } from './base/find-all-data'; import { SearchData, SearchDataImpl } from './base/search-data'; @@ -70,10 +67,7 @@ export interface UpdateDataService { * invalidateByHref - invalidate the href making all requests as stale */ -export abstract class UpdateDataServiceImpl extends IdentifiableDataService implements FindAllData, SearchData, CreateData, PatchData, PutData, DeleteData { - protected abstract store: Store; - protected abstract http: HttpClient; - +export class UpdateDataServiceImpl extends IdentifiableDataService implements FindAllData, SearchData, CreateData, PatchData, PutData, DeleteData { private findAllData: FindAllDataImpl; private searchData: SearchDataImpl; private createData: CreateDataImpl; @@ -146,23 +140,6 @@ export abstract class UpdateDataServiceImpl extends I return this.findAllData.findAll(options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow); } - /** - * Returns an observable of {@link RemoteData} of an object, based on its ID, with a list of - * {@link FollowLinkConfig}, to automatically resolve {@link HALLink}s of the object - * @param id ID of object we want to retrieve - * @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's - * no valid cached version. Defaults to true - * @param reRequestOnStale Whether or not the request should automatically be re- - * requested after the response becomes stale - * @param linksToFollow List of {@link FollowLinkConfig} that indicate which - * {@link HALLink}s should be automatically resolved - */ - findById(id: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig[]): Observable> { - const href$ = this.getIDHrefObs(encodeURIComponent(id), ...linksToFollow); - return this.findByHref(href$, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow); - } - - /** * Make a new FindListRequest with given search method * diff --git a/src/app/suggestion-notifications/suggestion.service.spec.ts b/src/app/suggestion-notifications/suggestion.service.spec.ts index ad2cb8fcc4a..acb0bb3be2e 100644 --- a/src/app/suggestion-notifications/suggestion.service.spec.ts +++ b/src/app/suggestion-notifications/suggestion.service.spec.ts @@ -1,12 +1,10 @@ import { SuggestionsService } from './suggestions.service'; -import { AuthService } from '../core/auth/auth.service'; import { ResearcherProfileDataService } from '../core/profile/researcher-profile-data.service'; import { SuggestionsDataService } from '../core/suggestion-notifications/suggestions-data.service'; -import { - SuggestionSourceDataService -} from '../core/suggestion-notifications/source/suggestion-source-data.service'; + + import { SuggestionTargetDataService } from '../core/suggestion-notifications/target/suggestion-target-data.service'; @@ -29,10 +27,8 @@ import { describe('SuggestionsService test', () => { let scheduler: TestScheduler; let service: SuggestionsService; - let authService: AuthService; let researcherProfileService: ResearcherProfileDataService; let suggestionsDataService: SuggestionsDataService; - let suggestionSourceDataService: SuggestionSourceDataService; let suggestionTargetDataService: SuggestionTargetDataService; let translateService: any = { instant: (str) => str, @@ -53,10 +49,8 @@ describe('SuggestionsService test', () => { function initTestService() { return new SuggestionsService( - authService, researcherProfileService, suggestionsDataService, - suggestionSourceDataService, suggestionTargetDataService, translateService ); @@ -65,11 +59,6 @@ describe('SuggestionsService test', () => { beforeEach(() => { scheduler = getTestScheduler(); - - suggestionSourceDataService = jasmine.createSpyObj('suggestionSourcesDataService', { - getSources: observableOf(null), - }); - researcherProfileService = jasmine.createSpyObj('researcherProfileService', { findById: createSuccessfulRemoteDataObject$(mockResercherProfile as ResearcherProfile), findRelatedItemId: observableOf('1234'), From 604d35590221847519a69f1ca646cc53cfe33e06 Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Thu, 25 Jan 2024 13:59:17 +0100 Subject: [PATCH 20/34] update labels --- .../suggestion-evidences.component.html | 6 +++--- .../suggestion-list-element.component.html | 2 +- src/app/suggestions-page/suggestions-page.component.html | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/app/suggestion-notifications/suggestion-list-element/suggestion-evidences/suggestion-evidences.component.html b/src/app/suggestion-notifications/suggestion-list-element/suggestion-evidences/suggestion-evidences.component.html index 5ad4f0a9783..e23c244eb49 100644 --- a/src/app/suggestion-notifications/suggestion-list-element/suggestion-evidences/suggestion-evidences.component.html +++ b/src/app/suggestion-notifications/suggestion-list-element/suggestion-evidences/suggestion-evidences.component.html @@ -3,9 +3,9 @@

{{'reciter.suggestion.table.name' | translate}}{{'reciter.suggestion.table.actions' | translate}}{{'suggestion.table.name' | translate}}{{'suggestion.table.actions' | translate}}
- - - + + + diff --git a/src/app/suggestion-notifications/suggestion-list-element/suggestion-list-element.component.html b/src/app/suggestion-notifications/suggestion-list-element/suggestion-list-element.component.html index 3d95aed8a3c..ef27876f2cb 100644 --- a/src/app/suggestion-notifications/suggestion-list-element/suggestion-list-element.component.html +++ b/src/app/suggestion-notifications/suggestion-list-element/suggestion-list-element.component.html @@ -11,7 +11,7 @@
-
{{'reciter.suggestion.totalScore' | translate}}
+
{{'suggestion.totalScore' | translate}}
{{ object.score }}
diff --git a/src/app/suggestions-page/suggestions-page.component.html b/src/app/suggestions-page/suggestions-page.component.html index f8b14f67286..89c676ab71a 100644 --- a/src/app/suggestions-page/suggestions-page.component.html +++ b/src/app/suggestions-page/suggestions-page.component.html @@ -6,9 +6,9 @@

{{ translateSuggestionType() | translate }} - {{'reciter.suggestion.suggestionFor' | translate}} + {{'suggestion.suggestionFor' | translate}} {{researcherName}} - {{'reciter.suggestion.from.source' | translate}} {{ translateSuggestionSource() | translate }} + {{'suggestion.from.source' | translate}} {{ translateSuggestionSource() | translate }}

@@ -33,7 +33,7 @@

  • Date: Thu, 25 Jan 2024 19:32:12 +0100 Subject: [PATCH 21/34] add breadcrumbs, adapt tests, fix issue on page loading --- ...lication-claim-breadcrumb.resolver.spec.ts | 31 +++++++++++++ .../publication-claim-breadcrumb.resolver.ts | 24 ++++++++++ ...blication-claim-breadcrumb.service.spec.ts | 46 +++++++++++++++++++ .../publication-claim-breadcrumb.service.ts | 42 +++++++++++++++++ .../cache/builders/build-decorators.spec.ts | 8 +++- .../publication-claim.component.html | 2 +- .../publication-claim.component.ts | 9 +--- .../suggestions-page-routing.module.ts | 6 +-- 8 files changed, 155 insertions(+), 13 deletions(-) create mode 100644 src/app/core/breadcrumbs/publication-claim-breadcrumb.resolver.spec.ts create mode 100644 src/app/core/breadcrumbs/publication-claim-breadcrumb.resolver.ts create mode 100644 src/app/core/breadcrumbs/publication-claim-breadcrumb.service.spec.ts create mode 100644 src/app/core/breadcrumbs/publication-claim-breadcrumb.service.ts diff --git a/src/app/core/breadcrumbs/publication-claim-breadcrumb.resolver.spec.ts b/src/app/core/breadcrumbs/publication-claim-breadcrumb.resolver.spec.ts new file mode 100644 index 00000000000..b6f41424693 --- /dev/null +++ b/src/app/core/breadcrumbs/publication-claim-breadcrumb.resolver.spec.ts @@ -0,0 +1,31 @@ +import { PublicationClaimBreadcrumbResolver } from './publication-claim-breadcrumb.resolver'; + +describe('PublicationClaimBreadcrumbResolver', () => { + describe('resolve', () => { + let resolver: PublicationClaimBreadcrumbResolver; + let publicationClaimBreadcrumbService: any; + const fullPath = '/test/publication-claim/openaire:6bee076d-4f2a-4555-a475-04a267769b2a'; + const expectedKey = '6bee076d-4f2a-4555-a475-04a267769b2a'; + const expectedId = 'openaire:6bee076d-4f2a-4555-a475-04a267769b2a'; + let route; + + beforeEach(() => { + route = { + paramMap: { + get: function (param) { + return this[param]; + }, + targetId: expectedId, + } + }; + publicationClaimBreadcrumbService = {}; + resolver = new PublicationClaimBreadcrumbResolver(publicationClaimBreadcrumbService); + }); + + it('should resolve the breadcrumb config', () => { + const resolvedConfig = resolver.resolve(route as any, {url: fullPath } as any); + const expectedConfig = { provider: publicationClaimBreadcrumbService, key: expectedKey }; + expect(resolvedConfig).toEqual(expectedConfig); + }); + }); +}); diff --git a/src/app/core/breadcrumbs/publication-claim-breadcrumb.resolver.ts b/src/app/core/breadcrumbs/publication-claim-breadcrumb.resolver.ts new file mode 100644 index 00000000000..713500d6a73 --- /dev/null +++ b/src/app/core/breadcrumbs/publication-claim-breadcrumb.resolver.ts @@ -0,0 +1,24 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router'; +import {BreadcrumbConfig} from '../../breadcrumbs/breadcrumb/breadcrumb-config.model'; +import { PublicationClaimBreadcrumbService } from './publication-claim-breadcrumb.service'; + +@Injectable({ + providedIn: 'root' +}) +export class PublicationClaimBreadcrumbResolver implements Resolve> { + constructor(protected breadcrumbService: PublicationClaimBreadcrumbService) { + } + + /** + * Method that resolve Publication Claim item into a breadcrumb + * The parameter are retrieved by the url since part of the Publication Claim route config + * @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot + * @param {RouterStateSnapshot} state The current RouterStateSnapshot + * @returns BreadcrumbConfig object + */ + resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): BreadcrumbConfig { + const targetId = route.paramMap.get('targetId').split(':')[1]; + return { provider: this.breadcrumbService, key: targetId }; + } +} diff --git a/src/app/core/breadcrumbs/publication-claim-breadcrumb.service.spec.ts b/src/app/core/breadcrumbs/publication-claim-breadcrumb.service.spec.ts new file mode 100644 index 00000000000..61fb1d82bf3 --- /dev/null +++ b/src/app/core/breadcrumbs/publication-claim-breadcrumb.service.spec.ts @@ -0,0 +1,46 @@ +import { TestBed, waitForAsync } from '@angular/core/testing'; +import { Breadcrumb } from '../../breadcrumbs/breadcrumb/breadcrumb.model'; +import { getTestScheduler } from 'jasmine-marbles'; +import { PublicationClaimBreadcrumbService } from './publication-claim-breadcrumb.service'; +import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; + +describe('PublicationClaimBreadcrumbService', () => { + let service: PublicationClaimBreadcrumbService; + let dsoNameService: any = { + getName: (str) => str + }; + let translateService: any = { + instant: (str) => str, + }; + + let dataService: any = { + findById: (str) => createSuccessfulRemoteDataObject$(str), + }; + + let exampleKey; + + const ADMIN_PUBLICATION_CLAIMS_PATH = 'admin/notifications/publication-claim'; + const ADMIN_PUBLICATION_CLAIMS_BREADCRUMB_KEY = 'admin.notifications.publicationclaim.page.title'; + + function init() { + exampleKey = '6bee076d-4f2a-4555-a475-04a267769b2a'; + } + + beforeEach(waitForAsync(() => { + init(); + TestBed.configureTestingModule({}).compileComponents(); + })); + + beforeEach(() => { + service = new PublicationClaimBreadcrumbService(dataService,dsoNameService,translateService); + }); + + describe('getBreadcrumbs', () => { + it('should return a breadcrumb based on a string', () => { + const breadcrumbs = service.getBreadcrumbs(exampleKey); + getTestScheduler().expectObservable(breadcrumbs).toBe('(a|)', { a: [new Breadcrumb(ADMIN_PUBLICATION_CLAIMS_BREADCRUMB_KEY, ADMIN_PUBLICATION_CLAIMS_PATH), + new Breadcrumb(exampleKey, undefined)] + }); + }); + }); +}); diff --git a/src/app/core/breadcrumbs/publication-claim-breadcrumb.service.ts b/src/app/core/breadcrumbs/publication-claim-breadcrumb.service.ts new file mode 100644 index 00000000000..64284ee5100 --- /dev/null +++ b/src/app/core/breadcrumbs/publication-claim-breadcrumb.service.ts @@ -0,0 +1,42 @@ +import { Breadcrumb } from '../../breadcrumbs/breadcrumb/breadcrumb.model'; +import { BreadcrumbsProviderService } from './breadcrumbsProviderService'; +import { Observable } from 'rxjs'; +import { Injectable } from '@angular/core'; +import { ItemDataService } from '../data/item-data.service'; +import { getFirstCompletedRemoteData } from '../shared/operators'; +import { map } from 'rxjs/operators'; +import { DSONameService } from './dso-name.service'; +import { TranslateService } from '@ngx-translate/core'; + + + +/** + * Service to calculate Publication claims breadcrumbs + */ +@Injectable({ + providedIn: 'root' +}) +export class PublicationClaimBreadcrumbService implements BreadcrumbsProviderService { + private ADMIN_PUBLICATION_CLAIMS_PATH = 'admin/notifications/publication-claim'; + private ADMIN_PUBLICATION_CLAIMS_BREADCRUMB_KEY = 'admin.notifications.publicationclaim.page.title'; + + constructor(private dataService: ItemDataService, + private dsoNameService: DSONameService, + private tranlsateService: TranslateService) { + } + + + /** + * Method to calculate the breadcrumbs + * @param key The key used to resolve the breadcrumb + */ + getBreadcrumbs(key: string): Observable { + return this.dataService.findById(key).pipe( + getFirstCompletedRemoteData(), + map((item) => { + return [new Breadcrumb(this.tranlsateService.instant(this.ADMIN_PUBLICATION_CLAIMS_BREADCRUMB_KEY), this.ADMIN_PUBLICATION_CLAIMS_PATH), + new Breadcrumb(this.dsoNameService.getName(item.payload), undefined)]; + }) + ); + } +} diff --git a/src/app/core/cache/builders/build-decorators.spec.ts b/src/app/core/cache/builders/build-decorators.spec.ts index 90d4b4fef83..e4baaa4a5a8 100644 --- a/src/app/core/cache/builders/build-decorators.spec.ts +++ b/src/app/core/cache/builders/build-decorators.spec.ts @@ -1,7 +1,7 @@ import { HALLink } from '../../shared/hal-link.model'; import { HALResource } from '../../shared/hal-resource.model'; import { ResourceType } from '../../shared/resource-type'; -import { dataService, getLinkDefinition, link } from './build-decorators'; +import { dataService, getDataServiceFor, getLinkDefinition, link } from './build-decorators'; class TestHALResource implements HALResource { _links: { @@ -51,6 +51,12 @@ describe('build decorators', () => { it(`should throw error`, () => { expect(dataService(null)).toThrow(); }); + + it(`should set properly data service for type`, () => { + const target = new TestHALResource(); + dataService(testType)(target); + expect(getDataServiceFor(testType)).toEqual(target); + }); }); }); }); diff --git a/src/app/suggestion-notifications/suggestion-targets/publication-claim/publication-claim.component.html b/src/app/suggestion-notifications/suggestion-targets/publication-claim/publication-claim.component.html index 45068cd12c8..f2ab1e5b652 100644 --- a/src/app/suggestion-notifications/suggestion-targets/publication-claim/publication-claim.component.html +++ b/src/app/suggestion-notifications/suggestion-targets/publication-claim/publication-claim.component.html @@ -31,7 +31,7 @@

    {{'suggestion.title'| translate}}

  • {{'reciter.suggestion.evidence.score' | translate}}{{'reciter.suggestion.evidence.type' | translate}}{{'reciter.suggestion.evidence.notes' | translate}}{{'suggestion.evidence.score' | translate}}{{'suggestion.evidence.type' | translate}}{{'suggestion.evidence.notes' | translate}}
    - + ({{ getSelectedSuggestionsCount() }})
    +
    {{ 'suggestion.count.missing' | translate }}
    diff --git a/src/app/suggestions-page/suggestions-page.component.ts b/src/app/suggestions-page/suggestions-page.component.ts index 48245b60b3b..d986366f1c3 100644 --- a/src/app/suggestions-page/suggestions-page.component.ts +++ b/src/app/suggestions-page/suggestions-page.component.ts @@ -172,12 +172,12 @@ export class SuggestionsPageComponent implements OnInit { this.selectedSuggestions = {}; if (results.success > 0) { this.notificationService.success( - this.translateService.get('suggestion.notMine.bulk.success', + this.translateService.get('suggestion.ignoreSuggestion.bulk.success', {count: results.success})); } if (results.fails > 0) { this.notificationService.error( - this.translateService.get('suggestion.notMine.bulk.error', + this.translateService.get('suggestion.ignoreSuggestion.bulk.error', {count: results.fails})); } }); diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 006fbdb5d49..fd4e50610c3 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -3596,7 +3596,7 @@ "suggestion.approveAndImport.bulk": "Approve & import Selected", - ".suggestion.approveAndImport.bulk.success": "{{ count }} suggestions have been imported successfully ", + "suggestion.approveAndImport.bulk.success": "{{ count }} suggestions have been imported successfully ", "suggestion.approveAndImport.bulk.error": "{{ count }} suggestions haven't been imported due to unexpected server errors", @@ -3620,6 +3620,8 @@ "suggestion.from.source": "from the ", + "suggestion.count.missing": "You have no publication claims left", + "suggestion.totalScore": "Total Score", "suggestion.type.openaire": "OpenAIRE", diff --git a/src/config/default-app-config.ts b/src/config/default-app-config.ts index be9306c893c..3b3eb00ddbf 100644 --- a/src/config/default-app-config.ts +++ b/src/config/default-app-config.ts @@ -304,8 +304,9 @@ export class DefaultAppConfig implements AppConfig { // source: 'suggestionSource', // collectionId: 'collectionUUID' // } - // If not mapped the suggestion service won't be able to approve and import the related suggestion - // or load the fixed suggestions collections that can be configured here adding the source and the UUID of the collection + // This is used as a default fallback in case there aren't collections where to import the suggestion + // If not mapped the user will be allowed to import the suggestions only in the provided options, shown clicking the button "Approve and import" + // If not mapped and no options available for import the user won't be able to import the suggestions. ]; // Theme Config From b8555f53c3125b6e9a61da116a41e43b9896a471 Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Fri, 26 Jan 2024 11:53:56 +0100 Subject: [PATCH 23/34] add commented example --- config/config.example.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/config/config.example.yml b/config/config.example.yml index 69a9ffd320f..4b9c1a27ace 100644 --- a/config/config.example.yml +++ b/config/config.example.yml @@ -75,7 +75,7 @@ cache: anonymousCache: # Maximum number of pages to cache. Default is zero (0) which means anonymous user cache is disabled. # As all pages are cached in server memory, increasing this value will increase memory needs. - # Individual cached pages are usually small (<100KB), so a value of max=1000 would only require ~100MB of memory. + # Individual cached pages are usually small (<100KB), so a value of max=1000 would only require ~100MB of memory. max: 0 # Amount of time after which cached pages are considered stale (in ms). After becoming stale, the cached # copy is automatically refreshed on the next request. @@ -382,7 +382,13 @@ vocabularies: vocabulary: 'srsc' enabled: true -# Default collection/community sorting order at Advanced search, Create/update community and collection when there are not a query. +# Default collection/community sorting order at Advanced search, Create/update community and collection when there are not a query. comcolSelectionSort: sortField: 'dc.title' sortDirection: 'ASC' + +# Example of fallback collection for suggestions import +# suggestion: + # - collectionId: 8f7df5ca-f9c2-47a4-81ec-8a6393d6e5af + # source: "openaire" + From 2da613bff155035ae3c384924029820e7e1c88bc Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Thu, 1 Feb 2024 11:31:26 +0100 Subject: [PATCH 24/34] clean up --- ...rvice.spec.ts => update-data.service.spec.ts} | 2 +- ...te-data-service.ts => update-data.service.ts} | 0 .../suggestions-data.service.ts | 2 +- .../dso-edit-metadata.component.ts | 2 +- .../themed-dso-edit-metadata.component.ts | 2 +- .../suggestions-notification.component.html | 2 +- .../suggestions-notification.component.ts | 2 -- .../suggestions.service.ts | 4 +++- .../suggestions-page.component.html | 1 - .../suggestions-page.component.ts | 5 ++++- src/assets/i18n/en.json5 | 16 +++++++++------- 11 files changed, 21 insertions(+), 17 deletions(-) rename src/app/core/data/{update-data-service.spec.ts => update-data.service.spec.ts} (98%) rename src/app/core/data/{update-data-service.ts => update-data.service.ts} (100%) diff --git a/src/app/core/data/update-data-service.spec.ts b/src/app/core/data/update-data.service.spec.ts similarity index 98% rename from src/app/core/data/update-data-service.spec.ts rename to src/app/core/data/update-data.service.spec.ts index 6912527b918..426fa87eb6d 100644 --- a/src/app/core/data/update-data-service.spec.ts +++ b/src/app/core/data/update-data.service.spec.ts @@ -14,7 +14,7 @@ import { Version } from '../shared/version.model'; import { VersionHistory } from '../shared/version-history.model'; import { RequestEntry } from './request-entry.model'; import { testPatchDataImplementation } from './base/patch-data.spec'; -import { UpdateDataServiceImpl } from './update-data-service'; +import { UpdateDataServiceImpl } from './update-data.service'; import { testSearchDataImplementation } from './base/search-data.spec'; import { testDeleteDataImplementation } from './base/delete-data.spec'; import { testCreateDataImplementation } from './base/create-data.spec'; diff --git a/src/app/core/data/update-data-service.ts b/src/app/core/data/update-data.service.ts similarity index 100% rename from src/app/core/data/update-data-service.ts rename to src/app/core/data/update-data.service.ts diff --git a/src/app/core/suggestion-notifications/suggestions-data.service.ts b/src/app/core/suggestion-notifications/suggestions-data.service.ts index 2078fdb75e2..8956cbd208c 100644 --- a/src/app/core/suggestion-notifications/suggestions-data.service.ts +++ b/src/app/core/suggestion-notifications/suggestions-data.service.ts @@ -11,7 +11,7 @@ import { RemoteDataBuildService } from '../cache/builders/remote-data-build.serv import { ObjectCacheService } from '../cache/object-cache.service'; import { dataService } from '../cache/builders/build-decorators'; import { RequestService } from '../data/request.service'; -import { UpdateDataServiceImpl } from '../data/update-data-service'; +import { UpdateDataServiceImpl } from '../data/update-data.service'; import { ChangeAnalyzer } from '../data/change-analyzer'; import { DefaultChangeAnalyzer } from '../data/default-change-analyzer.service'; import { RemoteData } from '../data/remote-data'; diff --git a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.ts b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.ts index 22d23e84020..60b1dffa450 100644 --- a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.ts +++ b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.ts @@ -21,7 +21,7 @@ import { ArrayMoveChangeAnalyzer } from '../../core/data/array-move-change-analy import { DATA_SERVICE_FACTORY } from '../../core/data/base/data-service.decorator'; import { GenericConstructor } from '../../core/shared/generic-constructor'; import { HALDataService } from '../../core/data/base/hal-data-service.interface'; -import { UpdateDataService } from '../../core/data/update-data-service'; +import { UpdateDataService } from '../../core/data/update-data.service'; @Component({ selector: 'ds-dso-edit-metadata', diff --git a/src/app/dso-shared/dso-edit-metadata/themed-dso-edit-metadata.component.ts b/src/app/dso-shared/dso-edit-metadata/themed-dso-edit-metadata.component.ts index 5d7ccdd87d2..ba21907c99e 100644 --- a/src/app/dso-shared/dso-edit-metadata/themed-dso-edit-metadata.component.ts +++ b/src/app/dso-shared/dso-edit-metadata/themed-dso-edit-metadata.component.ts @@ -2,7 +2,7 @@ import { ThemedComponent } from '../../shared/theme-support/themed.component'; import { DsoEditMetadataComponent } from './dso-edit-metadata.component'; import { Component, Input } from '@angular/core'; import { DSpaceObject } from '../../core/shared/dspace-object.model'; -import { UpdateDataService } from '../../core/data/update-data-service'; +import { UpdateDataService } from '../../core/data/update-data.service'; @Component({ selector: 'ds-themed-dso-edit-metadata', diff --git a/src/app/suggestion-notifications/suggestions-notification/suggestions-notification.component.html b/src/app/suggestion-notifications/suggestions-notification/suggestions-notification.component.html index 40156b368fd..3eac8d20992 100644 --- a/src/app/suggestion-notifications/suggestions-notification/suggestions-notification.component.html +++ b/src/app/suggestion-notifications/suggestions-notification/suggestions-notification.component.html @@ -1,7 +1,7 @@
    -
    +
    diff --git a/src/app/suggestion-notifications/suggestions-notification/suggestions-notification.component.ts b/src/app/suggestion-notifications/suggestions-notification/suggestions-notification.component.ts index dfa1e187e3b..e2afe8f07ce 100644 --- a/src/app/suggestion-notifications/suggestions-notification/suggestions-notification.component.ts +++ b/src/app/suggestion-notifications/suggestions-notification/suggestions-notification.component.ts @@ -16,8 +16,6 @@ import { Observable } from 'rxjs'; }) export class SuggestionsNotificationComponent implements OnInit { - labelPrefix = 'mydspace.'; - /** * The user suggestion targets. */ diff --git a/src/app/suggestion-notifications/suggestions.service.ts b/src/app/suggestion-notifications/suggestions.service.ts index bf21b5ae2f4..565f8f94dbd 100644 --- a/src/app/suggestion-notifications/suggestions.service.ts +++ b/src/app/suggestion-notifications/suggestions.service.ts @@ -30,6 +30,7 @@ import { import { SuggestionsDataService } from '../core/suggestion-notifications/suggestions-data.service'; +import { getSuggestionPageRoute } from '../suggestions-page/suggestions-page-routing-paths'; /** * useful for multiple approvals and ignores operation @@ -256,7 +257,8 @@ export class SuggestionsService { source: this.translateService.instant(this.translateSuggestionSource(suggestionTarget.source)), type: this.translateService.instant(this.translateSuggestionType(suggestionTarget.source)), suggestionId: suggestionTarget.id, - displayName: suggestionTarget.display + displayName: suggestionTarget.display, + url: getSuggestionPageRoute(suggestionTarget.id) }; } diff --git a/src/app/suggestions-page/suggestions-page.component.html b/src/app/suggestions-page/suggestions-page.component.html index 5865b613ac0..4e367b638e8 100644 --- a/src/app/suggestions-page/suggestions-page.component.html +++ b/src/app/suggestions-page/suggestions-page.component.html @@ -5,7 +5,6 @@

    - {{ translateSuggestionType() | translate }} {{'suggestion.suggestionFor' | translate}} {{researcherName}} {{'suggestion.from.source' | translate}} {{ translateSuggestionSource() | translate }} diff --git a/src/app/suggestions-page/suggestions-page.component.ts b/src/app/suggestions-page/suggestions-page.component.ts index d986366f1c3..ec560dc3eca 100644 --- a/src/app/suggestions-page/suggestions-page.component.ts +++ b/src/app/suggestions-page/suggestions-page.component.ts @@ -22,6 +22,9 @@ import { PaginationService } from '../core/pagination/pagination.service'; import { WorkspaceItem } from '../core/submission/models/workspaceitem.model'; import {FindListOptions} from '../core/data/find-list-options.model'; import {redirectOn4xx} from '../core/shared/authorized.operators'; +import { + getWorkflowItemEditRoute +} from '../workflowitems-edit-page/workflowitems-edit-page-routing-paths'; @Component({ selector: 'ds-suggestion-page', @@ -190,7 +193,7 @@ export class SuggestionsPageComponent implements OnInit { approveAndImport(event: SuggestionApproveAndImport) { this.suggestionService.approveAndImport(this.workspaceItemService, event.suggestion, event.collectionId) .subscribe((workspaceitem: WorkspaceItem) => { - const content = this.translateService.instant('suggestion.approveAndImport.success', { workspaceItemId: workspaceitem.id }); + const content = this.translateService.instant('suggestion.approveAndImport.success', { url: getWorkflowItemEditRoute(workspaceitem.id) }); this.notificationService.success('', content, {timeOut:0}, true); this.suggestionTargetsStateService.dispatchRefreshUserSuggestionsAction(); this.updatePage(); diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index fd4e50610c3..bb82085a4a4 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -1772,6 +1772,10 @@ "form.create": "Create", + "form.number-picker.decrement": "Decrement {{field}}", + + "form.number-picker.increment": "Increment {{field}}", + "form.repeatable.sort.tip": "Drop the item in the new position", "grant-deny-request-copy.deny": "Don't send copy", @@ -3084,9 +3088,7 @@ "mydspace.import": "Import", - "notification.suggestion": "We found {{count}} publications
    in the {{source}} that seems to be related to your profile.
    Please review the suggestions", - - "notification.suggestion.page": "We found {{count}} {{type}} in the {{source}} that seems to be related to your profile. Please review the suggestions.", + "notification.suggestion": "We found {{count}} publications
    in the {{source}} that seems to be related to your profile.
    Please review the suggestions", "nav.browse.header": "All of DSpace", @@ -3564,9 +3566,9 @@ "suggestion.loading": "Loading ...", - "suggestion.title": "Publication Claims", + "suggestion.title": "Publication Claim", - "suggestion.title.breadcrumbs": "Publication Claims", + "suggestion.title.breadcrumbs": "Publication Claim", "suggestion.targets.description": "Below you can see all the suggestions ", @@ -3592,7 +3594,7 @@ "suggestion.approveAndImport": "Approve & import", - "suggestion.approveAndImport.success": "The suggestion has been imported successfully. View.", + "suggestion.approveAndImport.success": "The suggestion has been imported successfully. View.", "suggestion.approveAndImport.bulk": "Approve & import Selected", @@ -3614,7 +3616,7 @@ "suggestion.hideEvidence": "Hide evidence", - "suggestion.suggestionFor": "Suggestion for", + "suggestion.suggestionFor": "Suggestions for", "suggestion.source.openaire": "OpenAIRE Graph", From 937687c41433073bf51a9abdbfa2aeca69909fc8 Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Mon, 5 Feb 2024 12:48:23 +0100 Subject: [PATCH 25/34] fix routing, update issue, adapt labels --- .../suggestions-notification.component.ts | 1 + .../suggestions-popup/suggestions-popup.component.ts | 4 ++-- src/app/suggestion-notifications/suggestions.service.ts | 2 +- src/app/suggestions-page/suggestions-page.component.ts | 4 ++-- .../workflowitems-edit-page-routing-paths.ts | 5 +++++ src/assets/i18n/en.json5 | 4 ++-- 6 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/app/suggestion-notifications/suggestions-notification/suggestions-notification.component.ts b/src/app/suggestion-notifications/suggestions-notification/suggestions-notification.component.ts index e2afe8f07ce..e123f8f87ff 100644 --- a/src/app/suggestion-notifications/suggestions-notification/suggestions-notification.component.ts +++ b/src/app/suggestion-notifications/suggestions-notification/suggestions-notification.component.ts @@ -29,6 +29,7 @@ export class SuggestionsNotificationComponent implements OnInit { ) { } ngOnInit() { + this.suggestionTargetsStateService.dispatchRefreshUserSuggestionsAction(); this.suggestionsRD$ = this.suggestionTargetsStateService.getCurrentUserSuggestionTargets(); } diff --git a/src/app/suggestion-notifications/suggestions-popup/suggestions-popup.component.ts b/src/app/suggestion-notifications/suggestions-popup/suggestions-popup.component.ts index 762f80085a8..0b2176ea892 100644 --- a/src/app/suggestion-notifications/suggestions-popup/suggestions-popup.component.ts +++ b/src/app/suggestion-notifications/suggestions-popup/suggestions-popup.component.ts @@ -3,7 +3,7 @@ import { TranslateService } from '@ngx-translate/core'; import { SuggestionTargetsStateService } from '../suggestion-targets/suggestion-targets.state.service'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { SuggestionsService } from '../suggestions.service'; -import { takeUntil } from 'rxjs/operators'; +import { take, takeUntil } from 'rxjs/operators'; import { SuggestionTarget } from '../../core/suggestion-notifications/models/suggestion-target.model'; import { isNotEmpty } from '../../shared/empty.util'; import { combineLatest, Subject } from 'rxjs'; @@ -36,7 +36,7 @@ export class SuggestionsPopupComponent implements OnInit, OnDestroy { public initializePopup() { const notifier = new Subject(); this.subscription = combineLatest([ - this.suggestionTargetsStateService.getCurrentUserSuggestionTargets(), + this.suggestionTargetsStateService.getCurrentUserSuggestionTargets().pipe(take(2)), this.suggestionTargetsStateService.hasUserVisitedSuggestions() ]).pipe(takeUntil(notifier)).subscribe(([suggestions, visited]) => { this.suggestionTargetsStateService.dispatchRefreshUserSuggestionsAction(); diff --git a/src/app/suggestion-notifications/suggestions.service.ts b/src/app/suggestion-notifications/suggestions.service.ts index 565f8f94dbd..874659eab5f 100644 --- a/src/app/suggestion-notifications/suggestions.service.ts +++ b/src/app/suggestion-notifications/suggestions.service.ts @@ -154,7 +154,7 @@ export class SuggestionsService { * The EPerson id for which to retrieve suggestion targets */ public retrieveCurrentUserSuggestions(userUuid: string): Observable { - return this.researcherProfileService.findById(userUuid).pipe( + return this.researcherProfileService.findById(userUuid, true).pipe( getFirstSucceededRemoteDataPayload(), mergeMap((profile: ResearcherProfile) => { if (isNotEmpty(profile)) { diff --git a/src/app/suggestions-page/suggestions-page.component.ts b/src/app/suggestions-page/suggestions-page.component.ts index ec560dc3eca..20056460100 100644 --- a/src/app/suggestions-page/suggestions-page.component.ts +++ b/src/app/suggestions-page/suggestions-page.component.ts @@ -23,7 +23,7 @@ import { WorkspaceItem } from '../core/submission/models/workspaceitem.model'; import {FindListOptions} from '../core/data/find-list-options.model'; import {redirectOn4xx} from '../core/shared/authorized.operators'; import { - getWorkflowItemEditRoute + getWorkspaceItemEditRoute } from '../workflowitems-edit-page/workflowitems-edit-page-routing-paths'; @Component({ @@ -193,7 +193,7 @@ export class SuggestionsPageComponent implements OnInit { approveAndImport(event: SuggestionApproveAndImport) { this.suggestionService.approveAndImport(this.workspaceItemService, event.suggestion, event.collectionId) .subscribe((workspaceitem: WorkspaceItem) => { - const content = this.translateService.instant('suggestion.approveAndImport.success', { url: getWorkflowItemEditRoute(workspaceitem.id) }); + const content = this.translateService.instant('suggestion.approveAndImport.success', { url: getWorkspaceItemEditRoute(workspaceitem.id) }); this.notificationService.success('', content, {timeOut:0}, true); this.suggestionTargetsStateService.dispatchRefreshUserSuggestionsAction(); this.updatePage(); diff --git a/src/app/workflowitems-edit-page/workflowitems-edit-page-routing-paths.ts b/src/app/workflowitems-edit-page/workflowitems-edit-page-routing-paths.ts index 326eebe4a79..a4d4a0f2c93 100644 --- a/src/app/workflowitems-edit-page/workflowitems-edit-page-routing-paths.ts +++ b/src/app/workflowitems-edit-page/workflowitems-edit-page-routing-paths.ts @@ -28,9 +28,14 @@ export function getWorkspaceItemDeleteRoute(wsiId: string) { return new URLCombiner(getWorkspaceItemModuleRoute(), wsiId, WORKSPACE_ITEM_DELETE_PATH).toString(); } +export function getWorkspaceItemEditRoute(wsiId: string) { + return new URLCombiner(getWorkspaceItemModuleRoute(), wsiId, WORKSPACE_ITEM_EDIT_PATH).toString(); +} + export const WORKFLOW_ITEM_EDIT_PATH = 'edit'; export const WORKFLOW_ITEM_DELETE_PATH = 'delete'; export const WORKFLOW_ITEM_VIEW_PATH = 'view'; export const WORKFLOW_ITEM_SEND_BACK_PATH = 'sendback'; export const ADVANCED_WORKFLOW_PATH = 'advanced'; export const WORKSPACE_ITEM_DELETE_PATH = 'delete'; +export const WORKSPACE_ITEM_EDIT_PATH = 'edit'; diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index bb82085a4a4..1aebe0430eb 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -24,9 +24,9 @@ "404.page-not-found": "page not found", - "admin.notifications.publicationclaim.breadcrumbs": "Suggestions", + "admin.notifications.publicationclaim.breadcrumbs": "Publication Claim", - "admin.notifications.publicationclaim.page.title": "Suggestions", + "admin.notifications.publicationclaim.page.title": "Publication Claim", "error-page.description.401": "unauthorized", From 06cc944f2b64b94032e383965be1507ecd040b34 Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Mon, 5 Feb 2024 13:00:08 +0100 Subject: [PATCH 26/34] fix test --- src/app/suggestion-notifications/suggestion.service.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/suggestion-notifications/suggestion.service.spec.ts b/src/app/suggestion-notifications/suggestion.service.spec.ts index acb0bb3be2e..268eb59ddd1 100644 --- a/src/app/suggestion-notifications/suggestion.service.spec.ts +++ b/src/app/suggestion-notifications/suggestion.service.spec.ts @@ -121,7 +121,7 @@ describe('SuggestionsService test', () => { it('should retrieve current user suggestions', () => { service.retrieveCurrentUserSuggestions('1234'); - expect(researcherProfileService.findById).toHaveBeenCalledWith('1234'); + expect(researcherProfileService.findById).toHaveBeenCalledWith('1234', true); }); it('should approve and import suggestion', () => { From 044ec06cb1d47b076e75e1b0bfd51f74bf33b9cf Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Mon, 5 Feb 2024 16:53:21 +0100 Subject: [PATCH 27/34] update breadcrumb label --- .../core/breadcrumbs/publication-claim-breadcrumb.service.ts | 3 ++- src/assets/i18n/en.json5 | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/app/core/breadcrumbs/publication-claim-breadcrumb.service.ts b/src/app/core/breadcrumbs/publication-claim-breadcrumb.service.ts index 14ef7917c38..1a87fd7de60 100644 --- a/src/app/core/breadcrumbs/publication-claim-breadcrumb.service.ts +++ b/src/app/core/breadcrumbs/publication-claim-breadcrumb.service.ts @@ -38,7 +38,8 @@ export class PublicationClaimBreadcrumbService implements BreadcrumbsProviderSer map(([item, isAdmin]) => { const itemName = this.dsoNameService.getName(item.payload); return isAdmin ? [new Breadcrumb(this.tranlsateService.instant(this.ADMIN_PUBLICATION_CLAIMS_BREADCRUMB_KEY), this.ADMIN_PUBLICATION_CLAIMS_PATH), - new Breadcrumb(itemName, undefined)] : [new Breadcrumb(itemName, undefined)]; + new Breadcrumb(this.tranlsateService.instant('suggestion.suggestionFor.breadcrumb', {name: itemName}), undefined)] : + [new Breadcrumb(this.tranlsateService.instant('suggestion.suggestionFor.breadcrumb', {name: itemName}), undefined)]; }) ); } diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 1aebe0430eb..39785eed701 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -3618,6 +3618,8 @@ "suggestion.suggestionFor": "Suggestions for", + "suggestion.suggestionFor.breadcrumb": "Suggestions for {{ name }}", + "suggestion.source.openaire": "OpenAIRE Graph", "suggestion.from.source": "from the ", From 71e3d40c794a82058dc109e3f7793fa6d60fc135 Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Mon, 5 Feb 2024 17:49:49 +0100 Subject: [PATCH 28/34] fix breadcrumb test --- .../breadcrumbs/publication-claim-breadcrumb.service.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/core/breadcrumbs/publication-claim-breadcrumb.service.spec.ts b/src/app/core/breadcrumbs/publication-claim-breadcrumb.service.spec.ts index 6a2e6e54a63..11062210bb3 100644 --- a/src/app/core/breadcrumbs/publication-claim-breadcrumb.service.spec.ts +++ b/src/app/core/breadcrumbs/publication-claim-breadcrumb.service.spec.ts @@ -28,7 +28,7 @@ describe('PublicationClaimBreadcrumbService', () => { const ADMIN_PUBLICATION_CLAIMS_BREADCRUMB_KEY = 'admin.notifications.publicationclaim.page.title'; function init() { - exampleKey = '6bee076d-4f2a-4555-a475-04a267769b2a'; + exampleKey = 'suggestion.suggestionFor.breadcrumb'; } beforeEach(waitForAsync(() => { From 0ee6ee8ea1244947ed0e76b1363f4481a969a4b1 Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Fri, 9 Feb 2024 09:24:58 +0100 Subject: [PATCH 29/34] CST-5249 resolve conflicts --- src/assets/i18n/en.json5 | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index eea9c0befbc..90714201da6 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -24,10 +24,6 @@ "404.page-not-found": "page not found", - "admin.notifications.publicationclaim.breadcrumbs": "Publication Claim", - - "admin.notifications.publicationclaim.page.title": "Publication Claim", - "error-page.description.401": "unauthorized", "error-page.description.403": "forbidden", @@ -5564,4 +5560,7 @@ "vocabulary-treeview.search.form.add": "Add", + "admin.notifications.publicationclaim.breadcrumbs": "Publication Claim", + + "admin.notifications.publicationclaim.page.title": "Publication Claim" } From 5b7ca7cfa5ca44eafd6ca53cde8d33f927201abb Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Fri, 9 Feb 2024 09:32:03 +0100 Subject: [PATCH 30/34] CST-5249 fix lint --- src/assets/i18n/en.json5 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 90714201da6..97884bfb511 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -5562,5 +5562,5 @@ "admin.notifications.publicationclaim.breadcrumbs": "Publication Claim", - "admin.notifications.publicationclaim.page.title": "Publication Claim" + "admin.notifications.publicationclaim.page.title": "Publication Claim", } From 8dc7eeb2425152c5405ff875c81a90db346f056f Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Tue, 13 Feb 2024 12:37:29 +0100 Subject: [PATCH 31/34] [CST-5249] fix suggestion refresh after ignore --- .../suggestion-data.service.spec.ts | 2 +- .../suggestion-notifications/suggestions-data.service.ts | 2 +- src/app/suggestions-page/suggestions-page.component.ts | 6 ++++-- src/assets/i18n/en.json5 | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/app/core/suggestion-notifications/suggestion-data.service.spec.ts b/src/app/core/suggestion-notifications/suggestion-data.service.spec.ts index a854514c896..c0bc97ea129 100644 --- a/src/app/core/suggestion-notifications/suggestion-data.service.spec.ts +++ b/src/app/core/suggestion-notifications/suggestion-data.service.spec.ts @@ -155,7 +155,7 @@ describe('SuggestionDataService test', () => { searchParams: [new RequestParam('target', testUserId), new RequestParam('source', testSource)] }; service.getSuggestionsByTargetAndSource(testUserId, testSource); - expect(suggestionsDataService.searchBy).toHaveBeenCalledWith('findByTargetAndSource', options, true, true); + expect(suggestionsDataService.searchBy).toHaveBeenCalledWith('findByTargetAndSource', options, false, true); }); it('should call suggestionsDataService.delete', () => { diff --git a/src/app/core/suggestion-notifications/suggestions-data.service.ts b/src/app/core/suggestion-notifications/suggestions-data.service.ts index 8956cbd208c..17b14825782 100644 --- a/src/app/core/suggestion-notifications/suggestions-data.service.ts +++ b/src/app/core/suggestion-notifications/suggestions-data.service.ts @@ -217,7 +217,7 @@ export class SuggestionsDataService { new RequestParam('source', source) ]; - return this.suggestionsDataService.searchBy(this.searchFindByTargetAndSourceMethod, options, true, true, ...linksToFollow); + return this.suggestionsDataService.searchBy(this.searchFindByTargetAndSourceMethod, options, false, true, ...linksToFollow); } /** diff --git a/src/app/suggestions-page/suggestions-page.component.ts b/src/app/suggestions-page/suggestions-page.component.ts index 20056460100..d775a1e2206 100644 --- a/src/app/suggestions-page/suggestions-page.component.ts +++ b/src/app/suggestions-page/suggestions-page.component.ts @@ -2,7 +2,7 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Data, Router } from '@angular/router'; import { BehaviorSubject, combineLatest, Observable } from 'rxjs'; -import { distinctUntilChanged, map, switchMap, take } from 'rxjs/operators'; +import { delay, distinctUntilChanged, map, switchMap, take } from 'rxjs/operators'; import { TranslateService } from '@ngx-translate/core'; import { SortDirection, SortOptions, } from '../core/cache/models/sort-options.model'; @@ -155,7 +155,9 @@ export class SuggestionsPageComponent implements OnInit { * @suggestionId */ ignoreSuggestion(suggestionId) { - this.suggestionService.ignoreSuggestion(suggestionId).subscribe(() => { + this.suggestionService.ignoreSuggestion(suggestionId).pipe( + delay(100) + ).subscribe(() => { this.suggestionTargetsStateService.dispatchRefreshUserSuggestionsAction(); this.updatePage(); }); diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 97884bfb511..15134a01fd8 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -3120,7 +3120,7 @@ "mydspace.import": "Import", - "notification.suggestion": "We found {{count}} publications
    in the {{source}} that seems to be related to your profile.
    Please review the suggestions", + "notification.suggestion": "We found {{count}} publications in the {{source}} that seems to be related to your profile.
    Please review the suggestions", "nav.browse.header": "All of DSpace", From 5d85e511a1007a3575c03a3309811414d429a081 Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Tue, 13 Feb 2024 16:13:47 +0100 Subject: [PATCH 32/34] [CST-5249] fix tests --- .../suggestions-page/suggestions-page.component.spec.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/app/suggestions-page/suggestions-page.component.spec.ts b/src/app/suggestions-page/suggestions-page.component.spec.ts index 6abd28386f1..3e8ccfbc385 100644 --- a/src/app/suggestions-page/suggestions-page.component.spec.ts +++ b/src/app/suggestions-page/suggestions-page.component.spec.ts @@ -1,7 +1,7 @@ import { CommonModule } from '@angular/common'; import { BrowserModule } from '@angular/platform-browser'; import { NO_ERRORS_SCHEMA } from '@angular/core'; -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; import { TranslateModule, TranslateService } from '@ngx-translate/core'; import { of as observableOf } from 'rxjs'; @@ -129,16 +129,17 @@ describe('SuggestionPageComponent', () => { component.updatePage(); }); - it('should flag suggestion for deletion', () => { + it('should flag suggestion for deletion', fakeAsync(() => { spyOn(component, 'updatePage').and.stub(); scheduler.schedule(() => fixture.detectChanges()); scheduler.flush(); component.ignoreSuggestion('1'); + tick(101); expect(mockSuggestionsService.ignoreSuggestion).toHaveBeenCalledWith('1'); expect(mockSuggestionsTargetStateService.dispatchRefreshUserSuggestionsAction).toHaveBeenCalled(); expect(component.updatePage).toHaveBeenCalled(); - }); + })); it('should flag all suggestion for deletion', () => { spyOn(component, 'updatePage').and.stub(); From f475a4f516153ee033f4c4c74176de1ddd64e57e Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Wed, 14 Feb 2024 11:08:06 +0100 Subject: [PATCH 33/34] [CST-5249] fix deletion propagation --- .../suggestions-page/suggestions-page.component.spec.ts | 2 +- src/app/suggestions-page/suggestions-page.component.ts | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/app/suggestions-page/suggestions-page.component.spec.ts b/src/app/suggestions-page/suggestions-page.component.spec.ts index 3e8ccfbc385..98535297a29 100644 --- a/src/app/suggestions-page/suggestions-page.component.spec.ts +++ b/src/app/suggestions-page/suggestions-page.component.spec.ts @@ -135,9 +135,9 @@ describe('SuggestionPageComponent', () => { scheduler.schedule(() => fixture.detectChanges()); scheduler.flush(); component.ignoreSuggestion('1'); - tick(101); expect(mockSuggestionsService.ignoreSuggestion).toHaveBeenCalledWith('1'); expect(mockSuggestionsTargetStateService.dispatchRefreshUserSuggestionsAction).toHaveBeenCalled(); + tick(201); expect(component.updatePage).toHaveBeenCalled(); })); diff --git a/src/app/suggestions-page/suggestions-page.component.ts b/src/app/suggestions-page/suggestions-page.component.ts index d775a1e2206..fdbf131e592 100644 --- a/src/app/suggestions-page/suggestions-page.component.ts +++ b/src/app/suggestions-page/suggestions-page.component.ts @@ -2,7 +2,7 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Data, Router } from '@angular/router'; import { BehaviorSubject, combineLatest, Observable } from 'rxjs'; -import { delay, distinctUntilChanged, map, switchMap, take } from 'rxjs/operators'; +import { distinctUntilChanged, map, switchMap, take } from 'rxjs/operators'; import { TranslateService } from '@ngx-translate/core'; import { SortDirection, SortOptions, } from '../core/cache/models/sort-options.model'; @@ -155,11 +155,10 @@ export class SuggestionsPageComponent implements OnInit { * @suggestionId */ ignoreSuggestion(suggestionId) { - this.suggestionService.ignoreSuggestion(suggestionId).pipe( - delay(100) - ).subscribe(() => { + this.suggestionService.ignoreSuggestion(suggestionId).subscribe(() => { this.suggestionTargetsStateService.dispatchRefreshUserSuggestionsAction(); - this.updatePage(); + //We add a little delay in the page refresh so that we ensure the deletion has been propagated + setTimeout(() => this.updatePage(), 200); }); } From 75f5462a5b5e8d61d9dc3da1cda50c98ef241c4e Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Thu, 15 Feb 2024 16:10:13 +0100 Subject: [PATCH 34/34] [CST-5249] fix ssr rendering --- .../suggestions-notification.component.html | 5 +- .../suggestions-notification.component.ts | 4 -- .../suggestions-popup.component.html | 26 ++++++++++ .../suggestions-popup.component.spec.ts | 6 --- .../suggestions-popup.component.ts | 47 ++++++++++++------- src/assets/i18n/en.json5 | 6 ++- 6 files changed, 63 insertions(+), 31 deletions(-) diff --git a/src/app/suggestion-notifications/suggestions-notification/suggestions-notification.component.html b/src/app/suggestion-notifications/suggestions-notification/suggestions-notification.component.html index 3eac8d20992..838bdb95ad4 100644 --- a/src/app/suggestion-notifications/suggestions-notification/suggestions-notification.component.html +++ b/src/app/suggestion-notifications/suggestions-notification/suggestions-notification.component.html @@ -1,8 +1,9 @@
    -
    -
    +
    + {{ 'notification.suggestion.please' | translate }} + {{ 'notification.suggestion.review' | translate}}
    diff --git a/src/app/suggestion-notifications/suggestions-notification/suggestions-notification.component.ts b/src/app/suggestion-notifications/suggestions-notification/suggestions-notification.component.ts index e123f8f87ff..357a6ca34ef 100644 --- a/src/app/suggestion-notifications/suggestions-notification/suggestions-notification.component.ts +++ b/src/app/suggestion-notifications/suggestions-notification/suggestions-notification.component.ts @@ -1,8 +1,6 @@ import { Component, OnInit } from '@angular/core'; import { SuggestionTarget } from '../../core/suggestion-notifications/models/suggestion-target.model'; -import { TranslateService } from '@ngx-translate/core'; import { SuggestionTargetsStateService } from '../suggestion-targets/suggestion-targets.state.service'; -import { NotificationsService } from '../../shared/notifications/notifications.service'; import { SuggestionsService } from '../suggestions.service'; import { Observable } from 'rxjs'; @@ -22,9 +20,7 @@ export class SuggestionsNotificationComponent implements OnInit { suggestionsRD$: Observable; constructor( - private translateService: TranslateService, private suggestionTargetsStateService: SuggestionTargetsStateService, - private notificationsService: NotificationsService, private suggestionsService: SuggestionsService ) { } diff --git a/src/app/suggestion-notifications/suggestions-popup/suggestions-popup.component.html b/src/app/suggestion-notifications/suggestions-popup/suggestions-popup.component.html index 8b137891791..474dfa329e7 100644 --- a/src/app/suggestion-notifications/suggestions-popup/suggestions-popup.component.html +++ b/src/app/suggestion-notifications/suggestions-popup/suggestions-popup.component.html @@ -1 +1,27 @@ +
    + +
    + diff --git a/src/app/suggestion-notifications/suggestions-popup/suggestions-popup.component.spec.ts b/src/app/suggestion-notifications/suggestions-popup/suggestions-popup.component.spec.ts index 8a5721968bc..c8d59115d81 100644 --- a/src/app/suggestion-notifications/suggestions-popup/suggestions-popup.component.spec.ts +++ b/src/app/suggestion-notifications/suggestions-popup/suggestions-popup.component.spec.ts @@ -3,8 +3,6 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { SuggestionsPopupComponent } from './suggestions-popup.component'; import { TranslateModule } from '@ngx-translate/core'; import { SuggestionTargetsStateService } from '../suggestion-targets/suggestion-targets.state.service'; -import { NotificationsService } from '../../shared/notifications/notifications.service'; -import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { of as observableOf } from 'rxjs'; import { mockSuggestionTargetsObjectOne } from '../../shared/mocks/publication-claim-targets.mock'; @@ -13,7 +11,6 @@ import { SuggestionsService } from '../suggestions.service'; describe('SuggestionsPopupComponent', () => { let component: SuggestionsPopupComponent; let fixture: ComponentFixture; - let notificationsService: NotificationsService; const suggestionStateService = jasmine.createSpyObj('SuggestionTargetsStateService', { hasUserVisitedSuggestions: jasmine.createSpy('hasUserVisitedSuggestions'), @@ -35,7 +32,6 @@ describe('SuggestionsPopupComponent', () => { providers: [ { provide: SuggestionTargetsStateService, useValue: suggestionStateService }, { provide: SuggestionsService, useValue: suggestionService }, - { provide: NotificationsService, useValue: new NotificationsServiceStub() }, ], schemas: [NO_ERRORS_SCHEMA] @@ -69,12 +65,10 @@ describe('SuggestionsPopupComponent', () => { fixture = TestBed.createComponent(SuggestionsPopupComponent); component = fixture.componentInstance; - notificationsService = (component as any).notificationsService; fixture.detectChanges(); }); it('should show a notification when new publication suggestions are available', () => { - expect(notificationsService.success).toHaveBeenCalled(); expect(suggestionStateService.dispatchRefreshUserSuggestionsAction).toHaveBeenCalled(); expect(suggestionStateService.dispatchMarkUserSuggestionsAsVisitedAction).toHaveBeenCalled(); }); diff --git a/src/app/suggestion-notifications/suggestions-popup/suggestions-popup.component.ts b/src/app/suggestion-notifications/suggestions-popup/suggestions-popup.component.ts index 0b2176ea892..2cf3db128e6 100644 --- a/src/app/suggestion-notifications/suggestions-popup/suggestions-popup.component.ts +++ b/src/app/suggestion-notifications/suggestions-popup/suggestions-popup.component.ts @@ -1,12 +1,14 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; import { SuggestionTargetsStateService } from '../suggestion-targets/suggestion-targets.state.service'; -import { NotificationsService } from '../../shared/notifications/notifications.service'; import { SuggestionsService } from '../suggestions.service'; import { take, takeUntil } from 'rxjs/operators'; import { SuggestionTarget } from '../../core/suggestion-notifications/models/suggestion-target.model'; import { isNotEmpty } from '../../shared/empty.util'; -import { combineLatest, Subject } from 'rxjs'; +import { combineLatest, Observable, of, Subject } from 'rxjs'; +import { trigger } from '@angular/animations'; + + +import { fromTopEnter } from '../../shared/animations/fromTop'; /** * Show suggestions on a popover window, used on the homepage @@ -14,7 +16,12 @@ import { combineLatest, Subject } from 'rxjs'; @Component({ selector: 'ds-suggestions-popup', templateUrl: './suggestions-popup.component.html', - styleUrls: ['./suggestions-popup.component.scss'] + styleUrls: ['./suggestions-popup.component.scss'], + animations: [ + trigger('enterLeave', [ + fromTopEnter + ]) + ], }) export class SuggestionsPopupComponent implements OnInit, OnDestroy { @@ -22,10 +29,11 @@ export class SuggestionsPopupComponent implements OnInit, OnDestroy { subscription; + suggestionsRD$: Observable; + + constructor( - private translateService: TranslateService, private suggestionTargetsStateService: SuggestionTargetsStateService, - private notificationsService: NotificationsService, private suggestionsService: SuggestionsService ) { } @@ -42,7 +50,7 @@ export class SuggestionsPopupComponent implements OnInit, OnDestroy { this.suggestionTargetsStateService.dispatchRefreshUserSuggestionsAction(); if (isNotEmpty(suggestions)) { if (!visited) { - suggestions.forEach((suggestionTarget: SuggestionTarget) => this.showNotificationForNewSuggestions(suggestionTarget)); + this.suggestionsRD$ = of(suggestions); this.suggestionTargetsStateService.dispatchMarkUserSuggestionsAsVisitedAction(); notifier.next(null); notifier.complete(); @@ -51,21 +59,24 @@ export class SuggestionsPopupComponent implements OnInit, OnDestroy { }); } - /** - * Show a notification to user for a new suggestions detected - * @param suggestionTarget - * @private - */ - private showNotificationForNewSuggestions(suggestionTarget: SuggestionTarget): void { - const content = this.translateService.instant(this.labelPrefix + 'suggestion', - this.suggestionsService.getNotificationSuggestionInterpolation(suggestionTarget)); - this.notificationsService.success('', content, {timeOut:0}, true); - } - ngOnDestroy() { if (this.subscription) { this.subscription.unsubscribe(); } } + /** + * Interpolated params to build the notification suggestions notification. + * @param suggestionTarget + */ + public getNotificationSuggestionInterpolation(suggestionTarget: SuggestionTarget): any { + return this.suggestionsService.getNotificationSuggestionInterpolation(suggestionTarget); + } + + /** + * Hide popup from view + */ + public removePopup() { + this.suggestionsRD$ = null; + } } diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 15134a01fd8..05b3964a17f 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -3120,7 +3120,11 @@ "mydspace.import": "Import", - "notification.suggestion": "We found {{count}} publications in the {{source}} that seems to be related to your profile.
    Please review the suggestions", + "notification.suggestion": "We found {{count}} publications in the {{source}} that seems to be related to your profile.
    ", + + "notification.suggestion.review": "review the suggestions", + + "notification.suggestion.please": "Please", "nav.browse.header": "All of DSpace",