diff --git a/src/app/admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component.ts b/src/app/admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component.ts index a19a1f95e4..ac90df07fd 100644 --- a/src/app/admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component.ts +++ b/src/app/admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component.ts @@ -1,10 +1,10 @@ import { Component, Inject, Injector, OnInit } from '@angular/core'; import { MenuSectionComponent } from '../../../shared/menu/menu-section/menu-section.component'; -import { MenuID } from '../../../shared/menu/initial-menus-state'; import { MenuService } from '../../../shared/menu/menu.service'; import { rendersSectionForMenu } from '../../../shared/menu/menu-section.decorator'; import { LinkMenuItemModel } from '../../../shared/menu/menu-item/models/link.model'; -import { MenuSection } from '../../../shared/menu/menu.reducer'; +import { MenuSection } from '../../../shared/menu/menu-section.model'; +import { MenuID } from '../../../shared/menu/menu-id.model'; /** * Represents a non-expandable section in the admin sidebar diff --git a/src/app/admin/admin-sidebar/admin-sidebar.component.ts b/src/app/admin/admin-sidebar/admin-sidebar.component.ts index 53a9ecb2ab..255678e413 100644 --- a/src/app/admin/admin-sidebar/admin-sidebar.component.ts +++ b/src/app/admin/admin-sidebar/admin-sidebar.component.ts @@ -12,7 +12,6 @@ import { EditCollectionSelectorComponent } from '../../shared/dso-selector/modal import { EditCommunitySelectorComponent } from '../../shared/dso-selector/modal-wrappers/edit-community-selector/edit-community-selector.component'; import { EditItemSelectorComponent } from '../../shared/dso-selector/modal-wrappers/edit-item-selector/edit-item-selector.component'; import { ExportMetadataSelectorComponent } from '../../shared/dso-selector/modal-wrappers/export-metadata-selector/export-metadata-selector.component'; -import { MenuID, MenuItemType } from '../../shared/menu/initial-menus-state'; import { LinkMenuItemModel } from '../../shared/menu/menu-item/models/link.model'; import { OnClickMenuItemModel } from '../../shared/menu/menu-item/models/onclick.model'; import { TextMenuItemModel } from '../../shared/menu/menu-item/models/text.model'; @@ -21,6 +20,8 @@ import { MenuService } from '../../shared/menu/menu.service'; 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 { MenuID } from '../../shared/menu/menu-id.model'; +import { MenuItemType } from '../../shared/menu/menu-item-type.model'; /** * Component representing the admin sidebar diff --git a/src/app/admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.ts b/src/app/admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.ts index 112560de16..7cf39a5b6e 100644 --- a/src/app/admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.ts +++ b/src/app/admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.ts @@ -4,11 +4,11 @@ import { AdminSidebarSectionComponent } from '../admin-sidebar-section/admin-sid import { slide } from '../../../shared/animations/slide'; import { CSSVariableService } from '../../../shared/sass-helper/sass-helper.service'; import { bgColor } from '../../../shared/animations/bgColor'; -import { MenuID } from '../../../shared/menu/initial-menus-state'; import { MenuService } from '../../../shared/menu/menu.service'; import { combineLatest as combineLatestObservable, Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { rendersSectionForMenu } from '../../../shared/menu/menu-section.decorator'; +import { MenuID } from '../../../shared/menu/menu-id.model'; /** * Represents a expandable section in the sidebar diff --git a/src/app/app.reducer.ts b/src/app/app.reducer.ts index a02095d834..04ef9d4c13 100644 --- a/src/app/app.reducer.ts +++ b/src/app/app.reducer.ts @@ -22,7 +22,7 @@ import { nameVariantReducer } from './shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/name-variant.reducer'; import { formReducer, FormState } from './shared/form/form.reducer'; -import { menusReducer, MenusState } from './shared/menu/menu.reducer'; +import { menusReducer} from './shared/menu/menu.reducer'; import { notificationsReducer, NotificationsState @@ -49,6 +49,7 @@ import { import { sidebarReducer, SidebarState } from './shared/sidebar/sidebar.reducer'; import { truncatableReducer, TruncatablesState } from './shared/truncatable/truncatable.reducer'; import { ThemeState, themeReducer } from './shared/theme-support/theme.reducer'; +import { MenusState } from './shared/menu/menus-state.model'; export interface AppState { router: fromRouter.RouterReducerState; diff --git a/src/app/bitstream-page/legacy-bitstream-url.resolver.spec.ts b/src/app/bitstream-page/legacy-bitstream-url.resolver.spec.ts index 25e245c5b7..045582cb26 100644 --- a/src/app/bitstream-page/legacy-bitstream-url.resolver.spec.ts +++ b/src/app/bitstream-page/legacy-bitstream-url.resolver.spec.ts @@ -2,8 +2,8 @@ import { LegacyBitstreamUrlResolver } from './legacy-bitstream-url.resolver'; import { of as observableOf, EMPTY } from 'rxjs'; import { BitstreamDataService } from '../core/data/bitstream-data.service'; import { RemoteData } from '../core/data/remote-data'; -import { RequestEntryState } from '../core/data/request.reducer'; import { TestScheduler } from 'rxjs/testing'; +import { RequestEntryState } from '../core/data/request-entry-state.model'; describe(`LegacyBitstreamUrlResolver`, () => { let resolver: LegacyBitstreamUrlResolver; diff --git a/src/app/collection-page/collection-item-mapper/collection-item-mapper.component.spec.ts b/src/app/collection-page/collection-item-mapper/collection-item-mapper.component.spec.ts index 5ae1445cef..30ae285059 100644 --- a/src/app/collection-page/collection-item-mapper/collection-item-mapper.component.spec.ts +++ b/src/app/collection-page/collection-item-mapper/collection-item-mapper.component.spec.ts @@ -16,7 +16,7 @@ import { Collection } from '../../core/shared/collection.model'; import { RemoteData } from '../../core/data/remote-data'; import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; -import { EventEmitter } from '@angular/core'; +import { ChangeDetectionStrategy, EventEmitter } from '@angular/core'; import { HostWindowService } from '../../shared/host-window.service'; import { HostWindowServiceStub } from '../../shared/testing/host-window-service.stub'; import { By } from '@angular/platform-browser'; @@ -41,6 +41,8 @@ import { } from '../../shared/remote-data.utils'; import { createPaginatedList } from '../../shared/testing/utils.test'; import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; +import { MyDSpacePageComponent, SEARCH_CONFIG_SERVICE } from '../../my-dspace-page/my-dspace-page.component'; +import { SearchConfigurationServiceStub } from '../../shared/testing/search-configuration-service.stub'; describe('CollectionItemMapperComponent', () => { let comp: CollectionItemMapperComponent; @@ -159,6 +161,14 @@ describe('CollectionItemMapperComponent', () => { { provide: RouteService, useValue: routeServiceStub }, { provide: AuthorizationDataService, useValue: authorizationDataService } ] + }).overrideComponent(CollectionItemMapperComponent, { + set: { + providers: [ + { + provide: SEARCH_CONFIG_SERVICE, + useClass: SearchConfigurationServiceStub + } + ] } }).compileComponents(); })); diff --git a/src/app/collection-page/collection-page-routing.module.ts b/src/app/collection-page/collection-page-routing.module.ts index 5879e523af..29e342f140 100644 --- a/src/app/collection-page/collection-page-routing.module.ts +++ b/src/app/collection-page/collection-page-routing.module.ts @@ -18,9 +18,9 @@ import { COLLECTION_CREATE_PATH } from './collection-page-routing-paths'; import { CollectionPageAdministratorGuard } from './collection-page-administrator.guard'; -import { MenuItemType } from '../shared/menu/initial-menus-state'; import { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model'; import { ThemedCollectionPageComponent } from './themed-collection-page.component'; +import { MenuItemType } from '../shared/menu/menu-item-type.model'; @NgModule({ imports: [ diff --git a/src/app/collection-page/collection-page.component.ts b/src/app/collection-page/collection-page.component.ts index 366e1da7b1..5753aced0b 100644 --- a/src/app/collection-page/collection-page.component.ts +++ b/src/app/collection-page/collection-page.component.ts @@ -21,7 +21,6 @@ import { Item } from '../core/shared/item.model'; import { getAllSucceededRemoteDataPayload, getFirstSucceededRemoteData, - redirectOn4xx, toDSpaceObjectListRD } from '../core/shared/operators'; @@ -33,6 +32,7 @@ import { PaginationService } from '../core/pagination/pagination.service'; import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service'; import { FeatureID } from '../core/data/feature-authorization/feature-id'; import { getCollectionPageRoute } from './collection-page-routing-paths'; +import { redirectOn4xx } from '../core/shared/authorized.operators'; @Component({ selector: 'ds-collection-page', diff --git a/src/app/collection-page/edit-collection-page/collection-source/collection-source.component.spec.ts b/src/app/collection-page/edit-collection-page/collection-source/collection-source.component.spec.ts index 869238b956..ddee57afb4 100644 --- a/src/app/collection-page/edit-collection-page/collection-source/collection-source.component.spec.ts +++ b/src/app/collection-page/edit-collection-page/collection-source/collection-source.component.spec.ts @@ -9,7 +9,6 @@ import { ContentSource, ContentSourceHarvestType } from '../../../core/shared/co import { ObjectUpdatesService } from '../../../core/data/object-updates/object-updates.service'; import { INotification, Notification } from '../../../shared/notifications/models/notification.model'; import { NotificationType } from '../../../shared/notifications/models/notification-type'; -import { FieldUpdate } from '../../../core/data/object-updates/object-updates.reducer'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; import { DynamicFormControlModel, DynamicFormService } from '@ng-dynamic-forms/core'; import { hasValue } from '../../../shared/empty.util'; @@ -20,6 +19,7 @@ import { Collection } from '../../../core/shared/collection.model'; import { CollectionDataService } from '../../../core/data/collection-data.service'; import { RequestService } from '../../../core/data/request.service'; import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; +import { FieldUpdate } from '../../../core/data/object-updates/field-update.model'; const infoNotification: INotification = new Notification('id', NotificationType.Info, 'info'); const warningNotification: INotification = new Notification('id', NotificationType.Warning, 'warning'); diff --git a/src/app/collection-page/edit-collection-page/collection-source/collection-source.component.ts b/src/app/collection-page/edit-collection-page/collection-source/collection-source.component.ts index c4b42d028d..0d3d48e359 100644 --- a/src/app/collection-page/edit-collection-page/collection-source/collection-source.component.ts +++ b/src/app/collection-page/edit-collection-page/collection-source/collection-source.component.ts @@ -23,7 +23,6 @@ import { RemoteData } from '../../../core/data/remote-data'; import { Collection } from '../../../core/shared/collection.model'; import { first, map, switchMap, take } from 'rxjs/operators'; import { ActivatedRoute, Router } from '@angular/router'; -import { FieldUpdate, FieldUpdates } from '../../../core/data/object-updates/object-updates.reducer'; import { cloneDeep } from 'lodash'; import { CollectionDataService } from '../../../core/data/collection-data.service'; import { getFirstSucceededRemoteData, getFirstCompletedRemoteData } from '../../../core/shared/operators'; @@ -31,6 +30,8 @@ import { MetadataConfig } from '../../../core/shared/metadata-config.model'; import { INotification } from '../../../shared/notifications/models/notification.model'; import { RequestService } from '../../../core/data/request.service'; import { environment } from '../../../../environments/environment'; +import { FieldUpdate } from '../../../core/data/object-updates/field-update.model'; +import { FieldUpdates } from '../../../core/data/object-updates/field-updates.model'; /** * Component for managing the content source of the collection diff --git a/src/app/community-list-page/community-list-datasource.ts b/src/app/community-list-page/community-list-datasource.ts index 80ee51cd34..f234125373 100644 --- a/src/app/community-list-page/community-list-datasource.ts +++ b/src/app/community-list-page/community-list-datasource.ts @@ -1,10 +1,11 @@ import { Subscription } from 'rxjs/internal/Subscription'; import { FindListOptions } from '../core/data/request.models'; import { hasValue } from '../shared/empty.util'; -import { CommunityListService, FlatNode } from './community-list-service'; +import { CommunityListService} from './community-list-service'; import { CollectionViewer, DataSource } from '@angular/cdk/collections'; import { BehaviorSubject, Observable, } from 'rxjs'; import { finalize } from 'rxjs/operators'; +import { FlatNode } from './flat-node.model'; /** * DataSource object needed by a CDK Tree to render its nodes. diff --git a/src/app/community-list-page/community-list-service.spec.ts b/src/app/community-list-page/community-list-service.spec.ts index fe53a98257..f0e6912826 100644 --- a/src/app/community-list-page/community-list-service.spec.ts +++ b/src/app/community-list-page/community-list-service.spec.ts @@ -7,13 +7,14 @@ import { SortDirection, SortOptions } from '../core/cache/models/sort-options.mo import { buildPaginatedList } from '../core/data/paginated-list.model'; import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils'; import { StoreMock } from '../shared/testing/store.mock'; -import { CommunityListService, FlatNode, toFlatNode } from './community-list-service'; +import { CommunityListService, toFlatNode } from './community-list-service'; import { CollectionDataService } from '../core/data/collection-data.service'; import { CommunityDataService } from '../core/data/community-data.service'; import { Community } from '../core/shared/community.model'; import { Collection } from '../core/shared/collection.model'; import { FindListOptions } from '../core/data/request.models'; import { PageInfo } from '../core/shared/page-info.model'; +import { FlatNode } from './flat-node.model'; describe('CommunityListService', () => { let store: StoreMock; diff --git a/src/app/community-list-page/community-list-service.ts b/src/app/community-list-page/community-list-service.ts index 76d33585da..b5b6ffa3f5 100644 --- a/src/app/community-list-page/community-list-service.ts +++ b/src/app/community-list-page/community-list-service.ts @@ -12,39 +12,16 @@ import { Collection } from '../core/shared/collection.model'; import { PageInfo } from '../core/shared/page-info.model'; import { hasValue, isNotEmpty } from '../shared/empty.util'; import { RemoteData } from '../core/data/remote-data'; -import { PaginatedList, buildPaginatedList } from '../core/data/paginated-list.model'; +import { buildPaginatedList, PaginatedList } from '../core/data/paginated-list.model'; import { CollectionDataService } from '../core/data/collection-data.service'; import { CommunityListSaveAction } from './community-list.actions'; import { CommunityListState } from './community-list.reducer'; import { getCommunityPageRoute } from '../community-page/community-page-routing-paths'; import { getCollectionPageRoute } from '../collection-page/collection-page-routing-paths'; -import { getFirstSucceededRemoteData, getFirstCompletedRemoteData } from '../core/shared/operators'; +import { getFirstCompletedRemoteData, getFirstSucceededRemoteData } from '../core/shared/operators'; import { followLink } from '../shared/utils/follow-link-config.model'; - -/** - * Each node in the tree is represented by a flatNode which contains info about the node itself and its position and - * state in the tree. There are nodes representing communities, collections and show more links. - */ -export interface FlatNode { - isExpandable$: Observable; - name: string; - id: string; - level: number; - isExpanded?: boolean; - parent?: FlatNode; - payload: Community | Collection | ShowMoreFlatNode; - isShowMoreNode: boolean; - route?: string; - currentCommunityPage?: number; - currentCollectionPage?: number; -} - -/** - * The show more links in the community tree are also represented by a flatNode so we know where in - * the tree it should be rendered an who its parent is (needed for the action resulting in clicking this link) - */ -export class ShowMoreFlatNode { -} +import { FlatNode } from './flat-node.model'; +import { ShowMoreFlatNode } from './show-more-flat-node.model'; // Helper method to combine an flatten an array of observables of flatNode arrays export const combineAndFlatten = (obsList: Observable[]): Observable => diff --git a/src/app/community-list-page/community-list.actions.ts b/src/app/community-list-page/community-list.actions.ts index 1d2f732ac4..8e8d6d87cf 100644 --- a/src/app/community-list-page/community-list.actions.ts +++ b/src/app/community-list-page/community-list.actions.ts @@ -1,6 +1,6 @@ import { Action } from '@ngrx/store'; import { type } from '../shared/ngrx/type'; -import { FlatNode } from './community-list-service'; +import { FlatNode } from './flat-node.model'; /** * All the action types of the community-list diff --git a/src/app/community-list-page/community-list.reducer.ts b/src/app/community-list-page/community-list.reducer.ts index 236201b353..99c8350cf4 100644 --- a/src/app/community-list-page/community-list.reducer.ts +++ b/src/app/community-list-page/community-list.reducer.ts @@ -1,5 +1,5 @@ -import { FlatNode } from './community-list-service'; import { CommunityListActions, CommunityListActionTypes, CommunityListSaveAction } from './community-list.actions'; +import { FlatNode } from './flat-node.model'; /** * States we wish to put in store concerning the community list diff --git a/src/app/community-list-page/community-list/community-list.component.spec.ts b/src/app/community-list-page/community-list/community-list.component.spec.ts index 1f020b7744..575edf14e8 100644 --- a/src/app/community-list-page/community-list/community-list.component.spec.ts +++ b/src/app/community-list-page/community-list/community-list.component.spec.ts @@ -1,7 +1,7 @@ import { ComponentFixture, fakeAsync, inject, TestBed, tick, waitForAsync } from '@angular/core/testing'; import { CommunityListComponent } from './community-list.component'; -import { CommunityListService, FlatNode, showMoreFlatNode, toFlatNode } from '../community-list-service'; +import { CommunityListService, showMoreFlatNode, toFlatNode } from '../community-list-service'; import { CdkTreeModule } from '@angular/cdk/tree'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { TranslateLoaderMock } from '../../shared/mocks/translate-loader.mock'; @@ -15,6 +15,7 @@ import { Collection } from '../../core/shared/collection.model'; import { of as observableOf } from 'rxjs'; import { By } from '@angular/platform-browser'; import { isEmpty, isNotEmpty } from '../../shared/empty.util'; +import { FlatNode } from '../flat-node.model'; describe('CommunityListComponent', () => { let component: CommunityListComponent; diff --git a/src/app/community-list-page/community-list/community-list.component.ts b/src/app/community-list-page/community-list/community-list.component.ts index 49065c5ec5..d92c1c3860 100644 --- a/src/app/community-list-page/community-list/community-list.component.ts +++ b/src/app/community-list-page/community-list/community-list.component.ts @@ -2,10 +2,11 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { take } from 'rxjs/operators'; import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; import { FindListOptions } from '../../core/data/request.models'; -import { CommunityListService, FlatNode } from '../community-list-service'; +import { CommunityListService} from '../community-list-service'; import { CommunityListDatasource } from '../community-list-datasource'; import { FlatTreeControl } from '@angular/cdk/tree'; import { isEmpty } from '../../shared/empty.util'; +import { FlatNode } from '../flat-node.model'; /** * A tree-structured list of nodes representing the communities, their subCommunities and collections. diff --git a/src/app/community-list-page/flat-node.model.ts b/src/app/community-list-page/flat-node.model.ts new file mode 100644 index 0000000000..0aabbeb489 --- /dev/null +++ b/src/app/community-list-page/flat-node.model.ts @@ -0,0 +1,22 @@ +import { Observable } from 'rxjs'; +import { Community } from '../core/shared/community.model'; +import { Collection } from '../core/shared/collection.model'; +import { ShowMoreFlatNode } from './show-more-flat-node.model'; + +/** + * Each node in the tree is represented by a flatNode which contains info about the node itself and its position and + * state in the tree. There are nodes representing communities, collections and show more links. + */ +export interface FlatNode { + isExpandable$: Observable; + name: string; + id: string; + level: number; + isExpanded?: boolean; + parent?: FlatNode; + payload: Community | Collection | ShowMoreFlatNode; + isShowMoreNode: boolean; + route?: string; + currentCommunityPage?: number; + currentCollectionPage?: number; +} diff --git a/src/app/community-list-page/show-more-flat-node.model.ts b/src/app/community-list-page/show-more-flat-node.model.ts new file mode 100644 index 0000000000..801c9e7388 --- /dev/null +++ b/src/app/community-list-page/show-more-flat-node.model.ts @@ -0,0 +1,6 @@ +/** + * The show more links in the community tree are also represented by a flatNode so we know where in + * the tree it should be rendered an who its parent is (needed for the action resulting in clicking this link) + */ +export class ShowMoreFlatNode { +} diff --git a/src/app/community-page/community-page-routing.module.ts b/src/app/community-page/community-page-routing.module.ts index ad1b1fd2f2..25326448a8 100644 --- a/src/app/community-page/community-page-routing.module.ts +++ b/src/app/community-page/community-page-routing.module.ts @@ -11,9 +11,9 @@ import { DSOBreadcrumbsService } from '../core/breadcrumbs/dso-breadcrumbs.servi import { LinkService } from '../core/cache/builders/link.service'; import { COMMUNITY_EDIT_PATH, COMMUNITY_CREATE_PATH } from './community-page-routing-paths'; import { CommunityPageAdministratorGuard } from './community-page-administrator.guard'; -import { MenuItemType } from '../shared/menu/initial-menus-state'; import { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model'; import { ThemedCommunityPageComponent } from './themed-community-page.component'; +import { MenuItemType } from '../shared/menu/menu-item-type.model'; @NgModule({ imports: [ diff --git a/src/app/community-page/community-page.component.ts b/src/app/community-page/community-page.component.ts index 70259a599b..b1a0cfc946 100644 --- a/src/app/community-page/community-page.component.ts +++ b/src/app/community-page/community-page.component.ts @@ -13,11 +13,12 @@ import { MetadataService } from '../core/metadata/metadata.service'; import { fadeInOut } from '../shared/animations/fade'; import { hasValue } from '../shared/empty.util'; -import { getAllSucceededRemoteDataPayload, redirectOn4xx } from '../core/shared/operators'; +import { getAllSucceededRemoteDataPayload} from '../core/shared/operators'; import { AuthService } from '../core/auth/auth.service'; import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service'; import { FeatureID } from '../core/data/feature-authorization/feature-id'; import { getCommunityPageRoute } from './community-page-routing-paths'; +import { redirectOn4xx } from '../core/shared/authorized.operators'; @Component({ selector: 'ds-community-page', diff --git a/src/app/core/auth/models/auth-status.model.ts b/src/app/core/auth/models/auth-status.model.ts index 197c025407..fbe6ed6476 100644 --- a/src/app/core/auth/models/auth-status.model.ts +++ b/src/app/core/auth/models/auth-status.model.ts @@ -2,7 +2,6 @@ import { autoserialize, deserialize, deserializeAs } from 'cerialize'; import { Observable } from 'rxjs'; import { link, typedObject } from '../../cache/builders/build-decorators'; import { IDToUUIDSerializer } from '../../cache/id-to-uuid-serializer'; -import { CacheableObject } from '../../cache/object-cache.reducer'; import { RemoteData } from '../../data/remote-data'; import { EPerson } from '../../eperson/models/eperson.model'; import { EPERSON } from '../../eperson/models/eperson.resource-type'; @@ -13,6 +12,7 @@ import { AuthError } from './auth-error.model'; import { AUTH_STATUS } from './auth-status.resource-type'; import { AuthTokenInfo } from './auth-token-info.model'; import { AuthMethod } from './auth.method'; +import { CacheableObject } from '../../cache/cacheable-object.model'; /** * Object that represents the authenticated status of a user diff --git a/src/app/core/auth/models/short-lived-token.model.ts b/src/app/core/auth/models/short-lived-token.model.ts index 118c724328..3786bd8e6a 100644 --- a/src/app/core/auth/models/short-lived-token.model.ts +++ b/src/app/core/auth/models/short-lived-token.model.ts @@ -1,10 +1,10 @@ -import { CacheableObject } from '../../cache/object-cache.reducer'; import { typedObject } from '../../cache/builders/build-decorators'; import { excludeFromEquals } from '../../utilities/equals.decorators'; import { autoserialize, autoserializeAs, deserialize } from 'cerialize'; import { ResourceType } from '../../shared/resource-type'; import { SHORT_LIVED_TOKEN } from './short-lived-token.resource-type'; import { HALLink } from '../../shared/hal-link.model'; +import { CacheableObject } from '../../cache/cacheable-object.model'; /** * A short-lived token that can be used to authenticate a rest request diff --git a/src/app/core/cache/builders/build-decorators.ts b/src/app/core/cache/builders/build-decorators.ts index b561ababde..193eeb57e8 100644 --- a/src/app/core/cache/builders/build-decorators.ts +++ b/src/app/core/cache/builders/build-decorators.ts @@ -4,11 +4,11 @@ import { GenericConstructor } from '../../shared/generic-constructor'; import { HALResource } from '../../shared/hal-resource.model'; import { ResourceType } from '../../shared/resource-type'; import { - CacheableObject, - TypedObject, getResourceTypeValueFor } from '../object-cache.reducer'; import { InjectionToken } from '@angular/core'; +import { CacheableObject } from '../cacheable-object.model'; +import { TypedObject } from '../typed-object.model'; export const DATA_SERVICE_FACTORY = new InjectionToken<(resourceType: ResourceType) => GenericConstructor>('getDataServiceFor', { providedIn: 'root', diff --git a/src/app/core/cache/builders/remote-data-build.service.spec.ts b/src/app/core/cache/builders/remote-data-build.service.spec.ts index 0cb45733a6..adda461718 100644 --- a/src/app/core/cache/builders/remote-data-build.service.spec.ts +++ b/src/app/core/cache/builders/remote-data-build.service.spec.ts @@ -13,10 +13,11 @@ import { RequestService } from '../../data/request.service'; import { UnCacheableObject } from '../../shared/uncacheable-object.model'; import { RemoteData } from '../../data/remote-data'; import { Observable, of as observableOf } from 'rxjs'; -import { RequestEntry, RequestEntryState } from '../../data/request.reducer'; +import { RequestEntry} from '../../data/request.reducer'; import { followLink, FollowLinkConfig } from '../../../shared/utils/follow-link-config.model'; import { take } from 'rxjs/operators'; import { HALLink } from '../../shared/hal-link.model'; +import { RequestEntryState } from '../../data/request-entry-state.model'; describe('RemoteDataBuildService', () => { let service: RemoteDataBuildService; 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 6b67549f2d..1ae2ef961e 100644 --- a/src/app/core/cache/builders/remote-data-build.service.ts +++ b/src/app/core/cache/builders/remote-data-build.service.ts @@ -13,12 +13,9 @@ import { PaginatedList } from '../../data/paginated-list.model'; import { RemoteData } from '../../data/remote-data'; import { RequestEntry, - ResponseState, - RequestEntryState, - hasSucceeded + ResponseState } from '../../data/request.reducer'; import { RequestService } from '../../data/request.service'; -import { getRequestFromRequestHref, getRequestFromRequestUUID } from '../../shared/operators'; import { ObjectCacheService } from '../object-cache.service'; import { LinkService } from './link.service'; import { HALLink } from '../../shared/hal-link.model'; @@ -28,6 +25,8 @@ import { HALResource } from '../../shared/hal-resource.model'; import { PAGINATED_LIST } from '../../data/paginated-list.resource-type'; import { getUrlWithoutEmbedParams } from '../../index/index.selectors'; import { getResourceTypeValueFor } from '../object-cache.reducer'; +import { hasSucceeded, RequestEntryState } from '../../data/request-entry-state.model'; +import { getRequestFromRequestHref, getRequestFromRequestUUID } from '../../shared/request.operators'; @Injectable() export class RemoteDataBuildService { diff --git a/src/app/core/cache/cacheable-object.model.ts b/src/app/core/cache/cacheable-object.model.ts new file mode 100644 index 0000000000..b7d1609d58 --- /dev/null +++ b/src/app/core/cache/cacheable-object.model.ts @@ -0,0 +1,22 @@ +/* tslint:disable:max-classes-per-file */ +import { HALResource } from '../shared/hal-resource.model'; +import { HALLink } from '../shared/hal-link.model'; +import { TypedObject } from './typed-object.model'; + +/** + * An interface to represent objects that can be cached + * + * A cacheable object should have a self link + */ +export class CacheableObject extends TypedObject implements HALResource { + uuid?: string; + handle?: string; + _links: { + self: HALLink; + }; + // isNew: boolean; + // dirtyType: DirtyType; + // hasDirtyAttributes: boolean; + // changedAttributes: AttributeDiffh; + // save(): void; +} diff --git a/src/app/core/cache/object-cache.actions.ts b/src/app/core/cache/object-cache.actions.ts index ed509341a7..aa7ee00ff5 100644 --- a/src/app/core/cache/object-cache.actions.ts +++ b/src/app/core/cache/object-cache.actions.ts @@ -1,8 +1,8 @@ import { Action } from '@ngrx/store'; import { type } from '../../shared/ngrx/type'; -import { CacheableObject } from './object-cache.reducer'; import { Operation } from 'fast-json-patch'; +import { CacheableObject } from './cacheable-object.model'; /** * The list of ObjectCacheAction type definitions diff --git a/src/app/core/cache/object-cache.reducer.ts b/src/app/core/cache/object-cache.reducer.ts index 8c1420704c..36319ecf3c 100644 --- a/src/app/core/cache/object-cache.reducer.ts +++ b/src/app/core/cache/object-cache.reducer.ts @@ -1,18 +1,16 @@ -import { HALLink } from '../shared/hal-link.model'; -import { HALResource } from '../shared/hal-resource.model'; import { + AddPatchObjectCacheAction, + AddToObjectCacheAction, + ApplyPatchObjectCacheAction, ObjectCacheAction, ObjectCacheActionTypes, - AddToObjectCacheAction, RemoveFromObjectCacheAction, - ResetObjectCacheTimestampsAction, - AddPatchObjectCacheAction, - ApplyPatchObjectCacheAction + ResetObjectCacheTimestampsAction } from './object-cache.actions'; import { hasValue, isNotEmpty } from '../../shared/empty.util'; import { CacheEntry } from './cache-entry'; -import { ResourceType } from '../shared/resource-type'; import { applyPatch, Operation } from 'fast-json-patch'; +import { CacheableObject } from './cacheable-object.model'; /** * An interface to represent a JsonPatch @@ -29,11 +27,6 @@ export interface Patch { operations: Operation[]; } -export abstract class TypedObject { - static type: ResourceType; - type: ResourceType; -} - /** * Get the string value for an object that may be a string or a ResourceType * @@ -49,25 +42,6 @@ export const getResourceTypeValueFor = (type: any): string => { } }; -/* tslint:disable:max-classes-per-file */ -/** - * An interface to represent objects that can be cached - * - * A cacheable object should have a self link - */ -export class CacheableObject extends TypedObject implements HALResource { - uuid?: string; - handle?: string; - _links: { - self: HALLink; - }; - // isNew: boolean; - // dirtyType: DirtyType; - // hasDirtyAttributes: boolean; - // changedAttributes: AttributeDiffh; - // save(): void; -} - /** * An entry in the ObjectCache */ diff --git a/src/app/core/cache/object-cache.service.spec.ts b/src/app/core/cache/object-cache.service.spec.ts index 6863361c34..e9cc7694a7 100644 --- a/src/app/core/cache/object-cache.service.spec.ts +++ b/src/app/core/cache/object-cache.service.spec.ts @@ -20,10 +20,10 @@ import { Patch } from './object-cache.reducer'; import { ObjectCacheService } from './object-cache.service'; import { AddToSSBAction } from './server-sync-buffer.actions'; import { RemoveFromIndexBySubstringAction } from '../index/index.actions'; -import { IndexName } from '../index/index.reducer'; import { HALLink } from '../shared/hal-link.model'; import { storeModuleConfig } from '../../app.reducer'; import { TestColdObservable } from 'jasmine-marbles/src/test-observables'; +import { IndexName } from '../index/index-name.model'; describe('ObjectCacheService', () => { let service: ObjectCacheService; diff --git a/src/app/core/cache/object-cache.service.ts b/src/app/core/cache/object-cache.service.ts index 5fec462670..8b9660a668 100644 --- a/src/app/core/cache/object-cache.service.ts +++ b/src/app/core/cache/object-cache.service.ts @@ -22,11 +22,12 @@ import { RemoveFromObjectCacheAction } from './object-cache.actions'; -import { CacheableObject, ObjectCacheEntry, ObjectCacheState } from './object-cache.reducer'; +import { ObjectCacheEntry, ObjectCacheState } from './object-cache.reducer'; import { AddToSSBAction } from './server-sync-buffer.actions'; import { RemoveFromIndexBySubstringAction } from '../index/index.actions'; -import { IndexName } from '../index/index.reducer'; import { HALLink } from '../shared/hal-link.model'; +import { CacheableObject } from './cacheable-object.model'; +import { IndexName } from '../index/index-name.model'; /** * The base selector function to select the object cache in the store diff --git a/src/app/core/cache/response.models.ts b/src/app/core/cache/response.models.ts index 3c7c272830..c8a1052f98 100644 --- a/src/app/core/cache/response.models.ts +++ b/src/app/core/cache/response.models.ts @@ -1,9 +1,9 @@ -import { RequestError } from '../data/request.models'; import { PageInfo } from '../shared/page-info.model'; import { ConfigObject } from '../config/models/config.model'; import { DSpaceObject } from '../shared/dspace-object.model'; import { HALLink } from '../shared/hal-link.model'; import { UnCacheableObject } from '../shared/uncacheable-object.model'; +import { RequestError } from '../data/request-error.model'; /* tslint:disable:max-classes-per-file */ export class RestResponse { diff --git a/src/app/core/cache/typed-object.model.ts b/src/app/core/cache/typed-object.model.ts new file mode 100644 index 0000000000..02a530941a --- /dev/null +++ b/src/app/core/cache/typed-object.model.ts @@ -0,0 +1,6 @@ +import { ResourceType } from '../shared/resource-type'; + +export abstract class TypedObject { + static type: ResourceType; + type: ResourceType; +} diff --git a/src/app/core/config/models/config.model.ts b/src/app/core/config/models/config.model.ts index 53250ee045..170aa334ed 100644 --- a/src/app/core/config/models/config.model.ts +++ b/src/app/core/config/models/config.model.ts @@ -1,8 +1,8 @@ import { autoserialize, deserialize } from 'cerialize'; -import { CacheableObject } from '../../cache/object-cache.reducer'; import { HALLink } from '../../shared/hal-link.model'; import { ResourceType } from '../../shared/resource-type'; import { excludeFromEquals } from '../../utilities/equals.decorators'; +import { CacheableObject } from '../../cache/cacheable-object.model'; export abstract class ConfigObject implements CacheableObject { diff --git a/src/app/core/data/base-response-parsing.service.spec.ts b/src/app/core/data/base-response-parsing.service.spec.ts index 94285d49d8..c8a9683854 100644 --- a/src/app/core/data/base-response-parsing.service.spec.ts +++ b/src/app/core/data/base-response-parsing.service.spec.ts @@ -1,8 +1,8 @@ import { BaseResponseParsingService } from './base-response-parsing.service'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CacheableObject } from '../cache/object-cache.reducer'; import { GetRequest, RestRequest } from './request.models'; import { DSpaceObject } from '../shared/dspace-object.model'; +import { CacheableObject } from '../cache/cacheable-object.model'; /* tslint:disable:max-classes-per-file */ class TestService extends BaseResponseParsingService { diff --git a/src/app/core/data/base-response-parsing.service.ts b/src/app/core/data/base-response-parsing.service.ts index b571b29f02..61945c3161 100644 --- a/src/app/core/data/base-response-parsing.service.ts +++ b/src/app/core/data/base-response-parsing.service.ts @@ -1,6 +1,5 @@ import { hasNoValue, hasValue, isNotEmpty } from '../../shared/empty.util'; import { DSpaceSerializer } from '../dspace-rest/dspace.serializer'; -import { CacheableObject } from '../cache/object-cache.reducer'; import { Serializer } from '../serializer'; import { PageInfo } from '../shared/page-info.model'; import { ObjectCacheService } from '../cache/object-cache.service'; @@ -9,6 +8,7 @@ import { PaginatedList, buildPaginatedList } from './paginated-list.model'; import { getClassForType } from '../cache/builders/build-decorators'; import { RestRequest } from './request.models'; import { environment } from '../../../environments/environment'; +import { CacheableObject } from '../cache/cacheable-object.model'; /* tslint:disable:max-classes-per-file */ diff --git a/src/app/core/data/bitstream-data.service.ts b/src/app/core/data/bitstream-data.service.ts index 23aec80ff2..ca5e2761d8 100644 --- a/src/app/core/data/bitstream-data.service.ts +++ b/src/app/core/data/bitstream-data.service.ts @@ -25,10 +25,10 @@ import { RequestService } from './request.service'; import { BitstreamFormatDataService } from './bitstream-format-data.service'; import { BitstreamFormat } from '../shared/bitstream-format.model'; import { HttpOptions } from '../dspace-rest/dspace-rest.service'; -import { sendRequest } from '../shared/operators'; import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; import { PageInfo } from '../shared/page-info.model'; import { RequestParam } from '../cache/models/request-param.model'; +import { sendRequest } from '../shared/request.operators'; /** * A service to retrieve {@link Bitstream}s from the REST API diff --git a/src/app/core/data/bitstream-format-data.service.ts b/src/app/core/data/bitstream-format-data.service.ts index 0d0dc5eb63..1f787f4bbc 100644 --- a/src/app/core/data/bitstream-format-data.service.ts +++ b/src/app/core/data/bitstream-format-data.service.ts @@ -19,12 +19,12 @@ import { BitstreamFormat } from '../shared/bitstream-format.model'; import { BITSTREAM_FORMAT } from '../shared/bitstream-format.resource-type'; import { Bitstream } from '../shared/bitstream.model'; import { HALEndpointService } from '../shared/hal-endpoint.service'; -import { sendRequest } from '../shared/operators'; import { DataService } from './data.service'; import { DefaultChangeAnalyzer } from './default-change-analyzer.service'; import { RemoteData } from './remote-data'; import { PostRequest, PutRequest } from './request.models'; import { RequestService } from './request.service'; +import { sendRequest } from '../shared/request.operators'; const bitstreamFormatsStateSelector = createSelector( coreSelector, diff --git a/src/app/core/data/bundle-data.service.ts b/src/app/core/data/bundle-data.service.ts index bff21d2c8d..3aa4efda24 100644 --- a/src/app/core/data/bundle-data.service.ts +++ b/src/app/core/data/bundle-data.service.ts @@ -22,7 +22,7 @@ import { FindListOptions, GetRequest } from './request.models'; import { RequestService } from './request.service'; import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model'; import { Bitstream } from '../shared/bitstream.model'; -import { RequestEntryState } from './request.reducer'; +import { RequestEntryState } from './request-entry-state.model'; /** * A service to retrieve {@link Bundle}s from the REST API diff --git a/src/app/core/data/change-analyzer.ts b/src/app/core/data/change-analyzer.ts index 8efe26314e..45fd9b7e84 100644 --- a/src/app/core/data/change-analyzer.ts +++ b/src/app/core/data/change-analyzer.ts @@ -1,6 +1,6 @@ import { Operation } from 'fast-json-patch'; -import { TypedObject } from '../cache/object-cache.reducer'; +import { TypedObject } from '../cache/typed-object.model'; /** * An interface to determine what differs between two diff --git a/src/app/core/data/collection-data.service.ts b/src/app/core/data/collection-data.service.ts index f58f36450f..e6255144b6 100644 --- a/src/app/core/data/collection-data.service.ts +++ b/src/app/core/data/collection-data.service.ts @@ -214,4 +214,12 @@ export class CollectionDataService extends ComColDataService { findOwningCollectionFor(item: Item): Observable> { return this.findByHref(item._links.owningCollection.href); } + + protected getScopeCommunityHref(options: FindListOptions) { + return this.cds.getEndpoint().pipe( + map((endpoint: string) => this.cds.getIDHref(endpoint, options.scopeID)), + filter((href: string) => isNotEmpty(href)), + take(1) + ); + } } diff --git a/src/app/core/data/comcol-data.service.spec.ts b/src/app/core/data/comcol-data.service.spec.ts index 864c583dc2..3570487875 100644 --- a/src/app/core/data/comcol-data.service.spec.ts +++ b/src/app/core/data/comcol-data.service.spec.ts @@ -25,6 +25,12 @@ import { BitstreamDataService } from './bitstream-data.service'; const LINK_NAME = 'test'; +const scopeID = 'd9d30c0c-69b7-4369-8397-ca67c888974d'; + +const communitiesEndpoint = 'https://rest.api/core/communities'; + +const communityEndpoint = `${communitiesEndpoint}/${scopeID}`; + class TestService extends ComColDataService { constructor( @@ -47,6 +53,11 @@ class TestService extends ComColDataService { // implementation in subclasses for communities/collections return undefined; } + + protected getScopeCommunityHref(options: FindListOptions): Observable { + // implementation in subclasses for communities/collections + return observableOf(communityEndpoint); + } } // tslint:disable:no-shadowed-variable @@ -66,12 +77,9 @@ describe('ComColDataService', () => { const http = {} as HttpClient; const comparator = {} as any; - const scopeID = 'd9d30c0c-69b7-4369-8397-ca67c888974d'; const options = Object.assign(new FindListOptions(), { scopeID: scopeID }); - const communitiesEndpoint = 'https://rest.api/core/communities'; - const communityEndpoint = `${communitiesEndpoint}/${scopeID}`; const scopedEndpoint = `${communityEndpoint}/${LINK_NAME}`; const mockHalService = { diff --git a/src/app/core/data/comcol-data.service.ts b/src/app/core/data/comcol-data.service.ts index 12aedf8009..cf59c7ac74 100644 --- a/src/app/core/data/comcol-data.service.ts +++ b/src/app/core/data/comcol-data.service.ts @@ -4,8 +4,6 @@ import { hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util'; import { ObjectCacheService } from '../cache/object-cache.service'; import { Community } from '../shared/community.model'; import { HALLink } from '../shared/hal-link.model'; -import { CommunityDataService } from './community-data.service'; - import { DataService } from './data.service'; import { FindListOptions } from './request.models'; import { PaginatedList } from './paginated-list.model'; @@ -21,7 +19,6 @@ import { URLCombiner } from '../url-combiner/url-combiner'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; export abstract class ComColDataService extends DataService { - protected abstract cds: CommunityDataService; protected abstract objectCache: ObjectCacheService; protected abstract halService: HALEndpointService; protected abstract bitstreamDataService: BitstreamDataService; @@ -40,11 +37,7 @@ export abstract class ComColDataService extend if (isEmpty(options.scopeID)) { return this.halService.getEndpoint(linkPath); } else { - const scopeCommunityHrefObs = this.cds.getEndpoint().pipe( - map((endpoint: string) => this.cds.getIDHref(endpoint, options.scopeID)), - filter((href: string) => isNotEmpty(href)), - take(1) - ); + const scopeCommunityHrefObs = this.getScopeCommunityHref(options); this.createAndSendGetRequest(scopeCommunityHrefObs, true); @@ -65,6 +58,8 @@ export abstract class ComColDataService extend } } + protected abstract getScopeCommunityHref(options: FindListOptions): Observable; + protected abstract getFindByParentHref(parentUUID: string): Observable; public findByParent(parentUUID: string, options: FindListOptions = {}, ...linksToFollow: FollowLinkConfig[]): Observable>> { diff --git a/src/app/core/data/community-data.service.ts b/src/app/core/data/community-data.service.ts index 8dee72e391..82afe561a9 100644 --- a/src/app/core/data/community-data.service.ts +++ b/src/app/core/data/community-data.service.ts @@ -3,7 +3,7 @@ import { Injectable } from '@angular/core'; import { Store } from '@ngrx/store'; import { Observable } from 'rxjs'; -import { switchMap } from 'rxjs/operators'; +import { filter, map, switchMap, take } from 'rxjs/operators'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { dataService } from '../cache/builders/build-decorators'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; @@ -20,13 +20,13 @@ import { FindListOptions } from './request.models'; import { RequestService } from './request.service'; import { BitstreamDataService } from './bitstream-data.service'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; +import { isNotEmpty } from '../../shared/empty.util'; @Injectable() @dataService(COMMUNITY) export class CommunityDataService extends ComColDataService { protected linkPath = 'communities'; protected topLinkPath = 'search/top'; - protected cds = this; constructor( protected requestService: RequestService, @@ -58,4 +58,11 @@ export class CommunityDataService extends ComColDataService { ); } + protected getScopeCommunityHref(options: FindListOptions) { + return this.getEndpoint().pipe( + map((endpoint: string) => this.getIDHref(endpoint, options.scopeID)), + filter((href: string) => isNotEmpty(href)), + take(1) + ); + } } diff --git a/src/app/core/data/data.service.spec.ts b/src/app/core/data/data.service.spec.ts index 5bc7423824..56ef753a79 100644 --- a/src/app/core/data/data.service.spec.ts +++ b/src/app/core/data/data.service.spec.ts @@ -22,7 +22,7 @@ import { RequestParam } from '../cache/models/request-param.model'; import { getMockRemoteDataBuildService } from '../../shared/mocks/remote-data-build.service.mock'; import { TestScheduler } from 'rxjs/testing'; import { RemoteData } from './remote-data'; -import { RequestEntryState } from './request.reducer'; +import { RequestEntryState } from './request-entry-state.model'; const endpoint = 'https://rest.api/core'; diff --git a/src/app/core/data/data.service.ts b/src/app/core/data/data.service.ts index 6bad02e776..12a66e5092 100644 --- a/src/app/core/data/data.service.ts +++ b/src/app/core/data/data.service.ts @@ -21,7 +21,6 @@ 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 { CacheableObject } from '../cache/object-cache.reducer'; import { ObjectCacheService } from '../cache/object-cache.service'; import { CoreState } from '../core.reducers'; import { DSpaceSerializer } from '../dspace-rest/dspace.serializer'; @@ -45,6 +44,7 @@ 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'; export abstract class DataService implements UpdateDataService { protected abstract requestService: RequestService; diff --git a/src/app/core/data/default-change-analyzer.service.ts b/src/app/core/data/default-change-analyzer.service.ts index 34a619648a..e12a1f08d5 100644 --- a/src/app/core/data/default-change-analyzer.service.ts +++ b/src/app/core/data/default-change-analyzer.service.ts @@ -2,9 +2,9 @@ import { Injectable } from '@angular/core'; import { compare } from 'fast-json-patch'; import { Operation } from 'fast-json-patch'; import { getClassForType } from '../cache/builders/build-decorators'; -import { TypedObject } from '../cache/object-cache.reducer'; import { DSpaceSerializer } from '../dspace-rest/dspace.serializer'; import { ChangeAnalyzer } from './change-analyzer'; +import { TypedObject } from '../cache/typed-object.model'; /** * A class to determine what differs between two diff --git a/src/app/core/data/dspace-rest-response-parsing.service.ts b/src/app/core/data/dspace-rest-response-parsing.service.ts index 2fda0bf40a..27af802ce8 100644 --- a/src/app/core/data/dspace-rest-response-parsing.service.ts +++ b/src/app/core/data/dspace-rest-response-parsing.service.ts @@ -1,6 +1,5 @@ import { hasNoValue, hasValue, isNotEmpty } from '../../shared/empty.util'; import { DSpaceSerializer } from '../dspace-rest/dspace.serializer'; -import { CacheableObject } from '../cache/object-cache.reducer'; import { Serializer } from '../serializer'; import { PageInfo } from '../shared/page-info.model'; import { ObjectCacheService } from '../cache/object-cache.service'; @@ -17,6 +16,7 @@ import { ParsedResponse } from '../cache/response.models'; import { RestRequestMethod } from './rest-request-method'; import { getUrlWithoutEmbedParams, getEmbedSizeParams } from '../index/index.selectors'; import { URLCombiner } from '../url-combiner/url-combiner'; +import { CacheableObject } from '../cache/cacheable-object.model'; /* tslint:disable:max-classes-per-file */ diff --git a/src/app/core/data/endpoint-map-response-parsing.service.ts b/src/app/core/data/endpoint-map-response-parsing.service.ts index 1a81deaea0..73396e78bb 100644 --- a/src/app/core/data/endpoint-map-response-parsing.service.ts +++ b/src/app/core/data/endpoint-map-response-parsing.service.ts @@ -11,8 +11,8 @@ import { RestRequest } from './request.models'; import { RawRestResponse } from '../dspace-rest/raw-rest-response.model'; import { ParsedResponse } from '../cache/response.models'; import { DSpaceObject } from '../shared/dspace-object.model'; -import { CacheableObject } from '../cache/object-cache.reducer'; import { environment } from '../../../environments/environment'; +import { CacheableObject } from '../cache/cacheable-object.model'; /** * ResponseParsingService able to deal with HAL Endpoints that are only needed as steps diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.ts index 3a6cf745c9..18c7f8fb43 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.ts @@ -2,9 +2,9 @@ import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTr import { AuthorizationDataService } from '../authorization-data.service'; import { FeatureID } from '../feature-id'; import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs'; -import { returnForbiddenUrlTreeOrLoginOnAllFalse } from '../../../shared/operators'; import { switchMap } from 'rxjs/operators'; import { AuthService } from '../../../auth/auth.service'; +import { returnForbiddenUrlTreeOrLoginOnAllFalse } from '../../../shared/authorized.operators'; /** * Abstract Guard for preventing unauthorized activating and loading of routes when a user diff --git a/src/app/core/data/href-only-data.service.ts b/src/app/core/data/href-only-data.service.ts index c1298c054c..4baffea545 100644 --- a/src/app/core/data/href-only-data.service.ts +++ b/src/app/core/data/href-only-data.service.ts @@ -18,7 +18,7 @@ import { Observable } from 'rxjs/internal/Observable'; import { PaginatedList } from './paginated-list.model'; import { ITEM_TYPE } from '../shared/item-relationships/item-type.resource-type'; import { LICENSE } from '../shared/license.resource-type'; -import { CacheableObject } from '../cache/object-cache.reducer'; +import { CacheableObject } from '../cache/cacheable-object.model'; /* tslint:disable:max-classes-per-file */ class DataServiceImpl extends DataService { diff --git a/src/app/core/data/item-data.service.ts b/src/app/core/data/item-data.service.ts index 7a0116fe86..1c152701a1 100644 --- a/src/app/core/data/item-data.service.ts +++ b/src/app/core/data/item-data.service.ts @@ -16,7 +16,6 @@ import { ExternalSourceEntry } from '../shared/external-source-entry.model'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { Item } from '../shared/item.model'; import { ITEM } from '../shared/item.resource-type'; -import { sendRequest } from '../shared/operators'; import { URLCombiner } from '../url-combiner/url-combiner'; import { DataService } from './data.service'; @@ -34,6 +33,7 @@ import { NoContent } from '../shared/NoContent.model'; import { GenericConstructor } from '../shared/generic-constructor'; import { ResponseParsingService } from './parsing.service'; import { StatusCodeOnlyResponseParsingService } from './status-code-only-response-parsing.service'; +import { sendRequest } from '../shared/request.operators'; @Injectable() @dataService(ITEM) diff --git a/src/app/core/data/object-updates/field-change-type.model.ts b/src/app/core/data/object-updates/field-change-type.model.ts new file mode 100644 index 0000000000..7d8e308945 --- /dev/null +++ b/src/app/core/data/object-updates/field-change-type.model.ts @@ -0,0 +1,8 @@ +/** + * Enum that represents the different types of updates that can be performed on a field in the ObjectUpdates store + */ +export enum FieldChangeType { + UPDATE = 0, + ADD = 1, + REMOVE = 2 +} diff --git a/src/app/core/data/object-updates/field-update.model.ts b/src/app/core/data/object-updates/field-update.model.ts new file mode 100644 index 0000000000..47b6782471 --- /dev/null +++ b/src/app/core/data/object-updates/field-update.model.ts @@ -0,0 +1,10 @@ +import { Identifiable } from './identifiable.model'; +import { FieldChangeType } from './field-change-type.model'; + +/** + * The state of a single field update + */ +export interface FieldUpdate { + field: Identifiable; + changeType: FieldChangeType; +} diff --git a/src/app/core/data/object-updates/field-updates.model.ts b/src/app/core/data/object-updates/field-updates.model.ts new file mode 100644 index 0000000000..eff804bd02 --- /dev/null +++ b/src/app/core/data/object-updates/field-updates.model.ts @@ -0,0 +1,8 @@ +import { FieldUpdate } from './field-update.model'; + +/** + * The states of all field updates available for a single page, mapped by uuid + */ +export interface FieldUpdates { + [uuid: string]: FieldUpdate; +} diff --git a/src/app/core/data/object-updates/identifiable.model.ts b/src/app/core/data/object-updates/identifiable.model.ts new file mode 100644 index 0000000000..7d32859338 --- /dev/null +++ b/src/app/core/data/object-updates/identifiable.model.ts @@ -0,0 +1,6 @@ +/** + * Represents every object that has a UUID + */ +export interface Identifiable { + uuid: string; +} diff --git a/src/app/core/data/object-updates/object-updates.actions.ts b/src/app/core/data/object-updates/object-updates.actions.ts index 13bbabb286..6c09327a42 100644 --- a/src/app/core/data/object-updates/object-updates.actions.ts +++ b/src/app/core/data/object-updates/object-updates.actions.ts @@ -1,9 +1,10 @@ -import {type} from '../../../shared/ngrx/type'; -import {Action} from '@ngrx/store'; -import {Identifiable} from './object-updates.reducer'; -import {INotification} from '../../../shared/notifications/models/notification.model'; +import { type } from '../../../shared/ngrx/type'; +import { Action } from '@ngrx/store'; +import { INotification } from '../../../shared/notifications/models/notification.model'; import { PatchOperationService } from './patch-operation-service/patch-operation.service'; import { GenericConstructor } from '../../shared/generic-constructor'; +import { Identifiable } from './identifiable.model'; +import { FieldChangeType } from './field-change-type.model'; /** * The list of ObjectUpdatesAction type definitions @@ -23,15 +24,6 @@ export const ObjectUpdatesActionTypes = { /* tslint:disable:max-classes-per-file */ -/** - * Enum that represents the different types of updates that can be performed on a field in the ObjectUpdates store - */ -export enum FieldChangeType { - UPDATE = 0, - ADD = 1, - REMOVE = 2 -} - /** * An ngrx action to initialize a new page's fields in the ObjectUpdates state */ diff --git a/src/app/core/data/object-updates/object-updates.reducer.spec.ts b/src/app/core/data/object-updates/object-updates.reducer.spec.ts index 4d7fce24a7..a51a1431bb 100644 --- a/src/app/core/data/object-updates/object-updates.reducer.spec.ts +++ b/src/app/core/data/object-updates/object-updates.reducer.spec.ts @@ -2,7 +2,6 @@ import * as deepFreeze from 'deep-freeze'; import { AddFieldUpdateAction, DiscardObjectUpdatesAction, - FieldChangeType, InitializeFieldsAction, ReinstateObjectUpdatesAction, RemoveAllObjectUpdatesAction, @@ -14,6 +13,7 @@ import { } from './object-updates.actions'; import { OBJECT_UPDATES_TRASH_PATH, objectUpdatesReducer } from './object-updates.reducer'; import { Relationship } from '../../shared/item-relationships/relationship.model'; +import { FieldChangeType } from './field-change-type.model'; class NullAction extends RemoveFieldUpdateAction { type = null; diff --git a/src/app/core/data/object-updates/object-updates.reducer.ts b/src/app/core/data/object-updates/object-updates.reducer.ts index 6dfb4ab584..14bacc52db 100644 --- a/src/app/core/data/object-updates/object-updates.reducer.ts +++ b/src/app/core/data/object-updates/object-updates.reducer.ts @@ -1,16 +1,15 @@ import { AddFieldUpdateAction, DiscardObjectUpdatesAction, - FieldChangeType, InitializeFieldsAction, ObjectUpdatesAction, ObjectUpdatesActionTypes, ReinstateObjectUpdatesAction, RemoveFieldUpdateAction, RemoveObjectUpdatesAction, + SelectVirtualMetadataAction, SetEditableFieldUpdateAction, SetValidFieldUpdateAction, - SelectVirtualMetadataAction, } from './object-updates.actions'; import { hasNoValue, hasValue } from '../../../shared/empty.util'; import { Relationship } from '../../shared/item-relationships/relationship.model'; @@ -18,6 +17,9 @@ import { PatchOperationService } from './patch-operation-service/patch-operation import { Item } from '../../shared/item.model'; import { RelationshipType } from '../../shared/item-relationships/relationship-type.model'; import { GenericConstructor } from '../../shared/generic-constructor'; +import { Identifiable } from './identifiable.model'; +import { FieldUpdates } from './field-updates.model'; +import { FieldChangeType } from './field-change-type.model'; /** * Path where discarded objects are saved @@ -40,28 +42,6 @@ export interface FieldStates { [uuid: string]: FieldState; } -/** - * Represents every object that has a UUID - */ -export interface Identifiable { - uuid: string; -} - -/** - * The state of a single field update - */ -export interface FieldUpdate { - field: Identifiable; - changeType: FieldChangeType; -} - -/** - * The states of all field updates available for a single page, mapped by uuid - */ -export interface FieldUpdates { - [uuid: string]: FieldUpdate; -} - /** * The states of all virtual metadata selections available for a single page, mapped by the relationship uuid */ diff --git a/src/app/core/data/object-updates/object-updates.service.spec.ts b/src/app/core/data/object-updates/object-updates.service.spec.ts index 6c0b0f99c4..b0c63851e3 100644 --- a/src/app/core/data/object-updates/object-updates.service.spec.ts +++ b/src/app/core/data/object-updates/object-updates.service.spec.ts @@ -3,7 +3,6 @@ import { CoreState } from '../../core.reducers'; import { ObjectUpdatesService } from './object-updates.service'; import { DiscardObjectUpdatesAction, - FieldChangeType, InitializeFieldsAction, ReinstateObjectUpdatesAction, RemoveFieldUpdateAction, @@ -16,6 +15,7 @@ import { NotificationType } from '../../../shared/notifications/models/notificat import { OBJECT_UPDATES_TRASH_PATH } from './object-updates.reducer'; import { Relationship } from '../../shared/item-relationships/relationship.model'; import { Injector } from '@angular/core'; +import { FieldChangeType } from './field-change-type.model'; describe('ObjectUpdatesService', () => { let service: ObjectUpdatesService; diff --git a/src/app/core/data/object-updates/object-updates.service.ts b/src/app/core/data/object-updates/object-updates.service.ts index 88c7c0e453..0bdae06ce2 100644 --- a/src/app/core/data/object-updates/object-updates.service.ts +++ b/src/app/core/data/object-updates/object-updates.service.ts @@ -4,8 +4,6 @@ import { CoreState } from '../../core.reducers'; import { coreSelector } from '../../core.selectors'; import { FieldState, - FieldUpdates, - Identifiable, OBJECT_UPDATES_TRASH_PATH, ObjectUpdatesEntry, ObjectUpdatesState, @@ -15,7 +13,6 @@ import { Observable } from 'rxjs'; import { AddFieldUpdateAction, DiscardObjectUpdatesAction, - FieldChangeType, InitializeFieldsAction, ReinstateObjectUpdatesAction, RemoveFieldUpdateAction, @@ -35,6 +32,9 @@ import { INotification } from '../../../shared/notifications/models/notification import { Operation } from 'fast-json-patch'; import { PatchOperationService } from './patch-operation-service/patch-operation.service'; import { GenericConstructor } from '../../shared/generic-constructor'; +import { Identifiable } from './identifiable.model'; +import { FieldUpdates } from './field-updates.model'; +import { FieldChangeType } from './field-change-type.model'; function objectUpdatesStateSelector(): MemoizedSelector { return createSelector(coreSelector, (state: CoreState) => state['cache/object-updates']); diff --git a/src/app/core/data/object-updates/patch-operation-service/metadata-patch-operation.service.spec.ts b/src/app/core/data/object-updates/patch-operation-service/metadata-patch-operation.service.spec.ts index 9708266cd7..db46426b79 100644 --- a/src/app/core/data/object-updates/patch-operation-service/metadata-patch-operation.service.spec.ts +++ b/src/app/core/data/object-updates/patch-operation-service/metadata-patch-operation.service.spec.ts @@ -1,8 +1,8 @@ import { MetadataPatchOperationService } from './metadata-patch-operation.service'; -import { FieldUpdates } from '../object-updates.reducer'; import { Operation } from 'fast-json-patch'; -import { FieldChangeType } from '../object-updates.actions'; import { MetadatumViewModel } from '../../../shared/metadata.models'; +import { FieldUpdates } from '../field-updates.model'; +import { FieldChangeType } from '../field-change-type.model'; describe('MetadataPatchOperationService', () => { let service: MetadataPatchOperationService; diff --git a/src/app/core/data/object-updates/patch-operation-service/metadata-patch-operation.service.ts b/src/app/core/data/object-updates/patch-operation-service/metadata-patch-operation.service.ts index 59c981872a..33e9129a9d 100644 --- a/src/app/core/data/object-updates/patch-operation-service/metadata-patch-operation.service.ts +++ b/src/app/core/data/object-updates/patch-operation-service/metadata-patch-operation.service.ts @@ -1,14 +1,14 @@ import { PatchOperationService } from './patch-operation.service'; import { MetadatumViewModel } from '../../../shared/metadata.models'; -import { FieldUpdates } from '../object-updates.reducer'; import { Operation } from 'fast-json-patch'; -import { FieldChangeType } from '../object-updates.actions'; import { Injectable } from '@angular/core'; import { MetadataPatchOperation } from './operations/metadata/metadata-patch-operation.model'; import { hasValue } from '../../../../shared/empty.util'; import { MetadataPatchAddOperation } from './operations/metadata/metadata-patch-add-operation.model'; import { MetadataPatchRemoveOperation } from './operations/metadata/metadata-patch-remove-operation.model'; import { MetadataPatchReplaceOperation } from './operations/metadata/metadata-patch-replace-operation.model'; +import { FieldUpdates } from '../field-updates.model'; +import { FieldChangeType } from '../field-change-type.model'; /** * Service transforming {@link FieldUpdates} into {@link Operation}s for metadata values diff --git a/src/app/core/data/object-updates/patch-operation-service/patch-operation.service.ts b/src/app/core/data/object-updates/patch-operation-service/patch-operation.service.ts index 7c67f9a2e5..171c1d2a54 100644 --- a/src/app/core/data/object-updates/patch-operation-service/patch-operation.service.ts +++ b/src/app/core/data/object-updates/patch-operation-service/patch-operation.service.ts @@ -1,5 +1,5 @@ -import { FieldUpdates } from '../object-updates.reducer'; import { Operation } from 'fast-json-patch'; +import { FieldUpdates } from '../field-updates.model'; /** * Interface for a service dealing with the transformations of patch operations from the object-updates store diff --git a/src/app/core/data/paginated-list.model.ts b/src/app/core/data/paginated-list.model.ts index e85a91f791..415bfe234e 100644 --- a/src/app/core/data/paginated-list.model.ts +++ b/src/app/core/data/paginated-list.model.ts @@ -3,11 +3,11 @@ import { hasValue, isEmpty, hasNoValue, isUndefined } from '../../shared/empty.u import { HALResource } from '../shared/hal-resource.model'; import { HALLink } from '../shared/hal-link.model'; import { typedObject } from '../cache/builders/build-decorators'; -import { CacheableObject } from '../cache/object-cache.reducer'; import { PAGINATED_LIST } from './paginated-list.resource-type'; import { ResourceType } from '../shared/resource-type'; import { excludeFromEquals } from '../utilities/equals.decorators'; import { autoserialize, deserialize } from 'cerialize'; +import { CacheableObject } from '../cache/cacheable-object.model'; /** * Factory function for a paginated list diff --git a/src/app/core/data/relationship.service.ts b/src/app/core/data/relationship.service.ts index 727cd105e6..7bd36d0cec 100644 --- a/src/app/core/data/relationship.service.ts +++ b/src/app/core/data/relationship.service.ts @@ -29,7 +29,6 @@ import { Relationship } from '../shared/item-relationships/relationship.model'; import { RELATIONSHIP } from '../shared/item-relationships/relationship.resource-type'; import { Item } from '../shared/item.model'; import { - sendRequest, getFirstCompletedRemoteData, getFirstSucceededRemoteData, getFirstSucceededRemoteDataPayload, @@ -42,8 +41,9 @@ import { PaginatedList } from './paginated-list.model'; import { RemoteData } from './remote-data'; import { DeleteRequest, FindListOptions, PostRequest, RestRequest } from './request.models'; import { RequestService } from './request.service'; -import { RequestEntryState } from './request.reducer'; import { NoContent } from '../shared/NoContent.model'; +import { RequestEntryState } from './request-entry-state.model'; +import { sendRequest } from '../shared/request.operators'; const relationshipListsStateSelector = (state: AppState) => state.relationshipLists; diff --git a/src/app/core/data/remote-data.ts b/src/app/core/data/remote-data.ts index 4fa11607df..40c8d7f400 100644 --- a/src/app/core/data/remote-data.ts +++ b/src/app/core/data/remote-data.ts @@ -1,17 +1,17 @@ import { - RequestEntryState, - isStale, hasCompleted, - hasSucceeded, hasFailed, - isLoading, - isSuccessStale, - isErrorStale, - isSuccess, + hasSucceeded, isError, + isErrorStale, + isLoading, + isRequestPending, isResponsePending, - isRequestPending -} from './request.reducer'; + isStale, + isSuccess, + isSuccessStale, + RequestEntryState +} from './request-entry-state.model'; /** * A class to represent the state of a remote resource diff --git a/src/app/core/data/request-entry-state.model.ts b/src/app/core/data/request-entry-state.model.ts new file mode 100644 index 0000000000..a813b6e743 --- /dev/null +++ b/src/app/core/data/request-entry-state.model.ts @@ -0,0 +1,88 @@ +export enum RequestEntryState { + RequestPending = 'RequestPending', + ResponsePending = 'ResponsePending', + Error = 'Error', + Success = 'Success', + ErrorStale = 'ErrorStale', + SuccessStale = 'SuccessStale' +} + +/** + * Returns true if the given state is RequestPending, false otherwise + */ +export const isRequestPending = (state: RequestEntryState) => + state === RequestEntryState.RequestPending; + +/** + * Returns true if the given state is Error, false otherwise + */ +export const isError = (state: RequestEntryState) => + state === RequestEntryState.Error; + +/** + * Returns true if the given state is Success, false otherwise + */ +export const isSuccess = (state: RequestEntryState) => + state === RequestEntryState.Success; + +/** + * Returns true if the given state is ErrorStale, false otherwise + */ +export const isErrorStale = (state: RequestEntryState) => + state === RequestEntryState.ErrorStale; + +/** + * Returns true if the given state is SuccessStale, false otherwise + */ +export const isSuccessStale = (state: RequestEntryState) => + state === RequestEntryState.SuccessStale; + +/** + * Returns true if the given state is ResponsePending, false otherwise + */ +export const isResponsePending = (state: RequestEntryState) => + state === RequestEntryState.ResponsePending; +/** + * Returns true if the given state is RequestPending or ResponsePending, + * false otherwise + */ +export const isLoading = (state: RequestEntryState) => + isRequestPending(state) || isResponsePending(state); + +/** + * If isLoading is true for the given state, this method returns undefined, we can't know yet. + * If it isn't this method will return true if the the given state is Error or ErrorStale, + * false otherwise + */ +export const hasFailed = (state: RequestEntryState) => { + if (isLoading(state)) { + return undefined; + } else { + return isError(state) || isErrorStale(state); + } +}; + +/** + * If isLoading is true for the given state, this method returns undefined, we can't know yet. + * If it isn't this method will return true if the the given state is Success or SuccessStale, + * false otherwise + */ +export const hasSucceeded = (state: RequestEntryState) => { + if (isLoading(state)) { + return undefined; + } else { + return isSuccess(state) || isSuccessStale(state); + } +}; + +/** + * Returns true if the given state is not loading, false otherwise + */ +export const hasCompleted = (state: RequestEntryState) => + !isLoading(state); + +/** + * Returns true if the given state is SuccessStale or ErrorStale, false otherwise + */ +export const isStale = (state: RequestEntryState) => + isSuccessStale(state) || isErrorStale(state); diff --git a/src/app/core/data/request-error.model.ts b/src/app/core/data/request-error.model.ts new file mode 100644 index 0000000000..bde0e1ea23 --- /dev/null +++ b/src/app/core/data/request-error.model.ts @@ -0,0 +1,4 @@ +export class RequestError extends Error { + statusCode: number; + statusText: string; +} diff --git a/src/app/core/data/request.effects.ts b/src/app/core/data/request.effects.ts index acac82afa5..e5a4fc7df2 100644 --- a/src/app/core/data/request.effects.ts +++ b/src/app/core/data/request.effects.ts @@ -16,10 +16,11 @@ import { RequestSuccessAction, ResetResponseTimestampsAction } from './request.actions'; -import { RequestError, RestRequest } from './request.models'; +import { RestRequest } from './request.models'; import { RequestEntry } from './request.reducer'; import { RequestService } from './request.service'; import { ParsedResponse } from '../cache/response.models'; +import { RequestError } from './request-error.model'; @Injectable() export class RequestEffects { diff --git a/src/app/core/data/request.models.ts b/src/app/core/data/request.models.ts index 667e4a0434..990c37d192 100644 --- a/src/app/core/data/request.models.ts +++ b/src/app/core/data/request.models.ts @@ -275,8 +275,4 @@ export class MyDSpaceRequest extends GetRequest { public responseMsToLive = 10 * 1000; } -export class RequestError extends Error { - statusCode: number; - statusText: string; -} /* tslint:enable:max-classes-per-file */ diff --git a/src/app/core/data/request.reducer.spec.ts b/src/app/core/data/request.reducer.spec.ts index 3e210220fe..2073b4faa1 100644 --- a/src/app/core/data/request.reducer.spec.ts +++ b/src/app/core/data/request.reducer.spec.ts @@ -9,7 +9,8 @@ import { ResetResponseTimestampsAction } from './request.actions'; import { GetRequest } from './request.models'; -import { RequestEntryState, requestReducer, RequestState } from './request.reducer'; +import { requestReducer, RequestState } from './request.reducer'; +import { RequestEntryState } from './request-entry-state.model'; class NullAction extends RequestSuccessAction { type = null; diff --git a/src/app/core/data/request.reducer.ts b/src/app/core/data/request.reducer.ts index 5dcee2dbb1..8b6f50dda8 100644 --- a/src/app/core/data/request.reducer.ts +++ b/src/app/core/data/request.reducer.ts @@ -2,107 +2,18 @@ import { RequestAction, RequestActionTypes, RequestConfigureAction, + RequestErrorAction, RequestExecuteAction, RequestRemoveAction, - ResetResponseTimestampsAction, + RequestStaleAction, RequestSuccessAction, - RequestErrorAction, - RequestStaleAction + ResetResponseTimestampsAction } from './request.actions'; import { RestRequest } from './request.models'; import { HALLink } from '../shared/hal-link.model'; import { UnCacheableObject } from '../shared/uncacheable-object.model'; import { isNull } from '../../shared/empty.util'; - -export enum RequestEntryState { - RequestPending = 'RequestPending', - ResponsePending = 'ResponsePending', - Error = 'Error', - Success = 'Success', - ErrorStale = 'ErrorStale', - SuccessStale = 'SuccessStale' -} - -/** - * Returns true if the given state is RequestPending, false otherwise - */ -export const isRequestPending = (state: RequestEntryState) => - state === RequestEntryState.RequestPending; - -/** - * Returns true if the given state is ResponsePending, false otherwise - */ -export const isResponsePending = (state: RequestEntryState) => - state === RequestEntryState.ResponsePending; - -/** - * Returns true if the given state is Error, false otherwise - */ -export const isError = (state: RequestEntryState) => - state === RequestEntryState.Error; - -/** - * Returns true if the given state is Success, false otherwise - */ -export const isSuccess = (state: RequestEntryState) => - state === RequestEntryState.Success; - -/** - * Returns true if the given state is ErrorStale, false otherwise - */ -export const isErrorStale = (state: RequestEntryState) => - state === RequestEntryState.ErrorStale; - -/** - * Returns true if the given state is SuccessStale, false otherwise - */ -export const isSuccessStale = (state: RequestEntryState) => - state === RequestEntryState.SuccessStale; - -/** - * Returns true if the given state is RequestPending or ResponsePending, - * false otherwise - */ -export const isLoading = (state: RequestEntryState) => - isRequestPending(state) || isResponsePending(state); - -/** - * If isLoading is true for the given state, this method returns undefined, we can't know yet. - * If it isn't this method will return true if the the given state is Error or ErrorStale, - * false otherwise - */ -export const hasFailed = (state: RequestEntryState) => { - if (isLoading(state)) { - return undefined; - } else { - return isError(state) || isErrorStale(state); - } -}; - -/** - * If isLoading is true for the given state, this method returns undefined, we can't know yet. - * If it isn't this method will return true if the the given state is Success or SuccessStale, - * false otherwise - */ -export const hasSucceeded = (state: RequestEntryState) => { - if (isLoading(state)) { - return undefined; - } else { - return isSuccess(state) || isSuccessStale(state); - } -}; - -/** - * Returns true if the given state is not loading, false otherwise - */ -export const hasCompleted = (state: RequestEntryState) => - !isLoading(state); - -/** - * Returns true if the given state is SuccessStale or ErrorStale, false otherwise - */ -export const isStale = (state: RequestEntryState) => - isSuccessStale(state) || isErrorStale(state); +import { hasSucceeded, isStale, RequestEntryState } from './request-entry-state.model'; export class ResponseState { timeCompleted: number; diff --git a/src/app/core/data/request.service.spec.ts b/src/app/core/data/request.service.spec.ts index 7a07f6fe10..50d753ab45 100644 --- a/src/app/core/data/request.service.spec.ts +++ b/src/app/core/data/request.service.spec.ts @@ -19,11 +19,12 @@ import { PutRequest, RestRequest } from './request.models'; -import { RequestEntry, RequestEntryState } from './request.reducer'; +import { RequestEntry} from './request.reducer'; import { RequestService } from './request.service'; import { TestBed, waitForAsync } from '@angular/core/testing'; import { storeModuleConfig } from '../../app.reducer'; import { MockStore, provideMockStore } from '@ngrx/store/testing'; +import { RequestEntryState } from './request-entry-state.model'; describe('RequestService', () => { let scheduler: TestScheduler; diff --git a/src/app/core/data/request.service.ts b/src/app/core/data/request.service.ts index 14499b8214..b381fdd1fd 100644 --- a/src/app/core/data/request.service.ts +++ b/src/app/core/data/request.service.ts @@ -18,10 +18,11 @@ import { RequestStaleAction } from './request.actions'; import { GetRequest, RestRequest } from './request.models'; -import { RequestEntry, RequestState, isStale, isLoading } from './request.reducer'; +import { RequestEntry, RequestState} from './request.reducer'; import { CommitSSBAction } from '../cache/server-sync-buffer.actions'; import { RestRequestMethod } from './rest-request-method'; import { coreSelector } from '../core.selectors'; +import { isLoading, isStale } from './request-entry-state.model'; /** * The base selector function to select the request state in the store diff --git a/src/app/core/data/root.model.ts b/src/app/core/data/root.model.ts index 4840051df5..a2d2518dde 100644 --- a/src/app/core/data/root.model.ts +++ b/src/app/core/data/root.model.ts @@ -1,10 +1,10 @@ import { typedObject } from '../cache/builders/build-decorators'; -import { CacheableObject } from '../cache/object-cache.reducer'; import { ROOT } from './root.resource-type'; import { excludeFromEquals } from '../utilities/equals.decorators'; import { autoserialize, deserialize } from 'cerialize'; import { ResourceType } from '../shared/resource-type'; import { HALLink } from '../shared/hal-link.model'; +import { CacheableObject } from '../cache/cacheable-object.model'; /** * The root rest api resource diff --git a/src/app/core/end-user-agreement/abstract-end-user-agreement.guard.ts b/src/app/core/end-user-agreement/abstract-end-user-agreement.guard.ts index 5d44a8447a..b9f134f946 100644 --- a/src/app/core/end-user-agreement/abstract-end-user-agreement.guard.ts +++ b/src/app/core/end-user-agreement/abstract-end-user-agreement.guard.ts @@ -1,6 +1,6 @@ import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree } from '@angular/router'; import { Observable } from 'rxjs'; -import { returnEndUserAgreementUrlTreeOnFalse } from '../shared/operators'; +import { returnEndUserAgreementUrlTreeOnFalse } from '../shared/authorized.operators'; /** * An abstract guard for redirecting users to the user agreement page if a certain condition is met diff --git a/src/app/core/index/index-name.model.ts b/src/app/core/index/index-name.model.ts new file mode 100644 index 0000000000..fa4c20a006 --- /dev/null +++ b/src/app/core/index/index-name.model.ts @@ -0,0 +1,17 @@ +/** + * An enum containing all index names + */ +export enum IndexName { + // Contains all objects in the object cache indexed by UUID + OBJECT = 'object/uuid-to-self-link', + + // contains all requests in the request cache indexed by UUID + REQUEST = 'get-request/href-to-uuid', + + /** + * Contains the alternative link for an objects + * Maps these link on to their matching self link in the object cache + * Eg. /workspaceitems/12/item --> /items/12345 + */ + ALTERNATIVE_OBJECT_LINK = 'object/alt-link-to-self-link' +} diff --git a/src/app/core/index/index.actions.ts b/src/app/core/index/index.actions.ts index feff7a4486..091f788626 100644 --- a/src/app/core/index/index.actions.ts +++ b/src/app/core/index/index.actions.ts @@ -1,7 +1,7 @@ import { Action } from '@ngrx/store'; import { type } from '../../shared/ngrx/type'; -import { IndexName } from './index.reducer'; +import { IndexName } from './index-name.model'; /** * The list of HrefIndexAction type definitions diff --git a/src/app/core/index/index.effects.spec.ts b/src/app/core/index/index.effects.spec.ts index efca0ae364..c86860d01c 100644 --- a/src/app/core/index/index.effects.spec.ts +++ b/src/app/core/index/index.effects.spec.ts @@ -6,9 +6,9 @@ import { cold, hot } from 'jasmine-marbles'; import { AddToObjectCacheAction } from '../cache/object-cache.actions'; import { Item } from '../shared/item.model'; import { AddToIndexAction } from './index.actions'; -import { IndexName } from './index.reducer'; import { provideMockStore } from '@ngrx/store/testing'; import { NoOpAction } from '../../shared/ngrx/no-op.action'; +import { IndexName } from './index-name.model'; describe('ObjectUpdatesEffects', () => { let indexEffects: UUIDIndexEffects; diff --git a/src/app/core/index/index.effects.ts b/src/app/core/index/index.effects.ts index a1ab0b20a8..58acc08acc 100644 --- a/src/app/core/index/index.effects.ts +++ b/src/app/core/index/index.effects.ts @@ -14,12 +14,12 @@ import { } from '../data/request.actions'; import { AddToIndexAction, RemoveFromIndexByValueAction } from './index.actions'; import { hasValue } from '../../shared/empty.util'; -import { IndexName } from './index.reducer'; import { RestRequestMethod } from '../data/rest-request-method'; import { getUrlWithoutEmbedParams, uuidFromHrefSelector } from './index.selectors'; import { Store, select } from '@ngrx/store'; import { CoreState } from '../core.reducers'; import { NoOpAction } from '../../shared/ngrx/no-op.action'; +import { IndexName } from './index-name.model'; @Injectable() export class UUIDIndexEffects { diff --git a/src/app/core/index/index.reducer.spec.ts b/src/app/core/index/index.reducer.spec.ts index 4daacc03a6..867e4dbad8 100644 --- a/src/app/core/index/index.reducer.spec.ts +++ b/src/app/core/index/index.reducer.spec.ts @@ -1,11 +1,12 @@ import * as deepFreeze from 'deep-freeze'; -import { IndexName, indexReducer, MetaIndexState } from './index.reducer'; +import { indexReducer, MetaIndexState } from './index.reducer'; import { AddToIndexAction, RemoveFromIndexBySubstringAction, RemoveFromIndexByValueAction } from './index.actions'; +import { IndexName } from './index-name.model'; class NullAction extends AddToIndexAction { type = null; diff --git a/src/app/core/index/index.reducer.ts b/src/app/core/index/index.reducer.ts index 80cb322c75..21819d67ba 100644 --- a/src/app/core/index/index.reducer.ts +++ b/src/app/core/index/index.reducer.ts @@ -1,28 +1,5 @@ -import { - AddToIndexAction, - IndexAction, - IndexActionTypes, - RemoveFromIndexBySubstringAction, - RemoveFromIndexByValueAction -} from './index.actions'; - -/** - * An enum containing all index names - */ -export enum IndexName { - // Contains all objects in the object cache indexed by UUID - OBJECT = 'object/uuid-to-self-link', - - // contains all requests in the request cache indexed by UUID - REQUEST = 'get-request/href-to-uuid', - - /** - * Contains the alternative link for an objects - * Maps these link on to their matching self link in the object cache - * Eg. /workspaceitems/12/item --> /items/12345 - */ - ALTERNATIVE_OBJECT_LINK = 'object/alt-link-to-self-link' -} +import { AddToIndexAction, IndexAction, IndexActionTypes, RemoveFromIndexBySubstringAction, RemoveFromIndexByValueAction } from './index.actions'; +import { IndexName } from './index-name.model'; /** * The state of a single index diff --git a/src/app/core/index/index.selectors.ts b/src/app/core/index/index.selectors.ts index 9b57064388..435398d0a4 100644 --- a/src/app/core/index/index.selectors.ts +++ b/src/app/core/index/index.selectors.ts @@ -3,8 +3,9 @@ import { hasValue, isNotEmpty } from '../../shared/empty.util'; import { CoreState } from '../core.reducers'; import { coreSelector } from '../core.selectors'; import { URLCombiner } from '../url-combiner/url-combiner'; -import { IndexName, IndexState, MetaIndexState } from './index.reducer'; +import { IndexState, MetaIndexState } from './index.reducer'; import * as parse from 'url-parse'; +import { IndexName } from './index-name.model'; /** * Return the given url without `embed` params. diff --git a/src/app/core/resource-policy/models/resource-policy.model.ts b/src/app/core/resource-policy/models/resource-policy.model.ts index 94ae2c41a3..b0608dfbca 100644 --- a/src/app/core/resource-policy/models/resource-policy.model.ts +++ b/src/app/core/resource-policy/models/resource-policy.model.ts @@ -2,7 +2,6 @@ import { autoserialize, deserialize, deserializeAs } from 'cerialize'; import { link, typedObject } from '../../cache/builders/build-decorators'; import { IDToUUIDSerializer } from '../../cache/id-to-uuid-serializer'; import { ActionType } from './action-type.model'; -import { CacheableObject } from '../../cache/object-cache.reducer'; import { HALLink } from '../../shared/hal-link.model'; import { RESOURCE_POLICY } from './resource-policy.resource-type'; import { excludeFromEquals } from '../../utilities/equals.decorators'; @@ -14,6 +13,7 @@ import { GROUP } from '../../eperson/models/group.resource-type'; import { Group } from '../../eperson/models/group.model'; import { EPERSON } from '../../eperson/models/eperson.resource-type'; import { EPerson } from '../../eperson/models/eperson.model'; +import { CacheableObject } from '../../cache/cacheable-object.model'; /** * Model class for a Resource Policy diff --git a/src/app/core/shared/authorized.operators.ts b/src/app/core/shared/authorized.operators.ts new file mode 100644 index 0000000000..c36f0f41d2 --- /dev/null +++ b/src/app/core/shared/authorized.operators.ts @@ -0,0 +1,91 @@ +import { Router, UrlTree } from '@angular/router'; +import { AuthService } from '../auth/auth.service'; +import { combineLatest as observableCombineLatest, Observable } from 'rxjs'; +import { filter, map, withLatestFrom } from 'rxjs/operators'; +import { InjectionToken } from '@angular/core'; +import { RemoteData } from '../data/remote-data'; +import { getEndUserAgreementPath } from '../../info/info-routing-paths'; +import { getForbiddenRoute, getPageNotFoundRoute } from '../../app-routing-paths'; + +export const REDIRECT_ON_4XX = new InjectionToken<(router: Router, authService: AuthService) => (source: Observable>) => Observable>>('redirectOn4xx', { + providedIn: 'root', + factory: () => redirectOn4xx +}); +/** + * Operator that checks if a remote data object returned a 4xx error + * When it does contain such an error, it will redirect the user to the related error page, without + * altering the current URL + * + * @param router The router used to navigate to a new page + * @param authService Service to check if the user is authenticated + */ +export const redirectOn4xx = (router: Router, authService: AuthService) => + (source: Observable>): Observable> => + source.pipe( + withLatestFrom(authService.isAuthenticated()), + filter(([rd, isAuthenticated]: [RemoteData, boolean]) => { + if (rd.hasFailed) { + if (rd.statusCode === 404 || rd.statusCode === 422) { + router.navigateByUrl(getPageNotFoundRoute(), { skipLocationChange: true }); + return false; + } else if (rd.statusCode === 403 || rd.statusCode === 401) { + if (isAuthenticated) { + router.navigateByUrl(getForbiddenRoute(), { skipLocationChange: true }); + return false; + } else { + authService.setRedirectUrl(router.url); + router.navigateByUrl('login'); + return false; + } + } + } + return true; + }), + map(([rd,]: [RemoteData, boolean]) => rd) + ); +/** + * Operator that returns a UrlTree to a forbidden page or the login page when the boolean received is false + * @param router The router used to navigate to a forbidden page + * @param authService The AuthService used to determine whether or not the user is logged in + * @param redirectUrl The URL to redirect back to after logging in + */ +export const returnForbiddenUrlTreeOrLoginOnFalse = (router: Router, authService: AuthService, redirectUrl: string) => + (source: Observable): Observable => + source.pipe( + map((authorized) => [authorized]), + returnForbiddenUrlTreeOrLoginOnAllFalse(router, authService, redirectUrl), + ); +/** + * Operator that returns a UrlTree to a forbidden page or the login page when the booleans received are all false + * @param router The router used to navigate to a forbidden page + * @param authService The AuthService used to determine whether or not the user is logged in + * @param redirectUrl The URL to redirect back to after logging in + */ +export const returnForbiddenUrlTreeOrLoginOnAllFalse = (router: Router, authService: AuthService, redirectUrl: string) => + (source: Observable): Observable => + observableCombineLatest(source, authService.isAuthenticated()).pipe( + map(([authorizedList, authenticated]: [boolean[], boolean]) => { + if (authorizedList.some((b: boolean) => b === true)) { + return true; + } else { + if (authenticated) { + return router.parseUrl(getForbiddenRoute()); + } else { + authService.setRedirectUrl(redirectUrl); + return router.parseUrl('login'); + } + } + })); +/** + * Operator that returns a UrlTree to the unauthorized page when the boolean received is false + * @param router Router + * @param redirect Redirect URL to add to the UrlTree. This is used to redirect back to the original route after the + * user accepts the agreement. + */ +export const returnEndUserAgreementUrlTreeOnFalse = (router: Router, redirect: string) => + (source: Observable): Observable => + source.pipe( + map((hasAgreed: boolean) => { + const queryParams = { redirect: encodeURIComponent(redirect) }; + return hasAgreed ? hasAgreed : router.createUrlTree([getEndUserAgreementPath()], { queryParams }); + })); diff --git a/src/app/core/shared/bitstream-format.model.ts b/src/app/core/shared/bitstream-format.model.ts index 24a0646316..9e8dc5e1fe 100644 --- a/src/app/core/shared/bitstream-format.model.ts +++ b/src/app/core/shared/bitstream-format.model.ts @@ -1,12 +1,12 @@ import { autoserialize, deserialize, deserializeAs } from 'cerialize'; import { typedObject } from '../cache/builders/build-decorators'; import { IDToUUIDSerializer } from '../cache/id-to-uuid-serializer'; -import { CacheableObject } from '../cache/object-cache.reducer'; import { excludeFromEquals } from '../utilities/equals.decorators'; import { BitstreamFormatSupportLevel } from './bitstream-format-support-level'; import { BITSTREAM_FORMAT } from './bitstream-format.resource-type'; import { HALLink } from './hal-link.model'; import { ResourceType } from './resource-type'; +import { CacheableObject } from '../cache/cacheable-object.model'; /** * Model class for a Bitstream Format diff --git a/src/app/core/shared/browse-definition.model.ts b/src/app/core/shared/browse-definition.model.ts index 2c08417b6d..8a13beae21 100644 --- a/src/app/core/shared/browse-definition.model.ts +++ b/src/app/core/shared/browse-definition.model.ts @@ -1,11 +1,11 @@ import { autoserialize, autoserializeAs, deserialize } from 'cerialize'; import { typedObject } from '../cache/builders/build-decorators'; -import { CacheableObject } from '../cache/object-cache.reducer'; import { excludeFromEquals } from '../utilities/equals.decorators'; import { BROWSE_DEFINITION } from './browse-definition.resource-type'; import { HALLink } from './hal-link.model'; import { ResourceType } from './resource-type'; import { SortOption } from './sort-option.model'; +import { CacheableObject } from '../cache/cacheable-object.model'; @typedObject export class BrowseDefinition extends CacheableObject { diff --git a/src/app/core/shared/browse-entry.model.ts b/src/app/core/shared/browse-entry.model.ts index 9ae28ac2f0..df748ac61c 100644 --- a/src/app/core/shared/browse-entry.model.ts +++ b/src/app/core/shared/browse-entry.model.ts @@ -1,12 +1,12 @@ import { autoserialize, autoserializeAs, deserialize } from 'cerialize'; import { ListableObject } from '../../shared/object-collection/shared/listable-object.model'; import { typedObject } from '../cache/builders/build-decorators'; -import { TypedObject } from '../cache/object-cache.reducer'; import { excludeFromEquals } from '../utilities/equals.decorators'; import { BROWSE_ENTRY } from './browse-entry.resource-type'; import { GenericConstructor } from './generic-constructor'; import { HALLink } from './hal-link.model'; import { ResourceType } from './resource-type'; +import { TypedObject } from '../cache/typed-object.model'; /** * Class object representing a browse entry diff --git a/src/app/core/shared/configuration-property.model.ts b/src/app/core/shared/configuration-property.model.ts index 465523c29f..874e80f08d 100644 --- a/src/app/core/shared/configuration-property.model.ts +++ b/src/app/core/shared/configuration-property.model.ts @@ -1,10 +1,10 @@ import { autoserialize, autoserializeAs, deserialize } from 'cerialize'; import { typedObject } from '../cache/builders/build-decorators'; -import { CacheableObject } from '../cache/object-cache.reducer'; import { excludeFromEquals } from '../utilities/equals.decorators'; import { HALLink } from './hal-link.model'; import { ResourceType } from './resource-type'; import { CONFIG_PROPERTY } from './config-property.resource-type'; +import { CacheableObject } from '../cache/cacheable-object.model'; /** * Model class for a Configuration Property diff --git a/src/app/core/shared/content-source.model.ts b/src/app/core/shared/content-source.model.ts index 326407822f..10806cf5b4 100644 --- a/src/app/core/shared/content-source.model.ts +++ b/src/app/core/shared/content-source.model.ts @@ -1,11 +1,11 @@ import { autoserializeAs, deserializeAs, deserialize } from 'cerialize'; import { HALLink } from './hal-link.model'; import { MetadataConfig } from './metadata-config.model'; -import { CacheableObject } from '../cache/object-cache.reducer'; import { typedObject } from '../cache/builders/build-decorators'; import { CONTENT_SOURCE } from './content-source.resource-type'; import { excludeFromEquals } from '../utilities/equals.decorators'; import { ResourceType } from './resource-type'; +import { CacheableObject } from '../cache/cacheable-object.model'; /** * The type of content harvesting used diff --git a/src/app/core/shared/dspace-object.model.ts b/src/app/core/shared/dspace-object.model.ts index 5ea2bced3d..6f5d45544d 100644 --- a/src/app/core/shared/dspace-object.model.ts +++ b/src/app/core/shared/dspace-object.model.ts @@ -2,7 +2,6 @@ import { autoserialize, autoserializeAs, deserialize, deserializeAs } from 'ceri import { hasNoValue, hasValue, isUndefined } from '../../shared/empty.util'; import { ListableObject } from '../../shared/object-collection/shared/listable-object.model'; import { typedObject } from '../cache/builders/build-decorators'; -import { CacheableObject } from '../cache/object-cache.reducer'; import { excludeFromEquals } from '../utilities/equals.decorators'; import { DSPACE_OBJECT } from './dspace-object.resource-type'; import { GenericConstructor } from './generic-constructor'; @@ -16,6 +15,7 @@ import { } from './metadata.models'; import { Metadata } from './metadata.utils'; import { ResourceType } from './resource-type'; +import { CacheableObject } from '../cache/cacheable-object.model'; /** * An abstract model class for a DSpaceObject. diff --git a/src/app/core/shared/external-source.model.ts b/src/app/core/shared/external-source.model.ts index e28c8953e9..f6011a08bc 100644 --- a/src/app/core/shared/external-source.model.ts +++ b/src/app/core/shared/external-source.model.ts @@ -1,10 +1,10 @@ import { autoserialize, deserialize } from 'cerialize'; import { typedObject } from '../cache/builders/build-decorators'; -import { CacheableObject } from '../cache/object-cache.reducer'; import { excludeFromEquals } from '../utilities/equals.decorators'; import { EXTERNAL_SOURCE } from './external-source.resource-type'; import { HALLink } from './hal-link.model'; import { ResourceType } from './resource-type'; +import { CacheableObject } from '../cache/cacheable-object.model'; /** * Model class for an external source diff --git a/src/app/core/shared/item-relationships/item-type.model.ts b/src/app/core/shared/item-relationships/item-type.model.ts index d41024cdaa..c51c3b9035 100644 --- a/src/app/core/shared/item-relationships/item-type.model.ts +++ b/src/app/core/shared/item-relationships/item-type.model.ts @@ -1,11 +1,11 @@ import { autoserialize, deserialize, deserializeAs } from 'cerialize'; import { typedObject } from '../../cache/builders/build-decorators'; import { IDToUUIDSerializer } from '../../cache/id-to-uuid-serializer'; -import { CacheableObject } from '../../cache/object-cache.reducer'; import { excludeFromEquals } from '../../utilities/equals.decorators'; import { HALLink } from '../hal-link.model'; import { ResourceType } from '../resource-type'; import { ITEM_TYPE } from './item-type.resource-type'; +import { CacheableObject } from '../../cache/cacheable-object.model'; /** * Describes a type of Item diff --git a/src/app/core/shared/item-relationships/relationship-type.model.ts b/src/app/core/shared/item-relationships/relationship-type.model.ts index fb62f685dd..73e3a7b47c 100644 --- a/src/app/core/shared/item-relationships/relationship-type.model.ts +++ b/src/app/core/shared/item-relationships/relationship-type.model.ts @@ -2,7 +2,6 @@ import { autoserialize, deserialize, deserializeAs } from 'cerialize'; import { Observable } from 'rxjs'; import { link, typedObject } from '../../cache/builders/build-decorators'; import { IDToUUIDSerializer } from '../../cache/id-to-uuid-serializer'; -import { CacheableObject } from '../../cache/object-cache.reducer'; import { RemoteData } from '../../data/remote-data'; import { excludeFromEquals } from '../../utilities/equals.decorators'; import { HALLink } from '../hal-link.model'; @@ -10,6 +9,7 @@ import { ResourceType } from '../resource-type'; import { ItemType } from './item-type.model'; import { ITEM_TYPE } from './item-type.resource-type'; import { RELATIONSHIP_TYPE } from './relationship-type.resource-type'; +import { CacheableObject } from '../../cache/cacheable-object.model'; /** * Describes a type of Relationship between multiple possible Items diff --git a/src/app/core/shared/item-relationships/relationship.model.ts b/src/app/core/shared/item-relationships/relationship.model.ts index b85d6e1b8e..db12e1d862 100644 --- a/src/app/core/shared/item-relationships/relationship.model.ts +++ b/src/app/core/shared/item-relationships/relationship.model.ts @@ -2,7 +2,6 @@ import { autoserialize, deserialize, deserializeAs } from 'cerialize'; import { Observable } from 'rxjs'; import { link, typedObject } from '../../cache/builders/build-decorators'; import { IDToUUIDSerializer } from '../../cache/id-to-uuid-serializer'; -import { CacheableObject } from '../../cache/object-cache.reducer'; import { RemoteData } from '../../data/remote-data'; import { excludeFromEquals } from '../../utilities/equals.decorators'; import { HALLink } from '../hal-link.model'; @@ -12,6 +11,7 @@ import { ResourceType } from '../resource-type'; import { RelationshipType } from './relationship-type.model'; import { RELATIONSHIP_TYPE } from './relationship-type.resource-type'; import { RELATIONSHIP } from './relationship.resource-type'; +import { CacheableObject } from '../../cache/cacheable-object.model'; /** * Describes a Relationship between two Items diff --git a/src/app/core/shared/operators.spec.ts b/src/app/core/shared/operators.spec.ts index 6fd15ceacc..eb102065cd 100644 --- a/src/app/core/shared/operators.spec.ts +++ b/src/app/core/shared/operators.spec.ts @@ -5,20 +5,17 @@ import { GetRequest } from '../data/request.models'; import { RequestEntry } from '../data/request.reducer'; import { RequestService } from '../data/request.service'; import { - sendRequest, getAllSucceededRemoteData, getFirstSucceededRemoteData, - getRemoteDataPayload, - getRequestFromRequestHref, - getRequestFromRequestUUID, - getResponseFromEntry, - redirectOn4xx + getRemoteDataPayload } from './operators'; import { of as observableOf } from 'rxjs'; import { createFailedRemoteDataObject, createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils'; +import { getRequestFromRequestHref, getRequestFromRequestUUID, getResponseFromEntry, sendRequest } from './request.operators'; +import { redirectOn4xx } from './authorized.operators'; // tslint:disable:no-shadowed-variable diff --git a/src/app/core/shared/operators.ts b/src/app/core/shared/operators.ts index 3be04447ab..3be8afb597 100644 --- a/src/app/core/shared/operators.ts +++ b/src/app/core/shared/operators.ts @@ -1,31 +1,13 @@ -import { Router, UrlTree } from '@angular/router'; import { combineLatest as observableCombineLatest, Observable } from 'rxjs'; -import { - debounceTime, - filter, - find, - map, - mergeMap, - switchMap, - take, - takeWhile, - tap, - withLatestFrom -} from 'rxjs/operators'; +import { debounceTime, filter, find, map, switchMap, take, takeWhile } from 'rxjs/operators'; import { hasNoValue, hasValue, hasValueOperator, isNotEmpty } from '../../shared/empty.util'; import { SearchResult } from '../../shared/search/search-result.model'; import { PaginatedList } from '../data/paginated-list.model'; import { RemoteData } from '../data/remote-data'; -import { RestRequest } from '../data/request.models'; -import { RequestEntry, ResponseState } from '../data/request.reducer'; -import { RequestService } from '../data/request.service'; import { MetadataField } from '../metadata/metadata-field.model'; import { MetadataSchema } from '../metadata/metadata-schema.model'; import { BrowseDefinition } from './browse-definition.model'; import { DSpaceObject } from './dspace-object.model'; -import { getForbiddenRoute, getPageNotFoundRoute } from '../../app-routing-paths'; -import { getEndUserAgreementPath } from '../../info/info-routing-paths'; -import { AuthService } from '../auth/auth.service'; import { InjectionToken } from '@angular/core'; export const DEBOUNCE_TIME_OPERATOR = new InjectionToken<(dueTime: number) => (source: Observable) => Observable>('debounceTime', { @@ -33,40 +15,6 @@ export const DEBOUNCE_TIME_OPERATOR = new InjectionToken<(dueTime: number) => factory: () => debounceTime }); -export const REDIRECT_ON_4XX = new InjectionToken<(router: Router, authService: AuthService) => (source: Observable>) => Observable>>('redirectOn4xx', { - providedIn: 'root', - factory: () => redirectOn4xx -}); - -/** - * This file contains custom RxJS operators that can be used in multiple places - */ - -export const getRequestFromRequestHref = (requestService: RequestService) => - (source: Observable): Observable => - source.pipe( - mergeMap((href: string) => requestService.getByHref(href)), - hasValueOperator() - ); - -export const getRequestFromRequestUUID = (requestService: RequestService) => - (source: Observable): Observable => - source.pipe( - mergeMap((uuid: string) => requestService.getByUUID(uuid)), - hasValueOperator() - ); - -export const getResponseFromEntry = () => - (source: Observable): Observable => - source.pipe( - filter((entry: RequestEntry) => hasValue(entry) && hasValue(entry.response)), - map((entry: RequestEntry) => entry.response) - ); - -export const sendRequest = (requestService: RequestService) => - (source: Observable): Observable => - source.pipe(tap((request: RestRequest) => requestService.send(request))); - export const getRemoteDataPayload = () => (source: Observable>): Observable => source.pipe(map((remoteData: RemoteData) => remoteData.payload)); @@ -190,88 +138,6 @@ export const getAllSucceededRemoteListPayload = () => getPaginatedListPayload() ); -/** - * Operator that checks if a remote data object returned a 4xx error - * When it does contain such an error, it will redirect the user to the related error page, without - * altering the current URL - * - * @param router The router used to navigate to a new page - * @param authService Service to check if the user is authenticated - */ -export const redirectOn4xx = (router: Router, authService: AuthService) => - (source: Observable>): Observable> => - source.pipe( - withLatestFrom(authService.isAuthenticated()), - filter(([rd, isAuthenticated]: [RemoteData, boolean]) => { - if (rd.hasFailed) { - if (rd.statusCode === 404 || rd.statusCode === 422) { - router.navigateByUrl(getPageNotFoundRoute(), { skipLocationChange: true }); - return false; - } else if (rd.statusCode === 403 || rd.statusCode === 401) { - if (isAuthenticated) { - router.navigateByUrl(getForbiddenRoute(), { skipLocationChange: true }); - return false; - } else { - authService.setRedirectUrl(router.url); - router.navigateByUrl('login'); - return false; - } - } - } - return true; - }), - map(([rd,]: [RemoteData, boolean]) => rd) - ); - -/** - * Operator that returns a UrlTree to a forbidden page or the login page when the boolean received is false - * @param router The router used to navigate to a forbidden page - * @param authService The AuthService used to determine whether or not the user is logged in - * @param redirectUrl The URL to redirect back to after logging in - */ -export const returnForbiddenUrlTreeOrLoginOnFalse = (router: Router, authService: AuthService, redirectUrl: string) => - (source: Observable): Observable => - source.pipe( - map((authorized) => [authorized]), - returnForbiddenUrlTreeOrLoginOnAllFalse(router, authService, redirectUrl), - ); - -/** - * Operator that returns a UrlTree to a forbidden page or the login page when the booleans received are all false - * @param router The router used to navigate to a forbidden page - * @param authService The AuthService used to determine whether or not the user is logged in - * @param redirectUrl The URL to redirect back to after logging in - */ -export const returnForbiddenUrlTreeOrLoginOnAllFalse = (router: Router, authService: AuthService, redirectUrl: string) => - (source: Observable): Observable => - observableCombineLatest(source, authService.isAuthenticated()).pipe( - map(([authorizedList, authenticated]: [boolean[], boolean]) => { - if (authorizedList.some((b: boolean) => b === true)) { - return true; - } else { - if (authenticated) { - return router.parseUrl(getForbiddenRoute()); - } else { - authService.setRedirectUrl(redirectUrl); - return router.parseUrl('login'); - } - } - })); - -/** - * Operator that returns a UrlTree to the unauthorized page when the boolean received is false - * @param router Router - * @param redirect Redirect URL to add to the UrlTree. This is used to redirect back to the original route after the - * user accepts the agreement. - */ -export const returnEndUserAgreementUrlTreeOnFalse = (router: Router, redirect: string) => - (source: Observable): Observable => - source.pipe( - map((hasAgreed: boolean) => { - const queryParams = { redirect: encodeURIComponent(redirect) }; - return hasAgreed ? hasAgreed : router.createUrlTree([getEndUserAgreementPath()], { queryParams }); - })); - export const getFinishedRemoteData = () => (source: Observable>): Observable> => source.pipe(find((rd: RemoteData) => !rd.isLoading)); diff --git a/src/app/core/shared/request.operators.ts b/src/app/core/shared/request.operators.ts new file mode 100644 index 0000000000..3caba824b9 --- /dev/null +++ b/src/app/core/shared/request.operators.ts @@ -0,0 +1,32 @@ +import { RequestService } from '../data/request.service'; +import { Observable } from 'rxjs'; +import { RestRequest } from '../data/request.models'; +import { filter, map, mergeMap, tap } from 'rxjs/operators'; +import { RequestEntry, ResponseState } from '../data/request.reducer'; +import { hasValue, hasValueOperator } from '../../shared/empty.util'; + +/** + * This file contains custom RxJS operators that can be used in multiple places + */ + +export const getRequestFromRequestHref = (requestService: RequestService) => + (source: Observable): Observable => + source.pipe( + mergeMap((href: string) => requestService.getByHref(href)), + hasValueOperator() + ); +export const getRequestFromRequestUUID = (requestService: RequestService) => + (source: Observable): Observable => + source.pipe( + mergeMap((uuid: string) => requestService.getByUUID(uuid)), + hasValueOperator() + ); +export const getResponseFromEntry = () => + (source: Observable): Observable => + source.pipe( + filter((entry: RequestEntry) => hasValue(entry) && hasValue(entry.response)), + map((entry: RequestEntry) => entry.response) + ); +export const sendRequest = (requestService: RequestService) => + (source: Observable): Observable => + source.pipe(tap((request: RestRequest) => requestService.send(request))); diff --git a/src/app/core/shared/search/search-configuration.service.spec.ts b/src/app/core/shared/search/search-configuration.service.spec.ts index 805ecd0486..30be28f65f 100644 --- a/src/app/core/shared/search/search-configuration.service.spec.ts +++ b/src/app/core/shared/search/search-configuration.service.spec.ts @@ -4,8 +4,16 @@ import { PaginationComponentOptions } from '../../../shared/pagination/paginatio import { SortDirection, SortOptions } from '../../cache/models/sort-options.model'; import { PaginatedSearchOptions } from '../../../shared/search/paginated-search-options.model'; import { SearchFilter } from '../../../shared/search/search-filter.model'; -import { of as observableOf } from 'rxjs'; +import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs'; import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub'; +import { HALEndpointServiceStub } from '../../../shared/testing/hal-endpoint-service.stub'; +import { getMockRemoteDataBuildService } from '../../../shared/mocks/remote-data-build.service.mock'; +import { RequestEntry } from '../../data/request.reducer'; +import { map } from 'rxjs/operators'; +import { RemoteData } from '../../data/remote-data'; +import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; +import { SearchObjects } from '../../../shared/search/search-objects.model'; +import { getMockRequestService } from '../../../shared/mocks/request.service.mock'; describe('SearchConfigurationService', () => { let service: SearchConfigurationService; @@ -38,10 +46,37 @@ describe('SearchConfigurationService', () => { const activatedRoute: any = new ActivatedRouteStub(); + const linkService: any = {}; + const requestService: any = getMockRequestService(); + const halService: any = { + /* tslint:disable:no-empty */ + getEndpoint: () => { + } + /* tslint:enable:no-empty */ + }; + + const rdb: any = { + toRemoteDataObservable: (requestEntryObs: Observable, payloadObs: Observable) => { + return observableCombineLatest([requestEntryObs, payloadObs]).pipe( + map(([req, pay]) => { + return { req, pay }; + }) + ); + }, + aggregate: (input: Observable>[]): Observable> => { + return createSuccessfulRemoteDataObject$([]); + }, + buildFromHref: (href: string): Observable> => { + return createSuccessfulRemoteDataObject$(Object.assign(new SearchObjects(), { + page: [] + })); + } + }; beforeEach(() => { - service = new SearchConfigurationService(routeService, paginationService as any, activatedRoute); + service = new SearchConfigurationService(routeService, paginationService as any, activatedRoute, linkService, halService, requestService, rdb); }); + describe('when the scope is called', () => { beforeEach(() => { service.getCurrentScope(''); @@ -160,4 +195,100 @@ describe('SearchConfigurationService', () => { }); }); + describe('when getSearchConfigurationFor is called with a scope', () => { + const endPoint = 'http://endpoint.com/test/config'; + const scope = 'test'; + const requestUrl = endPoint + '?scope=' + scope; + beforeEach(() => { + spyOn((service as any).halService, 'getEndpoint').and.returnValue(observableOf(endPoint)); + /* tslint:disable:no-empty */ + service.getSearchConfigurationFor(scope).subscribe((t) => { + }); // subscribe to make sure all methods are called + /* tslint:enable:no-empty */ + }); + + it('should call getEndpoint on the halService', () => { + expect((service as any).halService.getEndpoint).toHaveBeenCalled(); + }); + + it('should send out the request on the request service', () => { + expect((service as any).requestService.send).toHaveBeenCalled(); + }); + + it('should call send containing a request with the correct request url', () => { + expect((service as any).requestService.send).toHaveBeenCalledWith(jasmine.objectContaining({ href: requestUrl }), true); + }); + }); + + describe('when getSearchConfigurationFor is called without a scope', () => { + const endPoint = 'http://endpoint.com/test/config'; + beforeEach(() => { + spyOn((service as any).halService, 'getEndpoint').and.returnValue(observableOf(endPoint)); + spyOn((service as any).rdb, 'buildFromHref').and.callThrough(); + /* tslint:disable:no-empty */ + service.getSearchConfigurationFor(null).subscribe((t) => { + }); // subscribe to make sure all methods are called + /* tslint:enable:no-empty */ + }); + + it('should call getEndpoint on the halService', () => { + expect((service as any).halService.getEndpoint).toHaveBeenCalled(); + }); + + it('should send out the request on the request service', () => { + expect((service as any).requestService.send).toHaveBeenCalled(); + }); + + it('should call send containing a request with the correct request url', () => { + expect((service as any).requestService.send).toHaveBeenCalledWith(jasmine.objectContaining({ href: endPoint }), true); + }); + }); + describe('when getConfig is called without a scope', () => { + const endPoint = 'http://endpoint.com/test/config'; + beforeEach(() => { + spyOn((service as any).halService, 'getEndpoint').and.returnValue(observableOf(endPoint)); + spyOn((service as any).rdb, 'buildFromHref').and.callThrough(); + /* tslint:disable:no-empty */ + service.getConfig(null).subscribe((t) => { + }); // subscribe to make sure all methods are called + /* tslint:enable:no-empty */ + }); + + it('should call getEndpoint on the halService', () => { + expect((service as any).halService.getEndpoint).toHaveBeenCalled(); + }); + + it('should send out the request on the request service', () => { + expect((service as any).requestService.send).toHaveBeenCalled(); + }); + + it('should call send containing a request with the correct request url', () => { + expect((service as any).requestService.send).toHaveBeenCalledWith(jasmine.objectContaining({ href: endPoint }), true); + }); + }); + + describe('when getConfig is called with a scope', () => { + const endPoint = 'http://endpoint.com/test/config'; + const scope = 'test'; + const requestUrl = endPoint + '?scope=' + scope; + beforeEach(() => { + spyOn((service as any).halService, 'getEndpoint').and.returnValue(observableOf(endPoint)); + /* tslint:disable:no-empty */ + service.getConfig(scope).subscribe((t) => { + }); // subscribe to make sure all methods are called + /* tslint:enable:no-empty */ + }); + + it('should call getEndpoint on the halService', () => { + expect((service as any).halService.getEndpoint).toHaveBeenCalled(); + }); + + it('should send out the request on the request service', () => { + expect((service as any).requestService.send).toHaveBeenCalled(); + }); + + it('should call send containing a request with the correct request url', () => { + expect((service as any).requestService.send).toHaveBeenCalledWith(jasmine.objectContaining({ href: requestUrl }), true); + }); + }); }); diff --git a/src/app/core/shared/search/search-configuration.service.ts b/src/app/core/shared/search/search-configuration.service.ts index 74af230810..d62e5ea01b 100644 --- a/src/app/core/shared/search/search-configuration.service.ts +++ b/src/app/core/shared/search/search-configuration.service.ts @@ -1,14 +1,7 @@ import { Injectable, OnDestroy } from '@angular/core'; import { ActivatedRoute, Params } from '@angular/router'; -import { - BehaviorSubject, - combineLatest, - combineLatest as observableCombineLatest, - merge as observableMerge, - Observable, - Subscription -} from 'rxjs'; +import { BehaviorSubject, combineLatest, combineLatest as observableCombineLatest, merge as observableMerge, Observable, Subscription } from 'rxjs'; import { distinctUntilChanged, filter, map, startWith, switchMap, take } from 'rxjs/operators'; import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; import { SearchOptions } from '../../../shared/search/search-options.model'; @@ -18,16 +11,23 @@ import { RemoteData } from '../../data/remote-data'; import { DSpaceObjectType } from '../dspace-object-type.model'; import { SortDirection, SortOptions } from '../../cache/models/sort-options.model'; import { RouteService } from '../../services/route.service'; -import { - getAllSucceededRemoteDataPayload, - getFirstSucceededRemoteData -} from '../operators'; +import { getAllSucceededRemoteDataPayload, getFirstSucceededRemoteData } from '../operators'; import { hasNoValue, hasValue, isNotEmpty, isNotEmptyOperator } from '../../../shared/empty.util'; import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; import { SearchConfig } from './search-filters/search-config.model'; -import { SearchService } from './search.service'; import { of } from 'rxjs/internal/observable/of'; import { PaginationService } from '../../pagination/pagination.service'; +import { LinkService } from '../../cache/builders/link.service'; +import { HALEndpointService } from '../hal-endpoint.service'; +import { RequestService } from '../../data/request.service'; +import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service'; +import { GetRequest } from '../../data/request.models'; +import { URLCombiner } from '../../url-combiner/url-combiner'; +import { SearchFilterConfig } from '../../../shared/search/search-filter-config.model'; +import { GenericConstructor } from '../generic-constructor'; +import { ResponseParsingService } from '../../data/parsing.service'; +import { FacetConfigResponseParsingService } from '../../data/facet-config-response-parsing.service'; +import { FacetConfigResponse } from '../../../shared/search/facet-config-response.model'; /** * Service that performs all actions that have to do with the current search configuration @@ -35,6 +35,16 @@ import { PaginationService } from '../../pagination/pagination.service'; @Injectable() export class SearchConfigurationService implements OnDestroy { + /** + * Endpoint link path for retrieving search configurations + */ + private configurationLinkPath = 'discover/search'; + + /** + * Endpoint link path for retrieving facet config incl values + */ + private facetLinkPathPrefix = 'discover/facets/'; + public paginationID = 'spc'; /** * Default pagination settings @@ -88,11 +98,20 @@ export class SearchConfigurationService implements OnDestroy { /** * Initialize the search options * @param {RouteService} routeService + * @param paginationService * @param {ActivatedRoute} route + * @param linkService + * @param halService + * @param requestService + * @param rdb */ constructor(protected routeService: RouteService, protected paginationService: PaginationService, - protected route: ActivatedRoute) { + protected route: ActivatedRoute, + protected linkService: LinkService, + protected halService: HALEndpointService, + protected requestService: RequestService, + protected rdb: RemoteDataBuildService,) { this.initDefaults(); } @@ -212,10 +231,10 @@ export class SearchConfigurationService implements OnDestroy { * @param configuration$ * @param service */ - getConfigurationSearchConfigObservable(configuration$: Observable, service: SearchService): Observable { + getConfigurationSearchConfigObservable(configuration$: Observable): Observable { return configuration$.pipe( distinctUntilChanged(), - switchMap((configuration) => service.getSearchConfigurationFor(null, configuration)), + switchMap((configuration) => this.getSearchConfigurationFor(null, configuration)), getAllSucceededRemoteDataPayload()); } @@ -404,4 +423,93 @@ export class SearchConfigurationService implements OnDestroy { }), ); } + + + /** + * Request the search configuration for a given scope or the whole repository + * @param {string} scope UUID of the object for which config the filter config is requested, when no scope is provided the configuration for the whole repository is loaded + * @param {string} configurationName the name of the configuration + * @returns {Observable>} The found configuration + */ + getSearchConfigurationFor(scope?: string, configurationName?: string): Observable> { + const href$ = this.halService.getEndpoint(this.configurationLinkPath).pipe( + map((url: string) => this.getConfigUrl(url, scope, configurationName)), + ); + + href$.pipe(take(1)).subscribe((url: string) => { + const request = new GetRequest(this.requestService.generateRequestId(), url); + this.requestService.send(request, true); + }); + + return this.rdb.buildFromHref(href$); + } + + private getConfigUrl(url: string, scope?: string, configurationName?: string) { + const args: string[] = []; + + if (isNotEmpty(scope)) { + args.push(`scope=${scope}`); + } + + if (isNotEmpty(configurationName)) { + args.push(`configuration=${configurationName}`); + } + + if (isNotEmpty(args)) { + url = new URLCombiner(url, `?${args.join('&')}`).toString(); + } + + return url; + } + + + + /** + * Request the filter configuration for a given scope or the whole repository + * @param {string} scope UUID of the object for which config the filter config is requested, when no scope is provided the configuration for the whole repository is loaded + * @param {string} configurationName the name of the configuration + * @returns {Observable>} The found filter configuration + */ + getConfig(scope?: string, configurationName?: string): Observable> { + const href$ = this.halService.getEndpoint(this.facetLinkPathPrefix).pipe( + map((url: string) => this.getConfigUrl(url, scope, configurationName)), + ); + + href$.pipe(take(1)).subscribe((url: string) => { + let request = new GetRequest(this.requestService.generateRequestId(), url); + request = Object.assign(request, { + getResponseParser(): GenericConstructor { + return FacetConfigResponseParsingService; + } + }); + this.requestService.send(request, true); + }); + + return this.rdb.buildFromHref(href$).pipe( + map((rd: RemoteData) => { + if (rd.hasSucceeded) { + let filters: SearchFilterConfig[]; + if (isNotEmpty(rd.payload.filters)) { + filters = rd.payload.filters + .map((filter: any) => Object.assign(new SearchFilterConfig(), filter)); + } else { + filters = []; + } + + return new RemoteData( + rd.timeCompleted, + rd.msToLive, + rd.lastUpdated, + rd.state, + rd.errorMessage, + filters, + rd.statusCode, + ); + } else { + return rd as any as RemoteData; + } + }) + ); + } + } diff --git a/src/app/core/shared/search/search-filters/search-config.model.ts b/src/app/core/shared/search/search-filters/search-config.model.ts index 725761fe7b..abcec6dd1e 100644 --- a/src/app/core/shared/search/search-filters/search-config.model.ts +++ b/src/app/core/shared/search/search-filters/search-config.model.ts @@ -2,9 +2,9 @@ import { autoserialize, deserialize } from 'cerialize'; import { SEARCH_CONFIG } from './search-config.resource-type'; import { typedObject } from '../../../cache/builders/build-decorators'; -import { CacheableObject } from '../../../cache/object-cache.reducer'; import { HALLink } from '../../hal-link.model'; import { ResourceType } from '../../resource-type'; +import { CacheableObject } from '../../../cache/cacheable-object.model'; /** * The configuration for a search diff --git a/src/app/core/shared/search/search.service.spec.ts b/src/app/core/shared/search/search.service.spec.ts index 00f10230c3..d41f6d8d72 100644 --- a/src/app/core/shared/search/search.service.spec.ts +++ b/src/app/core/shared/search/search.service.spec.ts @@ -191,104 +191,5 @@ describe('SearchService', () => { expect((searchService as any).rdb.buildFromHref).toHaveBeenCalledWith(endPoint); }); }); - - describe('when getConfig is called without a scope', () => { - const endPoint = 'http://endpoint.com/test/config'; - beforeEach(() => { - spyOn((searchService as any).halService, 'getEndpoint').and.returnValue(observableOf(endPoint)); - spyOn((searchService as any).rdb, 'buildFromHref').and.callThrough(); - /* tslint:disable:no-empty */ - searchService.getConfig(null).subscribe((t) => { - }); // subscribe to make sure all methods are called - /* tslint:enable:no-empty */ - }); - - it('should call getEndpoint on the halService', () => { - expect((searchService as any).halService.getEndpoint).toHaveBeenCalled(); - }); - - it('should send out the request on the request service', () => { - expect((searchService as any).requestService.send).toHaveBeenCalled(); - }); - - it('should call send containing a request with the correct request url', () => { - expect((searchService as any).requestService.send).toHaveBeenCalledWith(jasmine.objectContaining({ href: endPoint }), true); - }); - }); - - describe('when getConfig is called with a scope', () => { - const endPoint = 'http://endpoint.com/test/config'; - const scope = 'test'; - const requestUrl = endPoint + '?scope=' + scope; - beforeEach(() => { - spyOn((searchService as any).halService, 'getEndpoint').and.returnValue(observableOf(endPoint)); - /* tslint:disable:no-empty */ - searchService.getConfig(scope).subscribe((t) => { - }); // subscribe to make sure all methods are called - /* tslint:enable:no-empty */ - }); - - it('should call getEndpoint on the halService', () => { - expect((searchService as any).halService.getEndpoint).toHaveBeenCalled(); - }); - - it('should send out the request on the request service', () => { - expect((searchService as any).requestService.send).toHaveBeenCalled(); - }); - - it('should call send containing a request with the correct request url', () => { - expect((searchService as any).requestService.send).toHaveBeenCalledWith(jasmine.objectContaining({ href: requestUrl }), true); - }); - }); - - describe('when getSearchConfigurationFor is called without a scope', () => { - const endPoint = 'http://endpoint.com/test/config'; - beforeEach(() => { - spyOn((searchService as any).halService, 'getEndpoint').and.returnValue(observableOf(endPoint)); - spyOn((searchService as any).rdb, 'buildFromHref').and.callThrough(); - /* tslint:disable:no-empty */ - searchService.getSearchConfigurationFor(null).subscribe((t) => { - }); // subscribe to make sure all methods are called - /* tslint:enable:no-empty */ - }); - - it('should call getEndpoint on the halService', () => { - expect((searchService as any).halService.getEndpoint).toHaveBeenCalled(); - }); - - it('should send out the request on the request service', () => { - expect((searchService as any).requestService.send).toHaveBeenCalled(); - }); - - it('should call send containing a request with the correct request url', () => { - expect((searchService as any).requestService.send).toHaveBeenCalledWith(jasmine.objectContaining({ href: endPoint }), true); - }); - }); - - describe('when getSearchConfigurationFor is called with a scope', () => { - const endPoint = 'http://endpoint.com/test/config'; - const scope = 'test'; - const requestUrl = endPoint + '?scope=' + scope; - beforeEach(() => { - spyOn((searchService as any).halService, 'getEndpoint').and.returnValue(observableOf(endPoint)); - /* tslint:disable:no-empty */ - searchService.getSearchConfigurationFor(scope).subscribe((t) => { - }); // subscribe to make sure all methods are called - /* tslint:enable:no-empty */ - }); - - it('should call getEndpoint on the halService', () => { - expect((searchService as any).halService.getEndpoint).toHaveBeenCalled(); - }); - - it('should send out the request on the request service', () => { - expect((searchService as any).requestService.send).toHaveBeenCalled(); - }); - - it('should call send containing a request with the correct request url', () => { - expect((searchService as any).requestService.send).toHaveBeenCalledWith(jasmine.objectContaining({ href: requestUrl }), true); - }); - }); - }); }); diff --git a/src/app/core/shared/search/search.service.ts b/src/app/core/shared/search/search.service.ts index 75723366bc..13340874e7 100644 --- a/src/app/core/shared/search/search.service.ts +++ b/src/app/core/shared/search/search.service.ts @@ -85,21 +85,11 @@ class DataServiceImpl extends DataService { @Injectable() export class SearchService implements OnDestroy { - /** - * Endpoint link path for retrieving search configurations - */ - private configurationLinkPath = 'discover/search'; - /** * Endpoint link path for retrieving general search results */ private searchLinkPath = 'discover/search/objects'; - /** - * Endpoint link path for retrieving facet config incl values - */ - private facetLinkPathPrefix = 'discover/facets/'; - /** * The ResponseParsingService constructor name */ @@ -298,71 +288,6 @@ export class SearchService implements OnDestroy { ); } - private getConfigUrl(url: string, scope?: string, configurationName?: string) { - const args: string[] = []; - - if (isNotEmpty(scope)) { - args.push(`scope=${scope}`); - } - - if (isNotEmpty(configurationName)) { - args.push(`configuration=${configurationName}`); - } - - if (isNotEmpty(args)) { - url = new URLCombiner(url, `?${args.join('&')}`).toString(); - } - - return url; - } - - /** - * Request the filter configuration for a given scope or the whole repository - * @param {string} scope UUID of the object for which config the filter config is requested, when no scope is provided the configuration for the whole repository is loaded - * @param {string} configurationName the name of the configuration - * @returns {Observable>} The found filter configuration - */ - getConfig(scope?: string, configurationName?: string): Observable> { - const href$ = this.halService.getEndpoint(this.facetLinkPathPrefix).pipe( - map((url: string) => this.getConfigUrl(url, scope, configurationName)), - ); - - href$.pipe(take(1)).subscribe((url: string) => { - let request = new this.request(this.requestService.generateRequestId(), url); - request = Object.assign(request, { - getResponseParser(): GenericConstructor { - return FacetConfigResponseParsingService; - } - }); - this.requestService.send(request, true); - }); - - return this.rdb.buildFromHref(href$).pipe( - map((rd: RemoteData) => { - if (rd.hasSucceeded) { - let filters: SearchFilterConfig[]; - if (isNotEmpty(rd.payload.filters)) { - filters = rd.payload.filters - .map((filter: any) => Object.assign(new SearchFilterConfig(), filter)); - } else { - filters = []; - } - - return new RemoteData( - rd.timeCompleted, - rd.msToLive, - rd.lastUpdated, - rd.state, - rd.errorMessage, - filters, - rd.statusCode, - ); - } else { - return rd as any as RemoteData; - } - }) - ); - } /** * Method to request a single page of filter values for a given value @@ -469,25 +394,6 @@ export class SearchService implements OnDestroy { }); } - /** - * Request the search configuration for a given scope or the whole repository - * @param {string} scope UUID of the object for which config the filter config is requested, when no scope is provided the configuration for the whole repository is loaded - * @param {string} configurationName the name of the configuration - * @returns {Observable>} The found configuration - */ - getSearchConfigurationFor(scope?: string, configurationName?: string): Observable> { - const href$ = this.halService.getEndpoint(this.configurationLinkPath).pipe( - map((url: string) => this.getConfigUrl(url, scope, configurationName)), - ); - - href$.pipe(take(1)).subscribe((url: string) => { - const request = new this.request(this.requestService.generateRequestId(), url); - this.requestService.send(request, true); - }); - - return this.rdb.buildFromHref(href$); - } - /** * @returns {string} The base path to the search page */ diff --git a/src/app/core/submission/models/submission-object.model.ts b/src/app/core/submission/models/submission-object.model.ts index 4c9ea6dfb0..3d7c0a3678 100644 --- a/src/app/core/submission/models/submission-object.model.ts +++ b/src/app/core/submission/models/submission-object.model.ts @@ -2,7 +2,6 @@ import { autoserialize, deserialize, inheritSerialization } from 'cerialize'; import { Observable } from 'rxjs'; import { link } from '../../cache/builders/build-decorators'; -import { CacheableObject } from '../../cache/object-cache.reducer'; import { SubmissionDefinitionsModel } from '../../config/models/config-submission-definitions.model'; import { RemoteData } from '../../data/remote-data'; import { EPerson } from '../../eperson/models/eperson.model'; @@ -14,6 +13,7 @@ import { HALLink } from '../../shared/hal-link.model'; import { ITEM } from '../../shared/item.resource-type'; import { excludeFromEquals } from '../../utilities/equals.decorators'; import { WorkspaceitemSectionsObject } from './workspaceitem-sections.model'; +import { CacheableObject } from '../../cache/cacheable-object.model'; export interface SubmissionObjectError { message: string; diff --git a/src/app/core/submission/submission-object-data.service.ts b/src/app/core/submission/submission-object-data.service.ts index 174229fdc7..97256ea8da 100644 --- a/src/app/core/submission/submission-object-data.service.ts +++ b/src/app/core/submission/submission-object-data.service.ts @@ -11,7 +11,7 @@ import { DataService } from '../data/data.service'; import { map } from 'rxjs/operators'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { environment } from '../../../environments/environment'; -import { RequestEntryState } from '../data/request.reducer'; +import { RequestEntryState } from '../data/request-entry-state.model'; /** * A service to retrieve submission objects (WorkspaceItem/WorkflowItem) diff --git a/src/app/core/submission/vocabularies/models/vocabulary.model.ts b/src/app/core/submission/vocabularies/models/vocabulary.model.ts index 83b578eb89..9cfc779115 100644 --- a/src/app/core/submission/vocabularies/models/vocabulary.model.ts +++ b/src/app/core/submission/vocabularies/models/vocabulary.model.ts @@ -2,13 +2,13 @@ import { autoserialize, deserialize } from 'cerialize'; import { HALLink } from '../../../shared/hal-link.model'; import { VOCABULARY, VOCABULARY_ENTRY } from './vocabularies.resource-type'; -import { CacheableObject } from '../../../cache/object-cache.reducer'; import { typedObject, link } from '../../../cache/builders/build-decorators'; import { excludeFromEquals } from '../../../utilities/equals.decorators'; import { Observable } from 'rxjs'; import { RemoteData } from '../../../data/remote-data'; import { PaginatedList } from '../../../data/paginated-list.model'; import { VocabularyEntry } from './vocabulary-entry.model'; +import { CacheableObject } from '../../../cache/cacheable-object.model'; /** * Model class for a Vocabulary diff --git a/src/app/core/tasks/models/task-object.model.ts b/src/app/core/tasks/models/task-object.model.ts index 1f23a84ca7..dc4aa8b5ae 100644 --- a/src/app/core/tasks/models/task-object.model.ts +++ b/src/app/core/tasks/models/task-object.model.ts @@ -2,7 +2,6 @@ import { autoserialize, deserialize, inheritSerialization } from 'cerialize'; import { Observable } from 'rxjs'; import { link, typedObject } from '../../cache/builders/build-decorators'; -import { CacheableObject } from '../../cache/object-cache.reducer'; import { RemoteData } from '../../data/remote-data'; import { EPerson } from '../../eperson/models/eperson.model'; import { EPERSON } from '../../eperson/models/eperson.resource-type'; @@ -14,6 +13,7 @@ import { TASK_OBJECT } from './task-object.resource-type'; import { WORKFLOWITEM } from '../../eperson/models/workflowitem.resource-type'; import { WORKFLOW_ACTION } from './workflow-action-object.resource-type'; import { WorkflowAction } from './workflow-action-object.model'; +import { CacheableObject } from '../../cache/cacheable-object.model'; /** * An abstract model class for a TaskObject. diff --git a/src/app/core/tasks/tasks.service.ts b/src/app/core/tasks/tasks.service.ts index 7aeb522170..d96e3320f2 100644 --- a/src/app/core/tasks/tasks.service.ts +++ b/src/app/core/tasks/tasks.service.ts @@ -15,9 +15,9 @@ import { hasValue, isNotEmpty } from '../../shared/empty.util'; import { HttpOptions } from '../dspace-rest/dspace-rest.service'; import { ProcessTaskResponse } from './models/process-task-response'; import { getAllCompletedRemoteData, getFirstCompletedRemoteData } from '../shared/operators'; -import { CacheableObject } from '../cache/object-cache.reducer'; import { RemoteData } from '../data/remote-data'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; +import { CacheableObject } from '../cache/cacheable-object.model'; /** * An abstract class that provides methods to handle task requests. diff --git a/src/app/core/utilities/equals.decorators.ts b/src/app/core/utilities/equals.decorators.ts index 82422e29b6..2e07084ea7 100644 --- a/src/app/core/utilities/equals.decorators.ts +++ b/src/app/core/utilities/equals.decorators.ts @@ -1,10 +1,59 @@ -import { isEmpty } from '../../shared/empty.util'; +import { hasNoValue, hasValue, isEmpty } from '../../shared/empty.util'; import { GenericConstructor } from '../shared/generic-constructor'; -import { EquatableObject } from './equatable'; const excludedFromEquals = new Map(); const fieldsForEqualsMap = new Map(); +/** + * Method to compare fields of two objects against each other + * @param object1 The first object for the comparison + * @param object2 The second object for the comparison + * @param fieldList The list of property/field names to compare + */ +function equalsByFields(object1, object2, fieldList): boolean { + const unequalProperty = fieldList.find((key) => { + if (object1[key] === object2[key]) { + return false; + } + if (hasNoValue(object1[key]) && hasNoValue(object2[key])) { + return false; + } + if (hasNoValue(object1[key]) || hasNoValue(object2[key])) { + return true; + } + const mapping = getFieldsForEquals(object1.constructor, key); + if (hasValue(mapping)) { + return !equalsByFields(object1[key], object2[key], mapping); + } + if (object1[key] instanceof EquatableObject) { + return !object1[key].equals(object2[key]); + } + if (typeof object1[key] === 'object') { + return !equalsByFields(object1[key], object2[key], Object.keys(object1)); + } + return object1[key] !== object2[key]; + }); + return hasNoValue(unequalProperty); +} + +/** + * Abstract class to represent objects that can be compared to each other + * It provides a default way of comparing + */ +export abstract class EquatableObject { + equals(other: T): boolean { + if (hasNoValue(other)) { + return false; + } + if (this as any === other) { + return true; + } + const excludedKeys = getExcludedFromEqualsFor(this.constructor); + const keys = Object.keys(this).filter((key) => !excludedKeys.includes(key)); + return equalsByFields(this, other, keys); + } +} + /** * Decorator function that adds the equatable settings from the given (parent) object * @param parentCo The constructor of the parent object diff --git a/src/app/core/utilities/equatable.spec.ts b/src/app/core/utilities/equatable.spec.ts index 79136cd221..02d5e5a9f9 100644 --- a/src/app/core/utilities/equatable.spec.ts +++ b/src/app/core/utilities/equatable.spec.ts @@ -1,5 +1,4 @@ -import { excludeFromEquals, fieldsForEquals } from './equals.decorators'; -import { EquatableObject } from './equatable'; +import { EquatableObject, excludeFromEquals, fieldsForEquals } from './equals.decorators'; import { cloneDeep } from 'lodash'; class Dog extends EquatableObject { diff --git a/src/app/core/utilities/equatable.ts b/src/app/core/utilities/equatable.ts deleted file mode 100644 index 783a43f339..0000000000 --- a/src/app/core/utilities/equatable.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { getExcludedFromEqualsFor, getFieldsForEquals } from './equals.decorators'; -import { hasNoValue, hasValue } from '../../shared/empty.util'; - -/** - * Method to compare fields of two objects against each other - * @param object1 The first object for the comparison - * @param object2 The second object for the comparison - * @param fieldList The list of property/field names to compare - */ -function equalsByFields(object1, object2, fieldList): boolean { - const unequalProperty = fieldList.find((key) => { - if (object1[key] === object2[key]) { - return false; - } - if (hasNoValue(object1[key]) && hasNoValue(object2[key])) { - return false; - } - if (hasNoValue(object1[key]) || hasNoValue(object2[key])) { - return true; - } - const mapping = getFieldsForEquals(object1.constructor, key); - if (hasValue(mapping)) { - return !equalsByFields(object1[key], object2[key], mapping); - } - if (object1[key] instanceof EquatableObject) { - return !object1[key].equals(object2[key]); - } - if (typeof object1[key] === 'object') { - return !equalsByFields(object1[key], object2[key], Object.keys(object1)); - } - return object1[key] !== object2[key]; - }); - return hasNoValue(unequalProperty); -} - -/** - * Abstract class to represent objects that can be compared to each other - * It provides a default way of comparing - */ -export abstract class EquatableObject { - equals(other: T): boolean { - if (hasNoValue(other)) { - return false; - } - if (this as any === other) { - return true; - } - const excludedKeys = getExcludedFromEqualsFor(this.constructor); - const keys = Object.keys(this).filter((key) => !excludedKeys.includes(key)); - return equalsByFields(this, other, keys); - } -} diff --git a/src/app/header-nav-wrapper/header-navbar-wrapper.component.ts b/src/app/header-nav-wrapper/header-navbar-wrapper.component.ts index 128ce41b10..a610b2cb02 100644 --- a/src/app/header-nav-wrapper/header-navbar-wrapper.component.ts +++ b/src/app/header-nav-wrapper/header-navbar-wrapper.component.ts @@ -4,7 +4,7 @@ import { AppState } from '../app.reducer'; import { hasValue } from '../shared/empty.util'; import { Observable, Subscription } from 'rxjs'; import { MenuService } from '../shared/menu/menu.service'; -import { MenuID } from '../shared/menu/initial-menus-state'; +import { MenuID } from '../shared/menu/menu-id.model'; /** * This component represents a wrapper for the horizontal navbar and the header diff --git a/src/app/header/header.component.ts b/src/app/header/header.component.ts index 78298b1e06..366b4f5e7a 100644 --- a/src/app/header/header.component.ts +++ b/src/app/header/header.component.ts @@ -1,7 +1,7 @@ import { Component } from '@angular/core'; import { Observable } from 'rxjs'; import { MenuService } from '../shared/menu/menu.service'; -import { MenuID } from '../shared/menu/initial-menus-state'; +import { MenuID } from '../shared/menu/menu-id.model'; /** * Represents the header with the logo and simple navigation diff --git a/src/app/home-page/home-page-routing.module.ts b/src/app/home-page/home-page-routing.module.ts index 2a41c079da..196a290552 100644 --- a/src/app/home-page/home-page-routing.module.ts +++ b/src/app/home-page/home-page-routing.module.ts @@ -2,9 +2,9 @@ import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; import { HomePageResolver } from './home-page.resolver'; -import { MenuItemType } from '../shared/menu/initial-menus-state'; import { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model'; import { ThemedHomePageComponent } from './themed-home-page.component'; +import { MenuItemType } from '../shared/menu/menu-item-type.model'; @NgModule({ imports: [ diff --git a/src/app/item-page/edit-item-page/abstract-item-update/abstract-item-update.component.ts b/src/app/item-page/edit-item-page/abstract-item-update/abstract-item-update.component.ts index 01d6cc7439..80002f614b 100644 --- a/src/app/item-page/edit-item-page/abstract-item-update/abstract-item-update.component.ts +++ b/src/app/item-page/edit-item-page/abstract-item-update/abstract-item-update.component.ts @@ -1,8 +1,4 @@ import { Component, OnInit, OnDestroy, Input } from '@angular/core'; -import { - FieldUpdate, - FieldUpdates -} from '../../../core/data/object-updates/object-updates.reducer'; import { combineLatest as observableCombineLatest, Observable, Subscription } from 'rxjs'; import { Item } from '../../../core/shared/item.model'; import { ItemDataService } from '../../../core/data/item-data.service'; @@ -18,6 +14,8 @@ import { getItemPageRoute } from '../../item-page-routing-paths'; import { getAllSucceededRemoteData } from '../../../core/shared/operators'; import { hasValue } from '../../../shared/empty.util'; import { ITEM_PAGE_LINKS_TO_FOLLOW } from '../../item.resolver'; +import { FieldUpdate } from '../../../core/data/object-updates/field-update.model'; +import { FieldUpdates } from '../../../core/data/object-updates/field-updates.model'; @Component({ selector: 'ds-abstract-item-update', diff --git a/src/app/item-page/edit-item-page/item-bitstreams/item-bitstreams.component.spec.ts b/src/app/item-page/edit-item-page/item-bitstreams/item-bitstreams.component.spec.ts index a2299be5ba..dbbd3bc72e 100644 --- a/src/app/item-page/edit-item-page/item-bitstreams/item-bitstreams.component.spec.ts +++ b/src/app/item-page/edit-item-page/item-bitstreams/item-bitstreams.component.spec.ts @@ -9,7 +9,6 @@ import { ObjectUpdatesService } from '../../../core/data/object-updates/object-u import { ActivatedRoute, Router } from '@angular/router'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; import { ChangeDetectorRef, NO_ERRORS_SCHEMA } from '@angular/core'; -import { FieldChangeType } from '../../../core/data/object-updates/object-updates.actions'; import { INotification, Notification } from '../../../shared/notifications/models/notification.model'; import { NotificationType } from '../../../shared/notifications/models/notification-type'; import { BitstreamDataService } from '../../../core/data/bitstream-data.service'; @@ -25,6 +24,7 @@ import { RouterStub } from '../../../shared/testing/router.stub'; import { getMockRequestService } from '../../../shared/mocks/request.service.mock'; import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; import { createPaginatedList } from '../../../shared/testing/utils.test'; +import { FieldChangeType } from '../../../core/data/object-updates/field-change-type.model'; let comp: ItemBitstreamsComponent; let fixture: ComponentFixture; diff --git a/src/app/item-page/edit-item-page/item-bitstreams/item-bitstreams.component.ts b/src/app/item-page/edit-item-page/item-bitstreams/item-bitstreams.component.ts index d66c5d060d..88c594c74b 100644 --- a/src/app/item-page/edit-item-page/item-bitstreams/item-bitstreams.component.ts +++ b/src/app/item-page/edit-item-page/item-bitstreams/item-bitstreams.component.ts @@ -15,18 +15,16 @@ import { getFirstSucceededRemoteData, getRemoteDataPayload } from '../../../core import { RemoteData } from '../../../core/data/remote-data'; import { PaginatedList } from '../../../core/data/paginated-list.model'; import { Bundle } from '../../../core/shared/bundle.model'; -import { - FieldUpdate, - FieldUpdates -} from '../../../core/data/object-updates/object-updates.reducer'; import { Bitstream } from '../../../core/shared/bitstream.model'; -import { FieldChangeType } from '../../../core/data/object-updates/object-updates.actions'; import { BundleDataService } from '../../../core/data/bundle-data.service'; import { PaginatedSearchOptions } from '../../../shared/search/paginated-search-options.model'; import { ResponsiveColumnSizes } from '../../../shared/responsive-table-sizes/responsive-column-sizes'; import { ResponsiveTableSizes } from '../../../shared/responsive-table-sizes/responsive-table-sizes'; import { NoContent } from '../../../core/shared/NoContent.model'; import { Operation } from 'fast-json-patch'; +import { FieldUpdate } from '../../../core/data/object-updates/field-update.model'; +import { FieldUpdates } from '../../../core/data/object-updates/field-updates.model'; +import { FieldChangeType } from '../../../core/data/object-updates/field-change-type.model'; @Component({ selector: 'ds-item-bitstreams', diff --git a/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream/item-edit-bitstream.component.ts b/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream/item-edit-bitstream.component.ts index a4d30f6853..8f31bd4c2b 100644 --- a/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream/item-edit-bitstream.component.ts +++ b/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream/item-edit-bitstream.component.ts @@ -1,14 +1,14 @@ import { Component, Input, OnChanges, OnInit, SimpleChanges, ViewChild, ViewContainerRef } from '@angular/core'; -import { FieldUpdate } from '../../../../core/data/object-updates/object-updates.reducer'; import { Bitstream } from '../../../../core/shared/bitstream.model'; import { cloneDeep } from 'lodash'; import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service'; -import { FieldChangeType } from '../../../../core/data/object-updates/object-updates.actions'; import { Observable } from 'rxjs'; import { BitstreamFormat } from '../../../../core/shared/bitstream-format.model'; import { getRemoteDataPayload, getFirstSucceededRemoteData } from '../../../../core/shared/operators'; import { ResponsiveTableSizes } from '../../../../shared/responsive-table-sizes/responsive-table-sizes'; import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; +import { FieldUpdate } from '../../../../core/data/object-updates/field-update.model'; +import { FieldChangeType } from '../../../../core/data/object-updates/field-change-type.model'; @Component({ selector: 'ds-item-edit-bitstream', diff --git a/src/app/item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.spec.ts b/src/app/item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.spec.ts index 0a4df1bda5..29bfaf4f56 100644 --- a/src/app/item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.spec.ts +++ b/src/app/item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.spec.ts @@ -7,7 +7,6 @@ import { getTestScheduler } from 'jasmine-marbles'; import { of as observableOf } from 'rxjs'; import { TestScheduler } from 'rxjs/testing'; import { MetadataFieldDataService } from '../../../../core/data/metadata-field-data.service'; -import { FieldChangeType } from '../../../../core/data/object-updates/object-updates.actions'; import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service'; import { buildPaginatedList } from '../../../../core/data/paginated-list.model'; import { MetadataField } from '../../../../core/metadata/metadata-field.model'; @@ -21,6 +20,7 @@ import { EditInPlaceFieldComponent } from './edit-in-place-field.component'; import { MockComponent, MockDirective } from 'ng-mocks'; import { DebounceDirective } from '../../../../shared/utils/debounce.directive'; import { ValidationSuggestionsComponent } from '../../../../shared/input-suggestions/validation-suggestions/validation-suggestions.component'; +import { FieldChangeType } from '../../../../core/data/object-updates/field-change-type.model'; let comp: EditInPlaceFieldComponent; let fixture: ComponentFixture; diff --git a/src/app/item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.ts b/src/app/item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.ts index 2782747916..849a54cc48 100644 --- a/src/app/item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.ts +++ b/src/app/item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.ts @@ -8,13 +8,13 @@ import { RegistryService } from '../../../../core/registry/registry.service'; import { cloneDeep } from 'lodash'; import { BehaviorSubject, Observable, of as observableOf } from 'rxjs'; import { map } from 'rxjs/operators'; -import { FieldChangeType } from '../../../../core/data/object-updates/object-updates.actions'; -import { FieldUpdate } from '../../../../core/data/object-updates/object-updates.reducer'; import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service'; import { NgModel } from '@angular/forms'; import { MetadatumViewModel } from '../../../../core/shared/metadata.models'; import { InputSuggestion } from '../../../../shared/input-suggestions/input-suggestions.model'; import { followLink } from '../../../../shared/utils/follow-link-config.model'; +import { FieldUpdate } from '../../../../core/data/object-updates/field-update.model'; +import { FieldChangeType } from '../../../../core/data/object-updates/field-change-type.model'; @Component({ // tslint:disable-next-line:component-selector diff --git a/src/app/item-page/edit-item-page/item-metadata/item-metadata.component.spec.ts b/src/app/item-page/edit-item-page/item-metadata/item-metadata.component.spec.ts index 0f01efcc55..44ed6c783f 100644 --- a/src/app/item-page/edit-item-page/item-metadata/item-metadata.component.spec.ts +++ b/src/app/item-page/edit-item-page/item-metadata/item-metadata.component.spec.ts @@ -15,7 +15,6 @@ import { INotification, Notification } from '../../../shared/notifications/model import { NotificationType } from '../../../shared/notifications/models/notification-type'; import { RouterStub } from '../../../shared/testing/router.stub'; import { Item } from '../../../core/shared/item.model'; -import { FieldChangeType } from '../../../core/data/object-updates/object-updates.actions'; import { MetadatumViewModel } from '../../../core/shared/metadata.models'; import { RegistryService } from '../../../core/registry/registry.service'; import { MetadataSchema } from '../../../core/metadata/metadata-schema.model'; @@ -24,6 +23,7 @@ import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } f import { ObjectCacheService } from '../../../core/cache/object-cache.service'; import { DSOSuccessResponse } from '../../../core/cache/response.models'; import { createPaginatedList } from '../../../shared/testing/utils.test'; +import { FieldChangeType } from '../../../core/data/object-updates/field-change-type.model'; let comp: any; let fixture: ComponentFixture; diff --git a/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts b/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts index 6742234058..91bff2a8b5 100644 --- a/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts +++ b/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts @@ -4,7 +4,6 @@ import { By } from '@angular/platform-browser'; import { TranslateModule } from '@ngx-translate/core'; import { of as observableOf } from 'rxjs'; import { LinkService } from '../../../../core/cache/builders/link.service'; -import { FieldChangeType } from '../../../../core/data/object-updates/object-updates.actions'; import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service'; import { RelationshipService } from '../../../../core/data/relationship.service'; import { ItemType } from '../../../../core/shared/item-relationships/item-type.model'; @@ -22,6 +21,7 @@ import { HostWindowService } from '../../../../shared/host-window.service'; import { HostWindowServiceStub } from '../../../../shared/testing/host-window-service.stub'; import { PaginationComponent } from '../../../../shared/pagination/pagination.component'; import { PaginationComponentOptions } from '../../../../shared/pagination/pagination-component-options.model'; +import { FieldChangeType } from '../../../../core/data/object-updates/field-change-type.model'; let comp: EditRelationshipListComponent; let fixture: ComponentFixture; diff --git a/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts b/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts index 9f417ab799..620f749ab9 100644 --- a/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts +++ b/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts @@ -1,7 +1,6 @@ import { Component, Input, OnInit, OnDestroy } from '@angular/core'; import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { LinkService } from '../../../../core/cache/builders/link.service'; -import { FieldChangeType } from '../../../../core/data/object-updates/object-updates.actions'; import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service'; import { combineLatest as observableCombineLatest, @@ -10,8 +9,6 @@ import { from as observableFrom } from 'rxjs'; import { - FieldUpdate, - FieldUpdates, RelationshipIdentifiable } from '../../../../core/data/object-updates/object-updates.reducer'; import { RelationshipService } from '../../../../core/data/relationship.service'; @@ -49,6 +46,9 @@ import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject'; import { Subscription } from 'rxjs/internal/Subscription'; import { PaginationComponentOptions } from '../../../../shared/pagination/pagination-component-options.model'; import { PaginationService } from '../../../../core/pagination/pagination.service'; +import { FieldUpdate } from '../../../../core/data/object-updates/field-update.model'; +import { FieldUpdates } from '../../../../core/data/object-updates/field-updates.model'; +import { FieldChangeType } from '../../../../core/data/object-updates/field-change-type.model'; @Component({ selector: 'ds-edit-relationship-list', diff --git a/src/app/item-page/edit-item-page/item-relationships/edit-relationship/edit-relationship.component.spec.ts b/src/app/item-page/edit-item-page/item-relationships/edit-relationship/edit-relationship.component.spec.ts index 3ab17ad623..a3062abf7e 100644 --- a/src/app/item-page/edit-item-page/item-relationships/edit-relationship/edit-relationship.component.spec.ts +++ b/src/app/item-page/edit-item-page/item-relationships/edit-relationship/edit-relationship.component.spec.ts @@ -2,7 +2,6 @@ import { NO_ERRORS_SCHEMA } from '@angular/core'; import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; import { TranslateModule } from '@ngx-translate/core'; import { of as observableOf } from 'rxjs'; -import { FieldChangeType } from '../../../../core/data/object-updates/object-updates.actions'; import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service'; import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model'; import { Relationship } from '../../../../core/shared/item-relationships/relationship.model'; @@ -11,6 +10,7 @@ import { EditRelationshipComponent } from './edit-relationship.component'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils'; import { createPaginatedList } from '../../../../shared/testing/utils.test'; +import { FieldChangeType } from '../../../../core/data/object-updates/field-change-type.model'; let objectUpdatesService; const url = 'http://test-url.com/test-url'; diff --git a/src/app/item-page/edit-item-page/item-relationships/edit-relationship/edit-relationship.component.ts b/src/app/item-page/edit-item-page/item-relationships/edit-relationship/edit-relationship.component.ts index 3a3a8f9675..112531cce5 100644 --- a/src/app/item-page/edit-item-page/item-relationships/edit-relationship/edit-relationship.component.ts +++ b/src/app/item-page/edit-item-page/item-relationships/edit-relationship/edit-relationship.component.ts @@ -1,10 +1,8 @@ import { Component, Input, OnChanges } from '@angular/core'; import { combineLatest as observableCombineLatest, Observable, of } from 'rxjs'; import { filter, map, switchMap, take } from 'rxjs/operators'; -import { FieldChangeType } from '../../../../core/data/object-updates/object-updates.actions'; import { DeleteRelationship, - FieldUpdate, RelationshipIdentifiable } from '../../../../core/data/object-updates/object-updates.reducer'; import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service'; @@ -13,6 +11,8 @@ import { getFirstSucceededRemoteData, getRemoteDataPayload } from '../../../../c import { ViewMode } from '../../../../core/shared/view-mode.model'; import { hasValue, isNotEmpty } from '../../../../shared/empty.util'; import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { FieldUpdate } from '../../../../core/data/object-updates/field-update.model'; +import { FieldChangeType } from '../../../../core/data/object-updates/field-change-type.model'; @Component({ // tslint:disable-next-line:component-selector diff --git a/src/app/item-page/edit-item-page/item-relationships/item-relationships.component.spec.ts b/src/app/item-page/edit-item-page/item-relationships/item-relationships.component.spec.ts index 65fd49f795..9fe8545a7d 100644 --- a/src/app/item-page/edit-item-page/item-relationships/item-relationships.component.spec.ts +++ b/src/app/item-page/edit-item-page/item-relationships/item-relationships.component.spec.ts @@ -9,7 +9,6 @@ import { ObjectCacheService } from '../../../core/cache/object-cache.service'; import { RestResponse } from '../../../core/cache/response.models'; import { EntityTypeService } from '../../../core/data/entity-type.service'; import { ItemDataService } from '../../../core/data/item-data.service'; -import { FieldChangeType } from '../../../core/data/object-updates/object-updates.actions'; import { ObjectUpdatesService } from '../../../core/data/object-updates/object-updates.service'; import { RelationshipService } from '../../../core/data/relationship.service'; import { RequestService } from '../../../core/data/request.service'; @@ -25,6 +24,7 @@ import { RouterStub } from '../../../shared/testing/router.stub'; import { ItemRelationshipsComponent } from './item-relationships.component'; import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; import { createPaginatedList } from '../../../shared/testing/utils.test'; +import { FieldChangeType } from '../../../core/data/object-updates/field-change-type.model'; let comp: any; let fixture: ComponentFixture; diff --git a/src/app/item-page/edit-item-page/item-relationships/item-relationships.component.ts b/src/app/item-page/edit-item-page/item-relationships/item-relationships.component.ts index e22ad8ddcb..07a35e03b4 100644 --- a/src/app/item-page/edit-item-page/item-relationships/item-relationships.component.ts +++ b/src/app/item-page/edit-item-page/item-relationships/item-relationships.component.ts @@ -2,8 +2,6 @@ import { ChangeDetectorRef, Component } from '@angular/core'; import { Item } from '../../../core/shared/item.model'; import { DeleteRelationship, - FieldUpdate, - FieldUpdates, RelationshipIdentifiable, } from '../../../core/data/object-updates/object-updates.reducer'; import { Observable } from 'rxjs/internal/Observable'; @@ -28,10 +26,12 @@ import { RequestService } from '../../../core/data/request.service'; import { RelationshipType } from '../../../core/shared/item-relationships/relationship-type.model'; import { ItemType } from '../../../core/shared/item-relationships/item-type.model'; import { EntityTypeService } from '../../../core/data/entity-type.service'; -import { FieldChangeType } from '../../../core/data/object-updates/object-updates.actions'; import { Relationship } from '../../../core/shared/item-relationships/relationship.model'; import { NoContent } from '../../../core/shared/NoContent.model'; import { hasValue } from '../../../shared/empty.util'; +import { FieldUpdate } from '../../../core/data/object-updates/field-update.model'; +import { FieldUpdates } from '../../../core/data/object-updates/field-updates.model'; +import { FieldChangeType } from '../../../core/data/object-updates/field-change-type.model'; @Component({ selector: 'ds-item-relationships', diff --git a/src/app/item-page/item-page-routing.module.ts b/src/app/item-page/item-page-routing.module.ts index f2d0a23935..de749915ac 100644 --- a/src/app/item-page/item-page-routing.module.ts +++ b/src/app/item-page/item-page-routing.module.ts @@ -8,10 +8,10 @@ import { LinkService } from '../core/cache/builders/link.service'; import { UploadBitstreamComponent } from './bitstreams/upload/upload-bitstream.component'; import { ITEM_EDIT_PATH, UPLOAD_BITSTREAM_PATH } from './item-page-routing-paths'; import { ItemPageAdministratorGuard } from './item-page-administrator.guard'; -import { MenuItemType } from '../shared/menu/initial-menus-state'; import { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model'; import { ThemedItemPageComponent } from './simple/themed-item-page.component'; import { ThemedFullItemPageComponent } from './full/themed-full-item-page.component'; +import { MenuItemType } from '../shared/menu/menu-item-type.model'; @NgModule({ imports: [ diff --git a/src/app/item-page/simple/item-page.component.ts b/src/app/item-page/simple/item-page.component.ts index cc23ba86d5..f410707731 100644 --- a/src/app/item-page/simple/item-page.component.ts +++ b/src/app/item-page/simple/item-page.component.ts @@ -9,10 +9,11 @@ import { RemoteData } from '../../core/data/remote-data'; import { Item } from '../../core/shared/item.model'; import { fadeInOut } from '../../shared/animations/fade'; -import { getAllSucceededRemoteDataPayload, redirectOn4xx } from '../../core/shared/operators'; +import { getAllSucceededRemoteDataPayload} from '../../core/shared/operators'; import { ViewMode } from '../../core/shared/view-mode.model'; import { AuthService } from '../../core/auth/auth.service'; import { getItemPageRoute } from '../item-page-routing-paths'; +import { redirectOn4xx } from '../../core/shared/authorized.operators'; /** * This component renders a simple item page. diff --git a/src/app/my-dspace-page/my-dspace-configuration.service.spec.ts b/src/app/my-dspace-page/my-dspace-configuration.service.spec.ts index fa278da967..611a3177ca 100644 --- a/src/app/my-dspace-page/my-dspace-configuration.service.spec.ts +++ b/src/app/my-dspace-page/my-dspace-configuration.service.spec.ts @@ -11,6 +11,12 @@ import { cold, hot } from 'jasmine-marbles'; import { MyDSpaceConfigurationValueType } from './my-dspace-configuration-value-type'; import { PaginationServiceStub } from '../shared/testing/pagination-service.stub'; import { PaginationService } from '../core/pagination/pagination.service'; +import { LinkService } from '../core/cache/builders/link.service'; +import { HALEndpointService } from '../core/shared/hal-endpoint.service'; +import { RequestService } from '../core/data/request.service'; +import { RemoteDataBuildService } from '../core/cache/builders/remote-data-build.service'; +import { HALEndpointServiceStub } from '../shared/testing/hal-endpoint-service.stub'; +import { getMockRemoteDataBuildService } from '../shared/mocks/remote-data-build.service.mock'; describe('MyDSpaceConfigurationService', () => { let service: MyDSpaceConfigurationService; @@ -44,8 +50,13 @@ describe('MyDSpaceConfigurationService', () => { const roleService: any = new RoleServiceMock(); + const linkService: any = {}; + const halService: any = new HALEndpointServiceStub(''); + const requestService: any = {}; + const rdb: any = getMockRemoteDataBuildService(); + beforeEach(() => { - service = new MyDSpaceConfigurationService(roleService, spy, paginationService as any, activatedRoute); + service = new MyDSpaceConfigurationService(roleService, spy, paginationService as any, activatedRoute, linkService, halService, requestService, rdb); }); describe('when the scope is called', () => { diff --git a/src/app/my-dspace-page/my-dspace-configuration.service.ts b/src/app/my-dspace-page/my-dspace-configuration.service.ts index 82f76eb776..7f741506c3 100644 --- a/src/app/my-dspace-page/my-dspace-configuration.service.ts +++ b/src/app/my-dspace-page/my-dspace-configuration.service.ts @@ -12,6 +12,10 @@ import { PaginationComponentOptions } from '../shared/pagination/pagination-comp import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model'; import { RouteService } from '../core/services/route.service'; import { PaginationService } from '../core/pagination/pagination.service'; +import { LinkService } from '../core/cache/builders/link.service'; +import { HALEndpointService } from '../core/shared/hal-endpoint.service'; +import { RequestService } from '../core/data/request.service'; +import { RemoteDataBuildService } from '../core/cache/builders/remote-data-build.service'; /** * Service that performs all actions that have to do with the current mydspace configuration @@ -58,13 +62,21 @@ export class MyDSpaceConfigurationService extends SearchConfigurationService { * @param {RouteService} routeService * @param {PaginationService} paginationService * @param {ActivatedRoute} route + * @param linkService + * @param halService + * @param requestService + * @param rdb */ constructor(protected roleService: RoleService, protected routeService: RouteService, protected paginationService: PaginationService, - protected route: ActivatedRoute) { + protected route: ActivatedRoute, + protected linkService: LinkService, + protected halService: HALEndpointService, + protected requestService: RequestService, + protected rdb: RemoteDataBuildService) { - super(routeService, paginationService, route); + super(routeService, paginationService, route, linkService, halService, requestService, rdb); // override parent class initialization this._defaults = null; diff --git a/src/app/my-dspace-page/my-dspace-page.component.spec.ts b/src/app/my-dspace-page/my-dspace-page.component.spec.ts index b4b75b42a0..a7f16d4184 100644 --- a/src/app/my-dspace-page/my-dspace-page.component.spec.ts +++ b/src/app/my-dspace-page/my-dspace-page.component.spec.ts @@ -16,8 +16,6 @@ import { CommunityDataService } from '../core/data/community-data.service'; import { HostWindowService } from '../shared/host-window.service'; import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model'; import { MyDSpacePageComponent, SEARCH_CONFIG_SERVICE } from './my-dspace-page.component'; -import { RouteService } from '../core/services/route.service'; -import { routeServiceStub } from '../shared/testing/route-service.stub'; import { SearchConfigurationServiceStub } from '../shared/testing/search-configuration-service.stub'; import { SearchService } from '../core/shared/search/search.service'; import { SearchConfigurationService } from '../core/shared/search/search-configuration.service'; @@ -90,7 +88,6 @@ describe('MyDSpacePageComponent', () => { useValue: jasmine.createSpyObj('communityService', ['findById', 'findAll']) }, { provide: ActivatedRoute, useValue: activatedRouteStub }, - { provide: RouteService, useValue: routeServiceStub }, { provide: Store, useValue: store }, @@ -109,9 +106,14 @@ describe('MyDSpacePageComponent', () => { { provide: SearchFilterService, useValue: {} - }, { + }, + { provide: SEARCH_CONFIG_SERVICE, - useValue: new SearchConfigurationServiceStub() + useFactory: () => new SearchConfigurationServiceStub() + }, + { + provide: SearchConfigurationService, + useFactory: new SearchConfigurationServiceStub() }, { provide: RoleService, @@ -120,7 +122,14 @@ describe('MyDSpacePageComponent', () => { ], schemas: [NO_ERRORS_SCHEMA] }).overrideComponent(MyDSpacePageComponent, { - set: { changeDetection: ChangeDetectionStrategy.Default } + set: { + changeDetection: ChangeDetectionStrategy.Default, + providers: [ + { + provide: SEARCH_CONFIG_SERVICE, + useClass: SearchConfigurationServiceStub + } + ] } }).compileComponents(); })); @@ -130,6 +139,7 @@ describe('MyDSpacePageComponent', () => { fixture.detectChanges(); searchServiceObject = (comp as any).service; searchConfigurationServiceObject = (comp as any).searchConfigService; + console.log(searchConfigurationServiceObject) }); afterEach(() => { @@ -198,9 +208,7 @@ describe('MyDSpacePageComponent', () => { }); it('should have initialized the sortOptions$ observable', (done) => { - comp.sortOptions$.subscribe((sortOptions) => { - expect(sortOptions.length).toEqual(2); expect(sortOptions[0]).toEqual(new SortOptions('score', SortDirection.ASC)); expect(sortOptions[1]).toEqual(new SortOptions('score', SortDirection.DESC)); diff --git a/src/app/my-dspace-page/my-dspace-page.component.ts b/src/app/my-dspace-page/my-dspace-page.component.ts index 3ded17191e..8745f458b3 100644 --- a/src/app/my-dspace-page/my-dspace-page.component.ts +++ b/src/app/my-dspace-page/my-dspace-page.component.ts @@ -160,7 +160,7 @@ export class MyDSpacePageComponent implements OnInit { ); const configuration$ = this.searchConfigService.getCurrentConfiguration('workspace'); - const searchConfig$ = this.searchConfigService.getConfigurationSearchConfigObservable(configuration$, this.service); + const searchConfig$ = this.searchConfigService.getConfigurationSearchConfigObservable(configuration$); this.sortOptions$ = this.searchConfigService.getConfigurationSortOptionsObservable(searchConfig$); this.searchConfigService.initializeSortOptionsFromConfiguration(searchConfig$); diff --git a/src/app/navbar/expandable-navbar-section/expandable-navbar-section.component.ts b/src/app/navbar/expandable-navbar-section/expandable-navbar-section.component.ts index 068854d6f8..5bc69bcbb4 100644 --- a/src/app/navbar/expandable-navbar-section/expandable-navbar-section.component.ts +++ b/src/app/navbar/expandable-navbar-section/expandable-navbar-section.component.ts @@ -1,11 +1,11 @@ import { Component, Inject, Injector, OnInit } from '@angular/core'; import { NavbarSectionComponent } from '../navbar-section/navbar-section.component'; import { MenuService } from '../../shared/menu/menu.service'; -import { MenuID } from '../../shared/menu/initial-menus-state'; import { slide } from '../../shared/animations/slide'; import { first } from 'rxjs/operators'; import { HostWindowService } from '../../shared/host-window.service'; import { rendersSectionForMenu } from '../../shared/menu/menu-section.decorator'; +import { MenuID } from '../../shared/menu/menu-id.model'; /** * Represents an expandable section in the navbar diff --git a/src/app/navbar/navbar-section/navbar-section.component.ts b/src/app/navbar/navbar-section/navbar-section.component.ts index e1488de3d3..9b86aa10f2 100644 --- a/src/app/navbar/navbar-section/navbar-section.component.ts +++ b/src/app/navbar/navbar-section/navbar-section.component.ts @@ -1,8 +1,8 @@ import { Component, Inject, Injector, OnInit } from '@angular/core'; import { MenuSectionComponent } from '../../shared/menu/menu-section/menu-section.component'; import { MenuService } from '../../shared/menu/menu.service'; -import { MenuID } from '../../shared/menu/initial-menus-state'; import { rendersSectionForMenu } from '../../shared/menu/menu-section.decorator'; +import { MenuID } from '../../shared/menu/menu-id.model'; /** * Represents a non-expandable section in the navbar diff --git a/src/app/navbar/navbar.component.ts b/src/app/navbar/navbar.component.ts index e741cea285..98b1eb5056 100644 --- a/src/app/navbar/navbar.component.ts +++ b/src/app/navbar/navbar.component.ts @@ -2,11 +2,12 @@ import { Component, Injector } from '@angular/core'; import { slideMobileNav } from '../shared/animations/slide'; import { MenuComponent } from '../shared/menu/menu.component'; import { MenuService } from '../shared/menu/menu.service'; -import { MenuID, MenuItemType } from '../shared/menu/initial-menus-state'; import { TextMenuItemModel } from '../shared/menu/menu-item/models/text.model'; import { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model'; import { HostWindowService } from '../shared/host-window.service'; import { environment } from '../../environments/environment'; +import { MenuID } from '../shared/menu/menu-id.model'; +import { MenuItemType } from '../shared/menu/menu-item-type.model'; /** * Component representing the public navbar diff --git a/src/app/navbar/navbar.effects.spec.ts b/src/app/navbar/navbar.effects.spec.ts index 0bd3fa8e6e..bcb105d5b2 100644 --- a/src/app/navbar/navbar.effects.spec.ts +++ b/src/app/navbar/navbar.effects.spec.ts @@ -6,9 +6,9 @@ import { provideMockActions } from '@ngrx/effects/testing'; import { cold, hot } from 'jasmine-marbles'; import * as fromRouter from '@ngrx/router-store'; import { CollapseMenuAction } from '../shared/menu/menu.actions'; -import { MenuID } from '../shared/menu/initial-menus-state'; import { MenuService } from '../shared/menu/menu.service'; import { MenuServiceStub } from '../shared/testing/menu-service.stub'; +import { MenuID } from '../shared/menu/menu-id.model'; describe('NavbarEffects', () => { let navbarEffects: NavbarEffects; diff --git a/src/app/navbar/navbar.effects.ts b/src/app/navbar/navbar.effects.ts index 6cb11f21c0..bdf6fe5be1 100644 --- a/src/app/navbar/navbar.effects.ts +++ b/src/app/navbar/navbar.effects.ts @@ -9,10 +9,10 @@ import { ExpandMenuPreviewAction, MenuActionTypes } from '../shared/menu/menu.actions'; -import { MenuID } from '../shared/menu/initial-menus-state'; import { MenuService } from '../shared/menu/menu.service'; -import { MenuState } from '../shared/menu/menu.reducer'; import { NoOpAction } from '../shared/ngrx/no-op.action'; +import { MenuState } from '../shared/menu/menu-state.model'; +import { MenuID } from '../shared/menu/menu-id.model'; @Injectable() export class NavbarEffects { diff --git a/src/app/process-page/detail/process-detail.component.ts b/src/app/process-page/detail/process-detail.component.ts index 481189cf05..775b9da7ca 100644 --- a/src/app/process-page/detail/process-detail.component.ts +++ b/src/app/process-page/detail/process-detail.component.ts @@ -13,7 +13,6 @@ import { Bitstream } from '../../core/shared/bitstream.model'; import { DSpaceObject } from '../../core/shared/dspace-object.model'; import { getFirstSucceededRemoteDataPayload, - redirectOn4xx, getFirstSucceededRemoteData } from '../../core/shared/operators'; import { URLCombiner } from '../../core/url-combiner/url-combiner'; @@ -21,6 +20,7 @@ import { AlertType } from '../../shared/alert/aletr-type'; import { hasValue } from '../../shared/empty.util'; import { ProcessStatus } from '../processes/process-status.model'; import { Process } from '../processes/process.model'; +import { redirectOn4xx } from '../../core/shared/authorized.operators'; @Component({ selector: 'ds-process-detail', diff --git a/src/app/process-page/processes/process.model.ts b/src/app/process-page/processes/process.model.ts index 9f9a3ee262..d5f6e77d32 100644 --- a/src/app/process-page/processes/process.model.ts +++ b/src/app/process-page/processes/process.model.ts @@ -2,7 +2,6 @@ import { Bitstream } from '../../core/shared/bitstream.model'; import { PROCESS_OUTPUT_TYPE } from '../../core/shared/process-output.resource-type'; import { ProcessStatus } from './process-status.model'; import { ProcessParameter } from './process-parameter.model'; -import { CacheableObject } from '../../core/cache/object-cache.reducer'; import { HALLink } from '../../core/shared/hal-link.model'; import { autoserialize, deserialize } from 'cerialize'; import { PROCESS } from './process.resource-type'; @@ -13,6 +12,7 @@ import { Observable } from 'rxjs'; import { RemoteData } from '../../core/data/remote-data'; import { SCRIPT } from '../scripts/script.resource-type'; import { Script } from '../scripts/script.model'; +import { CacheableObject } from '../../core/cache/cacheable-object.model'; /** * Object representing a process diff --git a/src/app/process-page/scripts/script.model.ts b/src/app/process-page/scripts/script.model.ts index e94d233fc2..afb1d41524 100644 --- a/src/app/process-page/scripts/script.model.ts +++ b/src/app/process-page/scripts/script.model.ts @@ -1,4 +1,3 @@ -import { CacheableObject } from '../../core/cache/object-cache.reducer'; import { HALLink } from '../../core/shared/hal-link.model'; import { autoserialize, deserialize } from 'cerialize'; import { SCRIPT } from './script.resource-type'; @@ -6,6 +5,7 @@ import { ScriptParameter } from './script-parameter.model'; import { typedObject } from '../../core/cache/builders/build-decorators'; import { excludeFromEquals } from '../../core/utilities/equals.decorators'; import { ResourceType } from '../../core/shared/resource-type'; +import { CacheableObject } from '../../core/cache/cacheable-object.model'; /** * Object representing a script diff --git a/src/app/root/root.component.ts b/src/app/root/root.component.ts index 209f17b520..ed6687033f 100644 --- a/src/app/root/root.component.ts +++ b/src/app/root/root.component.ts @@ -13,12 +13,12 @@ import { NativeWindowRef, NativeWindowService } from '../core/services/window.se import { AuthService } from '../core/auth/auth.service'; import { CSSVariableService } from '../shared/sass-helper/sass-helper.service'; import { MenuService } from '../shared/menu/menu.service'; -import { MenuID } from '../shared/menu/initial-menus-state'; import { HostWindowService } from '../shared/host-window.service'; import { ThemeConfig } from '../../config/theme.model'; import { Angulartics2DSpace } from '../statistics/angulartics/dspace-provider'; import { environment } from '../../environments/environment'; import { slideSidebarPadding } from '../shared/animations/slide'; +import { MenuID } from '../shared/menu/menu-id.model'; @Component({ selector: 'ds-root', diff --git a/src/app/search-page/search.component.spec.ts b/src/app/search-page/search.component.spec.ts index 445f4614d3..dae3b1479a 100644 --- a/src/app/search-page/search.component.spec.ts +++ b/src/app/search-page/search.component.spec.ts @@ -47,7 +47,7 @@ const searchServiceStub = jasmine.createSpyObj('SearchService', { search: mockResults, getSearchLink: '/search', getScopes: observableOf(['test-scope']), - getSearchConfigurationFor: createSuccessfulRemoteDataObject$({ sortOptions: [sortOption]}) + getSearchConfigurationFor: createSuccessfulRemoteDataObject$({ sortOptions: [sortOption] }) }); const configurationParam = 'default'; const queryParam = 'test query'; @@ -137,7 +137,13 @@ export function configureSearchComponentTestingModule(compType, additionalDeclar ], schemas: [NO_ERRORS_SCHEMA] }).overrideComponent(compType, { - set: { changeDetection: ChangeDetectionStrategy.Default } + set: { + changeDetection: ChangeDetectionStrategy.Default, + providers: [{ + provide: SEARCH_CONFIG_SERVICE, + useValue: new SearchConfigurationServiceStub() + }] + } }).compileComponents(); } diff --git a/src/app/search-page/search.component.ts b/src/app/search-page/search.component.ts index d4d65b87fe..1a9fb69c46 100644 --- a/src/app/search-page/search.component.ts +++ b/src/app/search-page/search.component.ts @@ -144,7 +144,7 @@ export class SearchComponent implements OnInit { this.configuration$ = this.searchConfigService.getCurrentConfiguration('default'); } - const searchConfig$ = this.searchConfigService.getConfigurationSearchConfigObservable(this.configuration$, this.service); + const searchConfig$ = this.searchConfigService.getConfigurationSearchConfigObservable(this.configuration$); this.sortOptions$ = this.searchConfigService.getConfigurationSortOptionsObservable(searchConfig$); this.searchConfigService.initializeSortOptionsFromConfiguration(searchConfig$); diff --git a/src/app/shared/bitstream-download-page/bitstream-download-page.component.ts b/src/app/shared/bitstream-download-page/bitstream-download-page.component.ts index a09d7e8b3e..0bd44ce496 100644 --- a/src/app/shared/bitstream-download-page/bitstream-download-page.component.ts +++ b/src/app/shared/bitstream-download-page/bitstream-download-page.component.ts @@ -2,7 +2,7 @@ import { Component, OnInit } from '@angular/core'; import { filter, map, switchMap, take } from 'rxjs/operators'; import { ActivatedRoute, Router } from '@angular/router'; import { hasValue, isNotEmpty } from '../empty.util'; -import { getRemoteDataPayload, redirectOn4xx } from '../../core/shared/operators'; +import { getRemoteDataPayload} from '../../core/shared/operators'; import { Bitstream } from '../../core/shared/bitstream.model'; import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; @@ -12,6 +12,7 @@ import { FileService } from '../../core/shared/file.service'; import { HardRedirectService } from '../../core/services/hard-redirect.service'; import { getForbiddenRoute } from '../../app-routing-paths'; import { RemoteData } from '../../core/data/remote-data'; +import { redirectOn4xx } from '../../core/shared/authorized.operators'; @Component({ selector: 'ds-bitstream-download-page', diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts index 7adb9a837b..8ff16de5b5 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts @@ -323,7 +323,7 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo getAllSucceededRemoteData(), getRemoteDataPayload(), map((leftItem: Item) => { - return new ReorderableRelationship(relationship, leftItem.uuid !== item.uuid, this.relationshipService, this.store, this.model.submissionId); + return new ReorderableRelationship(relationship, leftItem.uuid !== item.uuid, this.store, this.model.submissionId); }), ) ), diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component.ts index 899400eba0..326a8f71b7 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component.ts @@ -5,7 +5,6 @@ import { Store } from '@ngrx/store'; import { BehaviorSubject, Subscription } from 'rxjs'; import { filter, take } from 'rxjs/operators'; import { AppState } from '../../../../../app.reducer'; -import { RelationshipService } from '../../../../../core/data/relationship.service'; import { Relationship } from '../../../../../core/shared/item-relationships/relationship.model'; import { Item } from '../../../../../core/shared/item.model'; import { ItemMetadataRepresentation } from '../../../../../core/shared/metadata-representation/item/item-metadata-representation.model'; @@ -103,7 +102,6 @@ export class ReorderableRelationship extends Reorderable { constructor( public relationship: Relationship, public useLeftItem: boolean, - protected relationshipService: RelationshipService, protected store: Store, protected submissionID: string, oldIndex?: number, diff --git a/src/app/shared/menu/initial-menus-state.ts b/src/app/shared/menu/initial-menus-state.ts index 7b900540b6..d684afc3e7 100644 --- a/src/app/shared/menu/initial-menus-state.ts +++ b/src/app/shared/menu/initial-menus-state.ts @@ -1,19 +1,5 @@ -import { MenusState } from './menu.reducer'; - -/** - * Availavle Menu IDs - */ -export enum MenuID { - ADMIN = 'admin-sidebar', - PUBLIC = 'public' -} - -/** - * List of possible MenuItemTypes - */ -export enum MenuItemType { - TEXT, LINK, ALTMETRIC, SEARCH, ONCLICK -} +import { MenusState } from './menus-state.model'; +import { MenuID } from './menu-id.model'; /** * The initial state of the menus diff --git a/src/app/shared/menu/menu-id.model.ts b/src/app/shared/menu/menu-id.model.ts new file mode 100644 index 0000000000..50ce924a1b --- /dev/null +++ b/src/app/shared/menu/menu-id.model.ts @@ -0,0 +1,7 @@ +/** + * Availavle Menu IDs + */ +export enum MenuID { + ADMIN = 'admin-sidebar', + PUBLIC = 'public' +} diff --git a/src/app/shared/menu/menu-item-type.model.ts b/src/app/shared/menu/menu-item-type.model.ts new file mode 100644 index 0000000000..713dc6ab88 --- /dev/null +++ b/src/app/shared/menu/menu-item-type.model.ts @@ -0,0 +1,6 @@ +/** + * List of possible MenuItemTypes + */ +export enum MenuItemType { + TEXT, LINK, ALTMETRIC, SEARCH, ONCLICK +} diff --git a/src/app/shared/menu/menu-item.decorator.ts b/src/app/shared/menu/menu-item.decorator.ts index 3740a4eba4..ef3910e6a8 100644 --- a/src/app/shared/menu/menu-item.decorator.ts +++ b/src/app/shared/menu/menu-item.decorator.ts @@ -1,4 +1,4 @@ -import { MenuItemType } from './initial-menus-state'; +import { MenuItemType } from './menu-item-type.model'; const menuMenuItemComponentMap = new Map(); diff --git a/src/app/shared/menu/menu-item/link-menu-item.component.ts b/src/app/shared/menu/menu-item/link-menu-item.component.ts index e5b66c5aab..8e0767cdda 100644 --- a/src/app/shared/menu/menu-item/link-menu-item.component.ts +++ b/src/app/shared/menu/menu-item/link-menu-item.component.ts @@ -1,9 +1,9 @@ import { Component, Inject, Input, OnInit } from '@angular/core'; import { LinkMenuItemModel } from './models/link.model'; -import { MenuItemType } from '../initial-menus-state'; import { rendersMenuItemForType } from '../menu-item.decorator'; import { isNotEmpty } from '../../empty.util'; import { environment } from '../../../../environments/environment'; +import { MenuItemType } from '../menu-item-type.model'; /** * Component that renders a menu section of type LINK diff --git a/src/app/shared/menu/menu-item/models/altmetric.model.ts b/src/app/shared/menu/menu-item/models/altmetric.model.ts index 8dc3f01363..9dabb43a92 100644 --- a/src/app/shared/menu/menu-item/models/altmetric.model.ts +++ b/src/app/shared/menu/menu-item/models/altmetric.model.ts @@ -1,5 +1,5 @@ -import { MenuItemType } from '../../initial-menus-state'; import { MenuItemModel } from './menu-item.model'; +import { MenuItemType } from '../../menu-item-type.model'; /** * Model representing an Altmetric Menu Section diff --git a/src/app/shared/menu/menu-item/models/link.model.ts b/src/app/shared/menu/menu-item/models/link.model.ts index a5b3671f62..e4f43a0c80 100644 --- a/src/app/shared/menu/menu-item/models/link.model.ts +++ b/src/app/shared/menu/menu-item/models/link.model.ts @@ -1,5 +1,5 @@ import { MenuItemModel } from './menu-item.model'; -import { MenuItemType } from '../../initial-menus-state'; +import { MenuItemType } from '../../menu-item-type.model'; /** * Model representing an Link Menu Section diff --git a/src/app/shared/menu/menu-item/models/menu-item.model.ts b/src/app/shared/menu/menu-item/models/menu-item.model.ts index 7bf5fca066..b60b55719e 100644 --- a/src/app/shared/menu/menu-item/models/menu-item.model.ts +++ b/src/app/shared/menu/menu-item/models/menu-item.model.ts @@ -1,4 +1,4 @@ -import { MenuItemType } from '../../initial-menus-state'; +import { MenuItemType } from '../../menu-item-type.model'; /** * Interface for models representing a Menu Section diff --git a/src/app/shared/menu/menu-item/models/onclick.model.ts b/src/app/shared/menu/menu-item/models/onclick.model.ts index 4cef3084f9..2501163bfc 100644 --- a/src/app/shared/menu/menu-item/models/onclick.model.ts +++ b/src/app/shared/menu/menu-item/models/onclick.model.ts @@ -1,5 +1,5 @@ import { MenuItemModel } from './menu-item.model'; -import { MenuItemType } from '../../initial-menus-state'; +import { MenuItemType } from '../../menu-item-type.model'; /** * Model representing an OnClick Menu Section diff --git a/src/app/shared/menu/menu-item/models/search.model.ts b/src/app/shared/menu/menu-item/models/search.model.ts index e8eeda5501..eac281da51 100644 --- a/src/app/shared/menu/menu-item/models/search.model.ts +++ b/src/app/shared/menu/menu-item/models/search.model.ts @@ -1,5 +1,5 @@ -import { MenuItemType } from '../../initial-menus-state'; import { MenuItemModel } from './menu-item.model'; +import { MenuItemType } from '../../menu-item-type.model'; /** * Model representing an Search Bar Menu Section diff --git a/src/app/shared/menu/menu-item/models/text.model.ts b/src/app/shared/menu/menu-item/models/text.model.ts index bbaf7804d9..70dc7c1203 100644 --- a/src/app/shared/menu/menu-item/models/text.model.ts +++ b/src/app/shared/menu/menu-item/models/text.model.ts @@ -1,5 +1,5 @@ -import { MenuItemType } from '../../initial-menus-state'; import { MenuItemModel } from './menu-item.model'; +import { MenuItemType } from '../../menu-item-type.model'; /** * Model representing an Text Menu Section diff --git a/src/app/shared/menu/menu-item/onclick-menu-item.component.ts b/src/app/shared/menu/menu-item/onclick-menu-item.component.ts index 95b896ed64..e41dfea187 100644 --- a/src/app/shared/menu/menu-item/onclick-menu-item.component.ts +++ b/src/app/shared/menu/menu-item/onclick-menu-item.component.ts @@ -1,7 +1,7 @@ import { Component, Inject } from '@angular/core'; -import { MenuItemType } from '../initial-menus-state'; import { rendersMenuItemForType } from '../menu-item.decorator'; import { OnClickMenuItemModel } from './models/onclick.model'; +import { MenuItemType } from '../menu-item-type.model'; /** * Component that renders a menu section of type ONCLICK diff --git a/src/app/shared/menu/menu-item/text-menu-item.component.ts b/src/app/shared/menu/menu-item/text-menu-item.component.ts index f7d3402be0..af690d198c 100644 --- a/src/app/shared/menu/menu-item/text-menu-item.component.ts +++ b/src/app/shared/menu/menu-item/text-menu-item.component.ts @@ -1,7 +1,7 @@ import { Component, Inject, Input } from '@angular/core'; import { TextMenuItemModel } from './models/text.model'; -import { MenuItemType } from '../initial-menus-state'; import { rendersMenuItemForType } from '../menu-item.decorator'; +import { MenuItemType } from '../menu-item-type.model'; /** * Component that renders a menu section of type TEXT diff --git a/src/app/shared/menu/menu-section-Index.model.ts b/src/app/shared/menu/menu-section-Index.model.ts new file mode 100644 index 0000000000..f9b12f8236 --- /dev/null +++ b/src/app/shared/menu/menu-section-Index.model.ts @@ -0,0 +1,6 @@ +/** + * Represents the a mapping of all sections to their subsections for a menu in the store + */ +export interface MenuSectionIndex { + [id: string]: string[]; +} diff --git a/src/app/shared/menu/menu-section.decorator.ts b/src/app/shared/menu/menu-section.decorator.ts index c27e870e13..2aa695aabd 100644 --- a/src/app/shared/menu/menu-section.decorator.ts +++ b/src/app/shared/menu/menu-section.decorator.ts @@ -1,4 +1,4 @@ -import { MenuID } from './initial-menus-state'; +import { MenuID } from './menu-id.model'; const menuComponentMap = new Map(); diff --git a/src/app/shared/menu/menu-section.model.ts b/src/app/shared/menu/menu-section.model.ts new file mode 100644 index 0000000000..f14b029d77 --- /dev/null +++ b/src/app/shared/menu/menu-section.model.ts @@ -0,0 +1,15 @@ +import { MenuItemModel } from './menu-item/models/menu-item.model'; + +/** + * Represents the state of a single menu section in the store + */ +export class MenuSection { + id: string; + parentID?: string; + visible: boolean; + active: boolean; + model: MenuItemModel; + index?: number; + icon?: string; + shouldPersistOnRouteChange? = false; +} diff --git a/src/app/shared/menu/menu-section/menu-section.component.spec.ts b/src/app/shared/menu/menu-section/menu-section.component.spec.ts index 27d9821d52..f40f45f9b4 100644 --- a/src/app/shared/menu/menu-section/menu-section.component.spec.ts +++ b/src/app/shared/menu/menu-section/menu-section.component.spec.ts @@ -5,9 +5,9 @@ import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/co import { MenuSectionComponent } from './menu-section.component'; import { MenuService } from '../menu.service'; import { MenuServiceStub } from '../../testing/menu-service.stub'; -import { MenuSection } from '../menu.reducer'; import { of as observableOf } from 'rxjs'; import { LinkMenuItemComponent } from '../menu-item/link-menu-item.component'; +import { MenuSection } from '../menu-section.model'; describe('MenuSectionComponent', () => { let comp: MenuSectionComponent; diff --git a/src/app/shared/menu/menu-section/menu-section.component.ts b/src/app/shared/menu/menu-section/menu-section.component.ts index fcd96c65f1..bef90b21c3 100644 --- a/src/app/shared/menu/menu-section/menu-section.component.ts +++ b/src/app/shared/menu/menu-section/menu-section.component.ts @@ -1,13 +1,14 @@ import { Component, Injector, OnDestroy, OnInit } from '@angular/core'; import { MenuService } from '../menu.service'; -import { MenuSection } from '../menu.reducer'; import { getComponentForMenuItemType } from '../menu-item.decorator'; -import { MenuID, MenuItemType } from '../initial-menus-state'; import { hasNoValue, hasValue } from '../../empty.util'; import { BehaviorSubject, Observable, Subscription } from 'rxjs'; import { MenuItemModel } from '../menu-item/models/menu-item.model'; import { distinctUntilChanged, switchMap } from 'rxjs/operators'; import { GenericConstructor } from '../../../core/shared/generic-constructor'; +import { MenuSection } from '../menu-section.model'; +import { MenuID } from '../menu-id.model'; +import { MenuItemType } from '../menu-item-type.model'; /** * A basic implementation of a menu section's component diff --git a/src/app/shared/menu/menu-sections.model.ts b/src/app/shared/menu/menu-sections.model.ts new file mode 100644 index 0000000000..fc9dd81832 --- /dev/null +++ b/src/app/shared/menu/menu-sections.model.ts @@ -0,0 +1,8 @@ +import { MenuSection } from './menu-section.model'; + +/** + * Represents the state of all menu sections in the store + */ +export interface MenuSections { + [id: string]: MenuSection; +} diff --git a/src/app/shared/menu/menu-state.model.ts b/src/app/shared/menu/menu-state.model.ts new file mode 100644 index 0000000000..0bedac2129 --- /dev/null +++ b/src/app/shared/menu/menu-state.model.ts @@ -0,0 +1,15 @@ +import { MenuSectionIndex } from './menu-section-Index.model'; +import { MenuSections } from './menu-sections.model'; +import { MenuID } from './menu-id.model'; + +/** + * Represents the state of a single menu in the store + */ +export interface MenuState { + id: MenuID; + collapsed: boolean; + previewCollapsed: boolean; + visible: boolean; + sections: MenuSections; + sectionToSubsectionIndex: MenuSectionIndex; +} diff --git a/src/app/shared/menu/menu.actions.ts b/src/app/shared/menu/menu.actions.ts index 3f5bc995b1..56607977e3 100644 --- a/src/app/shared/menu/menu.actions.ts +++ b/src/app/shared/menu/menu.actions.ts @@ -1,7 +1,7 @@ import { Action } from '@ngrx/store'; -import { MenuID } from './initial-menus-state'; import { type } from '../ngrx/type'; -import { MenuSection } from './menu.reducer'; +import { MenuSection } from './menu-section.model'; +import { MenuID } from './menu-id.model'; /** * For each action type in an action group, make a simple diff --git a/src/app/shared/menu/menu.component.spec.ts b/src/app/shared/menu/menu.component.spec.ts index 883969137b..8c171094a0 100644 --- a/src/app/shared/menu/menu.component.spec.ts +++ b/src/app/shared/menu/menu.component.spec.ts @@ -6,10 +6,10 @@ import { MenuService } from './menu.service'; import { MenuComponent } from './menu.component'; import { MenuServiceStub } from '../testing/menu-service.stub'; import { of as observableOf } from 'rxjs'; -import { MenuSection } from './menu.reducer'; import { Router } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; -import { MenuID } from './initial-menus-state'; +import { MenuSection } from './menu-section.model'; +import { MenuID } from './menu-id.model'; describe('MenuComponent', () => { let comp: MenuComponent; diff --git a/src/app/shared/menu/menu.component.ts b/src/app/shared/menu/menu.component.ts index 32fd938f4e..8c93e478c5 100644 --- a/src/app/shared/menu/menu.component.ts +++ b/src/app/shared/menu/menu.component.ts @@ -1,14 +1,14 @@ import { ChangeDetectionStrategy, Component, Injector, OnDestroy, OnInit } from '@angular/core'; import { BehaviorSubject, Observable, Subscription } from 'rxjs'; import { MenuService } from './menu.service'; -import { MenuID } from './initial-menus-state'; -import { MenuSection } from './menu.reducer'; import { distinctUntilChanged, map, switchMap } from 'rxjs/operators'; import { GenericConstructor } from '../../core/shared/generic-constructor'; import { hasValue } from '../empty.util'; import { MenuSectionComponent } from './menu-section/menu-section.component'; import { getComponentForMenu } from './menu-section.decorator'; import { compareArraysUsingIds } from '../../item-page/simple/item-types/shared/item-relationships-utils'; +import { MenuSection } from './menu-section.model'; +import { MenuID } from './menu-id.model'; /** * A basic implementation of a MenuComponent diff --git a/src/app/shared/menu/menu.effects.spec.ts b/src/app/shared/menu/menu.effects.spec.ts index e8a4c6a7ce..72319e06b8 100644 --- a/src/app/shared/menu/menu.effects.spec.ts +++ b/src/app/shared/menu/menu.effects.spec.ts @@ -1,5 +1,3 @@ -import { MenuID, MenuItemType } from './initial-menus-state'; -import { MenuSection } from './menu.reducer'; import { LinkMenuItemModel } from './menu-item/models/link.model'; import { TestBed } from '@angular/core/testing'; import { MenuService } from './menu.service'; @@ -9,6 +7,9 @@ import { provideMockActions } from '@ngrx/effects/testing'; import { cold, hot } from 'jasmine-marbles'; import { ROUTER_NAVIGATED } from '@ngrx/router-store'; import { MenuEffects } from './menu.effects'; +import { MenuSection } from './menu-section.model'; +import { MenuID } from './menu-id.model'; +import { MenuItemType } from './menu-item-type.model'; describe('MenuEffects', () => { let menuEffects: MenuEffects; diff --git a/src/app/shared/menu/menu.effects.ts b/src/app/shared/menu/menu.effects.ts index 47cff90209..2a9e536ae8 100644 --- a/src/app/shared/menu/menu.effects.ts +++ b/src/app/shared/menu/menu.effects.ts @@ -1,5 +1,4 @@ import { ActivatedRoute } from '@angular/router'; -import { MenuSection } from './menu.reducer'; import { hasNoValue, hasValue } from '../empty.util'; import { Actions, Effect, ofType } from '@ngrx/effects'; import { MenuService } from './menu.service'; @@ -8,7 +7,8 @@ import { Action } from '@ngrx/store'; import { ROUTER_NAVIGATED } from '@ngrx/router-store'; import { Injectable } from '@angular/core'; import { map, take, tap } from 'rxjs/operators'; -import { MenuID } from './initial-menus-state'; +import { MenuSection } from './menu-section.model'; +import { MenuID } from './menu-id.model'; /** * Effects modifying the state of menus diff --git a/src/app/shared/menu/menu.reducer.spec.ts b/src/app/shared/menu/menu.reducer.spec.ts index f6db3e67d1..7ae05536af 100644 --- a/src/app/shared/menu/menu.reducer.spec.ts +++ b/src/app/shared/menu/menu.reducer.spec.ts @@ -15,8 +15,10 @@ import { ToggleActiveMenuSectionAction, ToggleMenuAction } from './menu.actions'; -import { MenuSectionIndex, menusReducer } from './menu.reducer'; -import { initialMenusState, MenuID } from './initial-menus-state'; +import { menusReducer } from './menu.reducer'; +import { initialMenusState} from './initial-menus-state'; +import { MenuSectionIndex } from './menu-section-Index.model'; +import { MenuID } from './menu-id.model'; let visibleSection1; let dummyState; diff --git a/src/app/shared/menu/menu.reducer.ts b/src/app/shared/menu/menu.reducer.ts index b8a21d3507..c7ea017cc2 100644 --- a/src/app/shared/menu/menu.reducer.ts +++ b/src/app/shared/menu/menu.reducer.ts @@ -10,56 +10,14 @@ import { ShowMenuSectionAction, ToggleActiveMenuSectionAction } from './menu.actions'; -import { initialMenusState, MenuID } from './initial-menus-state'; +import { initialMenusState} from './initial-menus-state'; import { hasValue } from '../empty.util'; -import { MenuItemModel } from './menu-item/models/menu-item.model'; - -/** - * Represents the state of all menus in the store - */ -export type MenusState = { - [id in MenuID]: MenuState; -}; - -/** - * Represents the state of a single menu in the store - */ -export interface MenuState { - id: MenuID; - collapsed: boolean; - previewCollapsed: boolean; - visible: boolean; - sections: MenuSections; - sectionToSubsectionIndex: MenuSectionIndex; -} - -/** - * Represents the a mapping of all sections to their subsections for a menu in the store - */ -export interface MenuSectionIndex { - [id: string]: string[]; -} - -/** - * Represents the state of all menu sections in the store - */ -export interface MenuSections { - [id: string]: MenuSection; -} - -/** - * Represents the state of a single menu section in the store - */ -export class MenuSection { - id: string; - parentID?: string; - visible: boolean; - active: boolean; - model: MenuItemModel; - index?: number; - icon?: string; - shouldPersistOnRouteChange? = false; -} +import { MenusState } from './menus-state.model'; +import { MenuState } from './menu-state.model'; +import { MenuSectionIndex } from './menu-section-Index.model'; +import { MenuSections } from './menu-sections.model'; +import { MenuSection } from './menu-section.model'; +import { MenuID } from './menu-id.model'; /** * Reducer that handles MenuActions to update the MenusState diff --git a/src/app/shared/menu/menu.service.spec.ts b/src/app/shared/menu/menu.service.spec.ts index c38cabaa53..7a6b304ec1 100644 --- a/src/app/shared/menu/menu.service.spec.ts +++ b/src/app/shared/menu/menu.service.spec.ts @@ -6,7 +6,6 @@ import { provideMockStore } from '@ngrx/store/testing'; import { cold } from 'jasmine-marbles'; import { MenuService } from './menu.service'; -import { MenuID } from './initial-menus-state'; import { ActivateMenuSectionAction, AddMenuSectionAction, @@ -21,8 +20,10 @@ import { ToggleActiveMenuSectionAction, ToggleMenuAction } from './menu.actions'; -import { MenuSection, menusReducer } from './menu.reducer'; +import { menusReducer } from './menu.reducer'; import { storeModuleConfig } from '../../app.reducer'; +import { MenuSection } from './menu-section.model'; +import { MenuID } from './menu-id.model'; describe('MenuService', () => { let service: MenuService; diff --git a/src/app/shared/menu/menu.service.ts b/src/app/shared/menu/menu.service.ts index ac713f4194..ef8c0b1fad 100644 --- a/src/app/shared/menu/menu.service.ts +++ b/src/app/shared/menu/menu.service.ts @@ -1,8 +1,6 @@ import { Injectable } from '@angular/core'; import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store'; -import { MenuSection, MenuSections, MenuState } from './menu.reducer'; import { AppState, keySelector } from '../../app.reducer'; -import { MenuID } from './initial-menus-state'; import { combineLatest as observableCombineLatest, Observable } from 'rxjs'; import { map, switchMap } from 'rxjs/operators'; import { @@ -20,6 +18,10 @@ import { ToggleMenuAction, } from './menu.actions'; import { hasNoValue, hasValue, hasValueOperator, isNotEmpty } from '../empty.util'; +import { MenuState } from './menu-state.model'; +import { MenuSections } from './menu-sections.model'; +import { MenuSection } from './menu-section.model'; +import { MenuID } from './menu-id.model'; export function menuKeySelector(key: string, selector): MemoizedSelector { return createSelector(selector, (state) => { diff --git a/src/app/shared/menu/menus-state.model.ts b/src/app/shared/menu/menus-state.model.ts new file mode 100644 index 0000000000..9590fa1760 --- /dev/null +++ b/src/app/shared/menu/menus-state.model.ts @@ -0,0 +1,9 @@ +import { MenuState } from './menu-state.model'; +import { MenuID } from './menu-id.model'; + +/** + * Represents the state of all menus in the store + */ +export type MenusState = { + [id in MenuID]: MenuState; +}; diff --git a/src/app/shared/mocks/remote-data-build.service.mock.ts b/src/app/shared/mocks/remote-data-build.service.mock.ts index a33326758f..90071f8dc6 100644 --- a/src/app/shared/mocks/remote-data-build.service.mock.ts +++ b/src/app/shared/mocks/remote-data-build.service.mock.ts @@ -28,7 +28,8 @@ export function getMockRemoteDataBuildService(toRemoteDataObservable$?: Observab return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])); } }, - buildFromRequestUUID: (id: string) => createSuccessfulRemoteDataObject$({}) + buildFromRequestUUID: (id: string) => createSuccessfulRemoteDataObject$({}), + buildFromHref: (href: string) => createSuccessfulRemoteDataObject$({}) } as RemoteDataBuildService; } @@ -64,7 +65,8 @@ export function getMockRemoteDataBuildServiceHrefMap(toRemoteDataObservable$?: O }) ); }, - buildFromRequestUUID: (id: string) => createSuccessfulRemoteDataObject$({}) + buildFromRequestUUID: (id: string) => createSuccessfulRemoteDataObject$({}), + buildFromHref: (href: string) => createSuccessfulRemoteDataObject$({}) } as RemoteDataBuildService; } 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 48a0630778..4a0c5f1fb2 100644 --- a/src/app/shared/mydspace-actions/mydspace-actions-service.factory.ts +++ b/src/app/shared/mydspace-actions/mydspace-actions-service.factory.ts @@ -4,13 +4,13 @@ import { WorkspaceitemDataService } from '../../core/submission/workspaceitem-da import { ClaimedTaskDataService } from '../../core/tasks/claimed-task-data.service'; import { PoolTaskDataService } from '../../core/tasks/pool-task-data.service'; import { WorkflowItemDataService } from '../../core/submission/workflowitem-data.service'; -import { CacheableObject } from '../../core/cache/object-cache.reducer'; import { ItemDataService } from '../../core/data/item-data.service'; import { Item } from '../../core/shared/item.model'; import { PoolTask } from '../../core/tasks/models/pool-task-object.model'; import { ClaimedTask } from '../../core/tasks/models/claimed-task-object.model'; import { WorkspaceItem } from '../../core/submission/models/workspaceitem.model'; import { WorkflowItem } from '../../core/submission/models/workflowitem.model'; +import { CacheableObject } from '../../core/cache/cacheable-object.model'; /** * Class to return DataService for given ResourceType diff --git a/src/app/shared/object-collection/shared/listable-object.model.ts b/src/app/shared/object-collection/shared/listable-object.model.ts index 7ff6a8dc57..cb44cefb56 100644 --- a/src/app/shared/object-collection/shared/listable-object.model.ts +++ b/src/app/shared/object-collection/shared/listable-object.model.ts @@ -1,5 +1,5 @@ -import { EquatableObject } from '../../../core/utilities/equatable'; import { GenericConstructor } from '../../../core/shared/generic-constructor'; +import { EquatableObject } from '../../../core/utilities/equals.decorators'; export abstract class ListableObject extends EquatableObject { /** diff --git a/src/app/shared/pagination-drag-and-drop/abstract-paginated-drag-and-drop-list.component.spec.ts b/src/app/shared/pagination-drag-and-drop/abstract-paginated-drag-and-drop-list.component.spec.ts index 0c6cd80a62..7db53425d5 100644 --- a/src/app/shared/pagination-drag-and-drop/abstract-paginated-drag-and-drop-list.component.spec.ts +++ b/src/app/shared/pagination-drag-and-drop/abstract-paginated-drag-and-drop-list.component.spec.ts @@ -5,7 +5,6 @@ import { Component, ElementRef } from '@angular/core'; import { BehaviorSubject, Observable, of as observableOf } from 'rxjs'; import { PaginatedList } from '../../core/data/paginated-list.model'; import { RemoteData } from '../../core/data/remote-data'; -import { FieldUpdates } from '../../core/data/object-updates/object-updates.reducer'; import { take } from 'rxjs/operators'; import { PaginationComponent } from '../pagination/pagination.component'; import { createSuccessfulRemoteDataObject } from '../remote-data.utils'; @@ -15,6 +14,7 @@ import { PaginationService } from '../../core/pagination/pagination.service'; import { PaginationComponentOptions } from '../pagination/pagination-component-options.model'; import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; import { PaginationServiceStub } from '../testing/pagination-service.stub'; +import { FieldUpdates } from '../../core/data/object-updates/field-updates.model'; @Component({ selector: 'ds-mock-paginated-drag-drop-abstract', diff --git a/src/app/shared/pagination-drag-and-drop/abstract-paginated-drag-and-drop-list.component.ts b/src/app/shared/pagination-drag-and-drop/abstract-paginated-drag-and-drop-list.component.ts index 64a3bc5361..8dba47566f 100644 --- a/src/app/shared/pagination-drag-and-drop/abstract-paginated-drag-and-drop-list.component.ts +++ b/src/app/shared/pagination-drag-and-drop/abstract-paginated-drag-and-drop-list.component.ts @@ -1,4 +1,3 @@ -import { FieldUpdate, FieldUpdates } from '../../core/data/object-updates/object-updates.reducer'; import { BehaviorSubject, Observable, Subscription } from 'rxjs'; import { RemoteData } from '../../core/data/remote-data'; import { PaginatedList } from '../../core/data/paginated-list.model'; @@ -18,6 +17,8 @@ import { PaginationComponent } from '../pagination/pagination.component'; import { ObjectValuesPipe } from '../utils/object-values-pipe'; import { compareArraysUsing } from '../../item-page/simple/item-types/shared/item-relationships-utils'; import { PaginationService } from '../../core/pagination/pagination.service'; +import { FieldUpdate } from '../../core/data/object-updates/field-update.model'; +import { FieldUpdates } from '../../core/data/object-updates/field-updates.model'; /** * Operator used for comparing {@link FieldUpdate}s by their field's UUID diff --git a/src/app/shared/remote-data.utils.ts b/src/app/shared/remote-data.utils.ts index 14e3c04ece..2a7dee6383 100644 --- a/src/app/shared/remote-data.utils.ts +++ b/src/app/shared/remote-data.utils.ts @@ -1,7 +1,7 @@ import { RemoteData } from '../core/data/remote-data'; import { Observable, of as observableOf } from 'rxjs'; import { environment } from '../../environments/environment'; -import { RequestEntryState } from '../core/data/request.reducer'; +import { RequestEntryState } from '../core/data/request-entry-state.model'; /** * A fixed timestamp to use in tests diff --git a/src/app/shared/search/facet-config-response.model.ts b/src/app/shared/search/facet-config-response.model.ts index 74190d35ba..0d8cb21a8a 100644 --- a/src/app/shared/search/facet-config-response.model.ts +++ b/src/app/shared/search/facet-config-response.model.ts @@ -1,10 +1,10 @@ -import { CacheableObject } from '../../core/cache/object-cache.reducer'; import { typedObject } from '../../core/cache/builders/build-decorators'; import { FACET_CONFIG_RESPONSE } from './facet-config-response.resouce-type'; import { excludeFromEquals } from '../../core/utilities/equals.decorators'; import { SearchFilterConfig } from './search-filter-config.model'; import { deserialize } from 'cerialize'; import { HALLink } from '../../core/shared/hal-link.model'; +import { CacheableObject } from '../../core/cache/cacheable-object.model'; /** * The response from the discover/facets endpoint diff --git a/src/app/shared/search/search-filter-config.model.ts b/src/app/shared/search/search-filter-config.model.ts index 1a0be94d2b..3523042e1b 100644 --- a/src/app/shared/search/search-filter-config.model.ts +++ b/src/app/shared/search/search-filter-config.model.ts @@ -2,9 +2,9 @@ import { FilterType } from './filter-type.model'; import { autoserialize, autoserializeAs, deserialize } from 'cerialize'; import { HALLink } from '../../core/shared/hal-link.model'; import { typedObject } from '../../core/cache/builders/build-decorators'; -import { CacheableObject } from '../../core/cache/object-cache.reducer'; import { excludeFromEquals } from '../../core/utilities/equals.decorators'; import { SEARCH_FILTER_CONFIG } from './search-filter-config.resource-type'; +import { CacheableObject } from '../../core/cache/cacheable-object.model'; /** * The configuration for a search filter diff --git a/src/app/shared/search/search-filters/search-filters.component.spec.ts b/src/app/shared/search/search-filters/search-filters.component.spec.ts index 031c2e132b..3864aeffd7 100644 --- a/src/app/shared/search/search-filters/search-filters.component.spec.ts +++ b/src/app/shared/search/search-filters/search-filters.component.spec.ts @@ -18,8 +18,6 @@ describe('SearchFiltersComponent', () => { const searchServiceStub = { /* tslint:disable:no-empty */ - getConfig: () => - observableOf({ hasSucceeded: true, payload: [] }), getClearFiltersQueryParams: () => { }, getSearchLink: () => { diff --git a/src/app/shared/search/search-filters/search-filters.component.ts b/src/app/shared/search/search-filters/search-filters.component.ts index 0556da4426..d602f28f0c 100644 --- a/src/app/shared/search/search-filters/search-filters.component.ts +++ b/src/app/shared/search/search-filters/search-filters.component.ts @@ -83,7 +83,7 @@ export class SearchFiltersComponent implements OnInit, OnDestroy { initFilters() { this.filters = this.searchConfigService.searchOptions.pipe( - switchMap((options) => this.searchService.getConfig(options.scope, options.configuration).pipe(getFirstSucceededRemoteData())), + switchMap((options) => this.searchConfigService.getConfig(options.scope, options.configuration).pipe(getFirstSucceededRemoteData())), ); } diff --git a/src/app/shared/testing/menu-service.stub.ts b/src/app/shared/testing/menu-service.stub.ts index 4f2828a7e1..14eccfda67 100644 --- a/src/app/shared/testing/menu-service.stub.ts +++ b/src/app/shared/testing/menu-service.stub.ts @@ -1,6 +1,6 @@ -import { MenuID } from '../menu/initial-menus-state'; import { Observable, of as observableOf } from 'rxjs'; -import { MenuSection } from '../menu/menu.reducer'; +import { MenuSection } from '../menu/menu-section.model'; +import { MenuID } from '../menu/menu-id.model'; export class MenuServiceStub { visibleSection1 = { diff --git a/src/app/shared/testing/search-configuration-service.stub.ts b/src/app/shared/testing/search-configuration-service.stub.ts index 4b8f1d6f12..80744ba59a 100644 --- a/src/app/shared/testing/search-configuration-service.stub.ts +++ b/src/app/shared/testing/search-configuration-service.stub.ts @@ -1,4 +1,6 @@ import { BehaviorSubject, of as observableOf } from 'rxjs'; +import { SearchConfig } from '../../core/shared/search/search-filters/search-config.model'; +import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; export class SearchConfigurationServiceStub { @@ -18,4 +20,24 @@ export class SearchConfigurationServiceStub { getCurrentConfiguration(a) { return observableOf(a); } + + getConfig () { + return observableOf({ hasSucceeded: true, payload: [] }); + } + + getAvailableConfigurationOptions() { + return observableOf([{value: 'test', label: 'test'}]); + } + + getConfigurationSearchConfigObservable() { + return observableOf(new SearchConfig()); + } + + getConfigurationSortOptionsObservable() { + return observableOf([new SortOptions('score', SortDirection.ASC), new SortOptions('score', SortDirection.DESC)]); + } + + initializeSortOptionsFromConfiguration() { + /* empty */ + } } diff --git a/src/app/shared/testing/utils.test.ts b/src/app/shared/testing/utils.test.ts index 342c987bc2..1e62ee8f00 100644 --- a/src/app/shared/testing/utils.test.ts +++ b/src/app/shared/testing/utils.test.ts @@ -2,9 +2,10 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { buildPaginatedList, PaginatedList } from '../../core/data/paginated-list.model'; import { PageInfo } from '../../core/shared/page-info.model'; import { Observable } from 'rxjs/internal/Observable'; -import { RequestEntry, RequestEntryState } from '../../core/data/request.reducer'; +import { RequestEntry} from '../../core/data/request.reducer'; import { of as observableOf } from 'rxjs/internal/observable/of'; import { UnCacheableObject } from '../../core/shared/uncacheable-object.model'; +import { RequestEntryState } from '../../core/data/request-entry-state.model'; /** * Returns true if a Native Element has a specified css class. diff --git a/src/app/statistics-page/statistics-page/statistics-page.component.ts b/src/app/statistics-page/statistics-page/statistics-page.component.ts index 099d299015..7aceb5c9b7 100644 --- a/src/app/statistics-page/statistics-page/statistics-page.component.ts +++ b/src/app/statistics-page/statistics-page/statistics-page.component.ts @@ -6,13 +6,13 @@ import { UsageReport } from '../../core/statistics/models/usage-report.model'; import { RemoteData } from '../../core/data/remote-data'; import { getRemoteDataPayload, - getFirstSucceededRemoteData, - redirectOn4xx + getFirstSucceededRemoteData } from '../../core/shared/operators'; import { DSpaceObject } from '../../core/shared/dspace-object.model'; import { ActivatedRoute, Router } from '@angular/router'; import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; import { AuthService } from '../../core/auth/auth.service'; +import { redirectOn4xx } from '../../core/shared/authorized.operators'; /** * Class representing an abstract statistics page component. diff --git a/src/app/statistics/statistics-endpoint.model.ts b/src/app/statistics/statistics-endpoint.model.ts index 1dafa215b8..0a3671432e 100644 --- a/src/app/statistics/statistics-endpoint.model.ts +++ b/src/app/statistics/statistics-endpoint.model.ts @@ -1,10 +1,10 @@ import { HALLink } from '../core/shared/hal-link.model'; import { typedObject } from '../core/cache/builders/build-decorators'; -import { CacheableObject } from '../core/cache/object-cache.reducer'; import { excludeFromEquals } from '../core/utilities/equals.decorators'; import { autoserialize, deserialize } from 'cerialize'; import { ResourceType } from '../core/shared/resource-type'; import { STATISTICS_ENDPOINT } from './statistics-endpoint.resource-type'; +import { CacheableObject } from '../core/cache/cacheable-object.model'; /** * Model class for the statistics endpoint diff --git a/src/app/submission/edit/submission-edit.component.ts b/src/app/submission/edit/submission-edit.component.ts index c415b89b81..ecb39043f2 100644 --- a/src/app/submission/edit/submission-edit.component.ts +++ b/src/app/submission/edit/submission-edit.component.ts @@ -18,8 +18,8 @@ import { getAllSucceededRemoteData } from '../../core/shared/operators'; import { ItemDataService } from '../../core/data/item-data.service'; import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject'; import { SubmissionJsonPatchOperationsService } from '../../core/submission/submission-json-patch-operations.service'; -import { SubmissionError } from '../objects/submission-objects.reducer'; import parseSectionErrors from '../utils/parseSectionErrors'; +import { SubmissionError } from '../objects/submission-error.model'; /** * This component allows to edit an existing workspaceitem/workflowitem. diff --git a/src/app/submission/form/submission-form.component.ts b/src/app/submission/form/submission-form.component.ts index 4cbffbca78..f204800164 100644 --- a/src/app/submission/form/submission-form.component.ts +++ b/src/app/submission/form/submission-form.component.ts @@ -11,12 +11,13 @@ import { WorkspaceitemSectionsObject } from '../../core/submission/models/worksp import { hasValue, isNotEmpty } from '../../shared/empty.util'; import { UploaderOptions } from '../../shared/uploader/uploader-options.model'; -import { SubmissionError, SubmissionObjectEntry } from '../objects/submission-objects.reducer'; +import { SubmissionObjectEntry } from '../objects/submission-objects.reducer'; import { SectionDataObject } from '../sections/models/section-data.model'; import { SubmissionService } from '../submission.service'; import { Item } from '../../core/shared/item.model'; import { SectionsType } from '../sections/sections-type'; import { SectionsService } from '../sections/sections.service'; +import { SubmissionError } from '../objects/submission-error.model'; /** * This component represents the submission form. diff --git a/src/app/submission/objects/section-visibility.model.ts b/src/app/submission/objects/section-visibility.model.ts new file mode 100644 index 0000000000..c41735178c --- /dev/null +++ b/src/app/submission/objects/section-visibility.model.ts @@ -0,0 +1,7 @@ +/** + * An interface to represent section visibility + */ +export interface SectionVisibility { + main: any; + other: any; +} diff --git a/src/app/submission/objects/submission-error.model.ts b/src/app/submission/objects/submission-error.model.ts new file mode 100644 index 0000000000..78a3143b7d --- /dev/null +++ b/src/app/submission/objects/submission-error.model.ts @@ -0,0 +1,8 @@ +import { SubmissionSectionError } from './submission-section-error.model'; + +/** + * An interface to represent section error + */ +export interface SubmissionError { + [submissionId: string]: SubmissionSectionError[]; +} diff --git a/src/app/submission/objects/submission-objects.actions.ts b/src/app/submission/objects/submission-objects.actions.ts index 4935acb792..1f36b22592 100644 --- a/src/app/submission/objects/submission-objects.actions.ts +++ b/src/app/submission/objects/submission-objects.actions.ts @@ -1,7 +1,6 @@ import { Action } from '@ngrx/store'; import { type } from '../../shared/ngrx/type'; -import { SectionVisibility, SubmissionError, SubmissionSectionError } from './submission-objects.reducer'; import { WorkspaceitemSectionUploadFileObject } from '../../core/submission/models/workspaceitem-section-upload-file.model'; import { WorkspaceitemSectionDataType, @@ -11,6 +10,9 @@ import { SubmissionObject } from '../../core/submission/models/submission-object import { SubmissionDefinitionsModel } from '../../core/config/models/config-submission-definitions.model'; import { SectionsType } from '../sections/sections-type'; import { Item } from '../../core/shared/item.model'; +import { SectionVisibility } from './section-visibility.model'; +import { SubmissionError } from './submission-error.model'; +import { SubmissionSectionError } from './submission-section-error.model'; /** * For each action type in an action group, make a simple diff --git a/src/app/submission/objects/submission-objects.effects.ts b/src/app/submission/objects/submission-objects.effects.ts index b4ba1c2480..12f79b5be9 100644 --- a/src/app/submission/objects/submission-objects.effects.ts +++ b/src/app/submission/objects/submission-objects.effects.ts @@ -43,7 +43,7 @@ import { UpdateSectionDataAction, UpdateSectionDataSuccessAction } from './submission-objects.actions'; -import { SubmissionObjectEntry, SubmissionSectionError, SubmissionSectionObject } from './submission-objects.reducer'; +import { SubmissionObjectEntry} from './submission-objects.reducer'; import { Item } from '../../core/shared/item.model'; import { RemoteData } from '../../core/data/remote-data'; import { getFirstSucceededRemoteDataPayload } from '../../core/shared/operators'; @@ -51,6 +51,8 @@ import { SubmissionObjectDataService } from '../../core/submission/submission-ob import { followLink } from '../../shared/utils/follow-link-config.model'; import parseSectionErrorPaths, { SectionErrorPath } from '../utils/parseSectionErrorPaths'; import { FormState } from '../../shared/form/form.reducer'; +import { SubmissionSectionObject } from './submission-section-object.model'; +import { SubmissionSectionError } from './submission-section-error.model'; @Injectable() export class SubmissionObjectEffects { diff --git a/src/app/submission/objects/submission-objects.reducer.ts b/src/app/submission/objects/submission-objects.reducer.ts index 4159c56ae1..564e5a701b 100644 --- a/src/app/submission/objects/submission-objects.reducer.ts +++ b/src/app/submission/objects/submission-objects.reducer.ts @@ -35,114 +35,8 @@ import { SubmissionObjectActionTypes, UpdateSectionDataAction } from './submission-objects.actions'; -import { WorkspaceitemSectionDataType } from '../../core/submission/models/workspaceitem-sections.model'; import { WorkspaceitemSectionUploadObject } from '../../core/submission/models/workspaceitem-section-upload.model'; -import { SectionsType } from '../sections/sections-type'; - -/** - * An interface to represent section visibility - */ -export interface SectionVisibility { - main: any; - other: any; -} - -/** - * An interface to represent section object state - */ -export interface SubmissionSectionObject { - /** - * The section header - */ - header: string; - - /** - * The section configuration url - */ - config: string; - - /** - * A boolean representing if this section is mandatory - */ - mandatory: boolean; - - /** - * The section type - */ - sectionType: SectionsType; - - /** - * The section visibility - */ - visibility: SectionVisibility; - - /** - * A boolean representing if this section is collapsed - */ - collapsed: boolean; - - /** - * A boolean representing if this section is enabled - */ - enabled: boolean; - - /** - * The list of the metadata ids of the section. - */ - metadata: string[]; - - /** - * The section data object - */ - data: WorkspaceitemSectionDataType; - - /** - * The list of the section's errors to show. It contains the error list to display when section is not pristine - */ - errorsToShow: SubmissionSectionError[]; - - /** - * The list of the section's errors detected by the server. They may not be shown yet if section is pristine - */ - serverValidationErrors: SubmissionSectionError[]; - - /** - * A boolean representing if this section is loading - */ - isLoading: boolean; - - /** - * A boolean representing if this section is valid - */ - isValid: boolean; - - /** - * The formId related to this section - */ - formId: string; -} - -/** - * An interface to represent section error - */ -export interface SubmissionError { - [submissionId: string]: SubmissionSectionError[]; -} - -/** - * An interface to represent section error - */ -export interface SubmissionSectionError { - /** - * A string representing error path - */ - path: string; - - /** - * The error message - */ - message: string; -} +import { SubmissionSectionObject } from './submission-section-object.model'; /** * An interface to represent SubmissionSectionObject entry diff --git a/src/app/submission/objects/submission-section-error.model.ts b/src/app/submission/objects/submission-section-error.model.ts new file mode 100644 index 0000000000..cf0d232a5a --- /dev/null +++ b/src/app/submission/objects/submission-section-error.model.ts @@ -0,0 +1,14 @@ +/** + * An interface to represent section error + */ +export interface SubmissionSectionError { + /** + * A string representing error path + */ + path: string; + + /** + * The error message + */ + message: string; +} diff --git a/src/app/submission/objects/submission-section-object.model.ts b/src/app/submission/objects/submission-section-object.model.ts new file mode 100644 index 0000000000..16e437da80 --- /dev/null +++ b/src/app/submission/objects/submission-section-object.model.ts @@ -0,0 +1,79 @@ +import { SectionsType } from '../sections/sections-type'; +import { SectionVisibility } from './section-visibility.model'; +import { WorkspaceitemSectionDataType } from '../../core/submission/models/workspaceitem-sections.model'; +import { SubmissionSectionError } from './submission-section-error.model'; + +/** + * An interface to represent section object state + */ +export interface SubmissionSectionObject { + /** + * The section header + */ + header: string; + + /** + * The section configuration url + */ + config: string; + + /** + * A boolean representing if this section is mandatory + */ + mandatory: boolean; + + /** + * The section type + */ + sectionType: SectionsType; + + /** + * The section visibility + */ + visibility: SectionVisibility; + + /** + * A boolean representing if this section is collapsed + */ + collapsed: boolean; + + /** + * A boolean representing if this section is enabled + */ + enabled: boolean; + + /** + * The list of the metadata ids of the section. + */ + metadata: string[]; + + /** + * The section data object + */ + data: WorkspaceitemSectionDataType; + + /** + * The list of the section's errors to show. It contains the error list to display when section is not pristine + */ + errorsToShow: SubmissionSectionError[]; + + /** + * The list of the section's errors detected by the server. They may not be shown yet if section is pristine + */ + serverValidationErrors: SubmissionSectionError[]; + + /** + * A boolean representing if this section is loading + */ + isLoading: boolean; + + /** + * A boolean representing if this section is valid + */ + isValid: boolean; + + /** + * The formId related to this section + */ + formId: string; +} diff --git a/src/app/submission/sections/form/section-form.component.spec.ts b/src/app/submission/sections/form/section-form.component.spec.ts index 90861bce5e..35a64046f2 100644 --- a/src/app/submission/sections/form/section-form.component.spec.ts +++ b/src/app/submission/sections/form/section-form.component.spec.ts @@ -35,7 +35,6 @@ import { FormFieldModel } from '../../../shared/form/builder/models/form-field.m import { FormFieldMetadataValueObject } from '../../../shared/form/builder/models/form-field-metadata-value.model'; import { DynamicRowGroupModel } from '../../../shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-row-group-model'; import { DsDynamicInputModel } from '../../../shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-input.model'; -import { SubmissionSectionError } from '../../objects/submission-objects.reducer'; import { DynamicFormControlEvent, DynamicFormControlEventType } from '@ng-dynamic-forms/core'; import { JsonPatchOperationPathCombiner } from '../../../core/json-patch/builder/json-patch-operation-path-combiner'; import { FormRowModel } from '../../../core/config/models/config-submission-form.model'; @@ -45,6 +44,7 @@ import { ObjectCacheService } from '../../../core/cache/object-cache.service'; import { RequestService } from '../../../core/data/request.service'; import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; import { cold } from 'jasmine-marbles'; +import { SubmissionSectionError } from '../../objects/submission-section-error.model'; function getMockSubmissionFormsConfigService(): SubmissionFormsConfigService { return jasmine.createSpyObj('FormOperationsService', { diff --git a/src/app/submission/sections/form/section-form.component.ts b/src/app/submission/sections/form/section-form.component.ts index 8ae246dcca..1850735cb7 100644 --- a/src/app/submission/sections/form/section-form.component.ts +++ b/src/app/submission/sections/form/section-form.component.ts @@ -14,7 +14,6 @@ import { SubmissionFormsConfigService } from '../../../core/config/submission-fo import { hasValue, isEmpty, isNotEmpty, isUndefined } from '../../../shared/empty.util'; import { JsonPatchOperationPathCombiner } from '../../../core/json-patch/builder/json-patch-operation-path-combiner'; import { SubmissionFormsModel } from '../../../core/config/models/config-submission-forms.model'; -import { SubmissionSectionError, SubmissionSectionObject } from '../../objects/submission-objects.reducer'; import { FormFieldPreviousValueObject } from '../../../shared/form/builder/models/form-field-previous-value-object'; import { SectionDataObject } from '../models/section-data.model'; import { renderSectionFor } from '../sections-decorator'; @@ -34,6 +33,8 @@ import { followLink } from '../../../shared/utils/follow-link-config.model'; import { environment } from '../../../../environments/environment'; import { ConfigObject } from '../../../core/config/models/config.model'; import { RemoteData } from '../../../core/data/remote-data'; +import { SubmissionSectionObject } from '../../objects/submission-section-object.model'; +import { SubmissionSectionError } from '../../objects/submission-section-error.model'; /** * This component represents a section that contains a Form. diff --git a/src/app/submission/sections/models/section-data.model.ts b/src/app/submission/sections/models/section-data.model.ts index 9078b5dfb9..6f8126dffc 100644 --- a/src/app/submission/sections/models/section-data.model.ts +++ b/src/app/submission/sections/models/section-data.model.ts @@ -1,6 +1,6 @@ -import { SubmissionSectionError } from '../../objects/submission-objects.reducer'; import { WorkspaceitemSectionDataType } from '../../../core/submission/models/workspaceitem-sections.model'; import { SectionsType } from '../sections-type'; +import { SubmissionSectionError } from '../../objects/submission-section-error.model'; /** * An interface to represent section model diff --git a/src/app/submission/sections/sections.directive.ts b/src/app/submission/sections/sections.directive.ts index a42cd4be78..3ffb317b15 100644 --- a/src/app/submission/sections/sections.directive.ts +++ b/src/app/submission/sections/sections.directive.ts @@ -6,10 +6,10 @@ import { uniq } from 'lodash'; import { SectionsService } from './sections.service'; import { hasValue, isNotEmpty, isNotNull } from '../../shared/empty.util'; -import { SubmissionSectionError } from '../objects/submission-objects.reducer'; import parseSectionErrorPaths, { SectionErrorPath } from '../utils/parseSectionErrorPaths'; import { SubmissionService } from '../submission.service'; import { SectionsType } from './sections-type'; +import { SubmissionSectionError } from '../objects/submission-section-error.model'; /** * Directive for handling generic section functionality diff --git a/src/app/submission/sections/sections.service.spec.ts b/src/app/submission/sections/sections.service.spec.ts index 9fbcedcc4d..5aa47d1447 100644 --- a/src/app/submission/sections/sections.service.spec.ts +++ b/src/app/submission/sections/sections.service.spec.ts @@ -31,12 +31,12 @@ import { import { FormClearErrorsAction } from '../../shared/form/form.actions'; import parseSectionErrors from '../utils/parseSectionErrors'; import { SubmissionScopeType } from '../../core/submission/submission-scope-type'; -import { SubmissionSectionError } from '../objects/submission-objects.reducer'; import { getMockScrollToService } from '../../shared/mocks/scroll-to-service.mock'; import { storeModuleConfig } from '../../app.reducer'; import { SectionsType } from './sections-type'; import { FormService } from '../../shared/form/form.service'; import { getMockFormService } from '../../shared/mocks/form-service.mock'; +import { SubmissionSectionError } from '../objects/submission-section-error.model'; describe('SectionsService test suite', () => { let notificationsServiceStub: NotificationsServiceStub; diff --git a/src/app/submission/sections/sections.service.ts b/src/app/submission/sections/sections.service.ts index dd68d42a87..64f9e2efcd 100644 --- a/src/app/submission/sections/sections.service.ts +++ b/src/app/submission/sections/sections.service.ts @@ -19,9 +19,7 @@ import { UpdateSectionDataAction } from '../objects/submission-objects.actions'; import { - SubmissionObjectEntry, - SubmissionSectionError, - SubmissionSectionObject + SubmissionObjectEntry } from '../objects/submission-objects.reducer'; import { submissionObjectFromIdSelector, @@ -43,6 +41,8 @@ import { parseReviver } from '@ng-dynamic-forms/core'; import { FormService } from '../../shared/form/form.service'; import { JsonPatchOperationPathCombiner } from '../../core/json-patch/builder/json-patch-operation-path-combiner'; import { FormError } from '../../shared/form/form.reducer'; +import { SubmissionSectionObject } from '../objects/submission-section-object.model'; +import { SubmissionSectionError } from '../objects/submission-section-error.model'; /** * A service that provides methods used in submission process. diff --git a/src/app/submission/selectors.ts b/src/app/submission/selectors.ts index df33732381..2c6c03a4ff 100644 --- a/src/app/submission/selectors.ts +++ b/src/app/submission/selectors.ts @@ -2,7 +2,8 @@ import { createSelector, MemoizedSelector, Selector } from '@ngrx/store'; import { hasValue } from '../shared/empty.util'; import { submissionSelector, SubmissionState } from './submission.reducers'; -import { SubmissionObjectEntry, SubmissionSectionObject } from './objects/submission-objects.reducer'; +import { SubmissionObjectEntry} from './objects/submission-objects.reducer'; +import { SubmissionSectionObject } from './objects/submission-section-object.model'; /** * Export a function to return a subset of the state by key diff --git a/src/app/submission/submission.service.ts b/src/app/submission/submission.service.ts index 4b3df65de1..9eb8cf110a 100644 --- a/src/app/submission/submission.service.ts +++ b/src/app/submission/submission.service.ts @@ -22,10 +22,8 @@ import { SetActiveSectionAction } from './objects/submission-objects.actions'; import { - SubmissionError, SubmissionObjectEntry, - SubmissionSectionEntry, - SubmissionSectionObject + SubmissionSectionEntry } from './objects/submission-objects.reducer'; import { submissionObjectFromIdSelector } from './selectors'; import { HttpOptions } from '../core/dspace-rest/dspace-rest.service'; @@ -46,6 +44,8 @@ import { SearchService } from '../core/shared/search/search.service'; import { Item } from '../core/shared/item.model'; import { environment } from '../../environments/environment'; import { SubmissionJsonPatchOperationsService } from '../core/submission/submission-json-patch-operations.service'; +import { SubmissionSectionObject } from './objects/submission-section-object.model'; +import { SubmissionError } from './objects/submission-error.model'; /** * A service that provides methods used in submission process.