mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
Merge pull request #531 from atmire/clean-relationships-in-submission
Create relationships during the submission
This commit is contained in:
@@ -15,7 +15,11 @@ module.exports = function (config) {
|
||||
};
|
||||
|
||||
var configuration = {
|
||||
|
||||
client: {
|
||||
jasmine: {
|
||||
random: false
|
||||
}
|
||||
},
|
||||
// base path that will be used to resolve all patterns (e.g. files, exclude)
|
||||
basePath: '',
|
||||
|
||||
|
@@ -140,6 +140,7 @@
|
||||
"text-mask-core": "5.0.1",
|
||||
"ts-loader": "^5.2.1",
|
||||
"ts-md5": "^1.2.4",
|
||||
"url-parse": "^1.4.7",
|
||||
"uuid": "^3.2.1",
|
||||
"webfontloader": "1.6.28",
|
||||
"webpack-cli": "^3.1.0",
|
||||
|
@@ -459,6 +459,9 @@
|
||||
"footer.link.duraspace": "DuraSpace",
|
||||
|
||||
|
||||
"form.add": "Add",
|
||||
|
||||
"form.add-help": "Click here to add the current entry and to add another one",
|
||||
|
||||
"form.cancel": "Cancel",
|
||||
|
||||
@@ -484,6 +487,10 @@
|
||||
|
||||
"form.loading": "Loading...",
|
||||
|
||||
"form.lookup": "Lookup",
|
||||
|
||||
"form.lookup-help": "Click here to look up an existing relation",
|
||||
|
||||
"form.no-results": "No results found",
|
||||
|
||||
"form.no-value": "No value entered",
|
||||
@@ -724,7 +731,7 @@
|
||||
|
||||
"item.edit.tabs.relationships.head": "Item Relationships",
|
||||
|
||||
"item.edit.tabs.relationships.title": "Item Edit - Relationships",
|
||||
"item.edit.tabs.relationships.title": "Item Edit - Relationships",
|
||||
|
||||
"item.edit.tabs.status.buttons.authorizations.button": "Authorizations...",
|
||||
|
||||
@@ -1361,6 +1368,9 @@
|
||||
"search.filters.applied.f.subject": "Subject",
|
||||
|
||||
"search.filters.applied.f.submitter": "Submitter",
|
||||
"search.filters.applied.f.jobTitle": "Job Title",
|
||||
"search.filters.applied.f.birthDate.max": "End birth date",
|
||||
"search.filters.applied.f.birthDate.min": "Start birth date",
|
||||
|
||||
|
||||
|
||||
@@ -1529,15 +1539,69 @@
|
||||
"submission.general.save-later": "Save for later",
|
||||
|
||||
|
||||
"submission.sections.describe.relationship-lookup.close": "Close",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.search-tab.deselect-all": "Deselect all",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.search-tab.deselect-page": "Deselect page",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.search-tab.loading": "Loading...",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.search-tab.placeholder": "Search query",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.search-tab.search": "Go",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.search-tab.select-all": "Select all",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.search-tab.select-page": "Select page",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.selected": "Selected {{ size }} items",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.search-tab.tab-title.Author": "Search for Authors",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.search-tab.tab-title.Journal": "Search for Journals",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.search-tab.tab-title.Journal Issue": "Search for Journal Issues",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.search-tab.tab-title.Journal Volume": "Search for Journal Volumes",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.search-tab.tab-title.Funding Agency": "Search for Funding Agencies",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.search-tab.tab-title.Funding": "Search for Funding",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.title.Funding Agency": "Funding Agency",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.title.Funding": "Funding",
|
||||
|
||||
|
||||
"submission.sections.describe.relationship-lookup.selection-tab.tab-title": "Current Selection ({{ count }})",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.title.Journal Issue": "Journal Issues",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.title.Journal Volume": "Journal Volumes",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.title.Journal": "Journals",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.title.Author": "Authors",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.title.Funding Agency": "Funding Agency",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.title.Funding": "Funding",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.search-tab.toggle-dropdown": "Toggle dropdown",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.selection-tab.settings": "Settings",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.selection-tab.no-selection": "Your selection is currently empty.",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.selection-tab.title.Author": "Selected Authors",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.selection-tab.title.Journal": "Selected Journals",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.selection-tab.title.Journal Volume": "Selected Journal Volume",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.selection-tab.title.Journal Issue": "Selected Issue",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.name-variant.notification.content": "Would you like to save \"{{ value }}\" as a name variant for this person so you and others can reuse it for future submissions? If you don\'t you can still use it for this submission.",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.name-variant.notification.confirm": "Save a new name variant",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.name-variant.notification.decline": "Use only for this submission",
|
||||
|
||||
"submission.sections.general.add-more": "Add more",
|
||||
|
||||
|
@@ -13,7 +13,6 @@ import { combineLatest as combineLatestObservable } from 'rxjs';
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { OnClickMenuItemModel } from '../../shared/menu/menu-item/models/onclick.model';
|
||||
import { CreateCommunityParentSelectorComponent } from '../../shared/dso-selector/modal-wrappers/create-community-parent-selector/create-community-parent-selector.component';
|
||||
import { CreateItemParentSelectorComponent } from '../../shared/dso-selector/modal-wrappers/create-item-parent-selector/create-item-parent-selector.component';
|
||||
import { CreateCollectionParentSelectorComponent } from '../../shared/dso-selector/modal-wrappers/create-collection-parent-selector/create-collection-parent-selector.component';
|
||||
import { EditItemSelectorComponent } from '../../shared/dso-selector/modal-wrappers/edit-item-selector/edit-item-selector.component';
|
||||
import { EditCommunitySelectorComponent } from '../../shared/dso-selector/modal-wrappers/edit-community-selector/edit-community-selector.component';
|
||||
|
@@ -1,29 +1,23 @@
|
||||
import { CollectionItemMapperComponent } from './collection-item-mapper.component';
|
||||
import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
||||
import { SearchFormComponent } from '../../shared/search-form/search-form.component';
|
||||
import { SearchPageModule } from '../../+search-page/search-page.module';
|
||||
import { ObjectCollectionComponent } from '../../shared/object-collection/object-collection.component';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { ActivatedRouteStub } from '../../shared/testing/active-router-stub';
|
||||
import { RouterStub } from '../../shared/testing/router-stub';
|
||||
import { SearchConfigurationService } from '../../+search-page/search-service/search-configuration.service';
|
||||
import { SearchService } from '../../+search-page/search-service/search.service';
|
||||
import { SearchServiceStub } from '../../shared/testing/search-service-stub';
|
||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||
import { NotificationsServiceStub } from '../../shared/testing/notifications-service-stub';
|
||||
import { ItemDataService } from '../../core/data/item-data.service';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { SharedModule } from '../../shared/shared.module';
|
||||
import { Collection } from '../../core/shared/collection.model';
|
||||
import { RemoteData } from '../../core/data/remote-data';
|
||||
import { PaginatedSearchOptions } from '../../+search-page/paginated-search-options.model';
|
||||
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
||||
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
|
||||
import { EventEmitter, NgModule } from '@angular/core';
|
||||
import { 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';
|
||||
@@ -36,13 +30,14 @@ import { ItemSelectComponent } from '../../shared/object-select/item-select/item
|
||||
import { ObjectSelectService } from '../../shared/object-select/object-select.service';
|
||||
import { ObjectSelectServiceStub } from '../../shared/testing/object-select-service-stub';
|
||||
import { VarDirective } from '../../shared/utils/var.directive';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { of as observableOf, of } from 'rxjs/internal/observable/of';
|
||||
import { RestResponse } from '../../core/cache/response.models';
|
||||
import { SearchFixedFilterService } from '../../+search-page/search-filters/search-filter/search-fixed-filter.service';
|
||||
import { RouteService } from '../../core/services/route.service';
|
||||
import { ErrorComponent } from '../../shared/error/error.component';
|
||||
import { LoadingComponent } from '../../shared/loading/loading.component';
|
||||
import { SearchConfigurationService } from '../../core/shared/search/search-configuration.service';
|
||||
import { SearchService } from '../../core/shared/search/search.service';
|
||||
import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model';
|
||||
|
||||
describe('CollectionItemMapperComponent', () => {
|
||||
let comp: CollectionItemMapperComponent;
|
||||
@@ -135,7 +130,6 @@ describe('CollectionItemMapperComponent', () => {
|
||||
{ provide: HostWindowService, useValue: new HostWindowServiceStub(0) },
|
||||
{ provide: ObjectSelectService, useValue: new ObjectSelectServiceStub() },
|
||||
{ provide: RouteService, useValue: routeServiceStub },
|
||||
{ provide: SearchFixedFilterService, useValue: fixedFilterServiceStub }
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
@@ -5,12 +5,9 @@ import { fadeIn, fadeInOut } from '../../shared/animations/fade';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { RemoteData } from '../../core/data/remote-data';
|
||||
import { Collection } from '../../core/shared/collection.model';
|
||||
import { SearchConfigurationService } from '../../+search-page/search-service/search-configuration.service';
|
||||
import { PaginatedSearchOptions } from '../../+search-page/paginated-search-options.model';
|
||||
import { PaginatedList } from '../../core/data/paginated-list';
|
||||
import { map, startWith, switchMap, take, tap } from 'rxjs/operators';
|
||||
import { map, startWith, switchMap, take } from 'rxjs/operators';
|
||||
import { getRemoteDataPayload, getSucceededRemoteData, toDSpaceObjectListRD } from '../../core/shared/operators';
|
||||
import { SearchService } from '../../+search-page/search-service/search.service';
|
||||
import { DSpaceObject } from '../../core/shared/dspace-object.model';
|
||||
import { DSpaceObjectType } from '../../core/shared/dspace-object-type.model';
|
||||
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
|
||||
@@ -22,6 +19,9 @@ import { isNotEmpty } from '../../shared/empty.util';
|
||||
import { RestResponse } from '../../core/cache/response.models';
|
||||
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
|
||||
import { SEARCH_CONFIG_SERVICE } from '../../+my-dspace-page/my-dspace-page.component';
|
||||
import { SearchConfigurationService } from '../../core/shared/search/search-configuration.service';
|
||||
import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model';
|
||||
import { SearchService } from '../../core/shared/search/search.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-collection-item-mapper',
|
||||
|
@@ -2,8 +2,8 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { BehaviorSubject, of as observableOf, Observable, Subject } from 'rxjs';
|
||||
import { filter, flatMap, map, startWith, switchMap, take, tap } from 'rxjs/operators';
|
||||
import { PaginatedSearchOptions } from '../+search-page/paginated-search-options.model';
|
||||
import { SearchService } from '../+search-page/search-service/search.service';
|
||||
import { PaginatedSearchOptions } from '../shared/search/paginated-search-options.model';
|
||||
import { SearchService } from '../core/shared/search/search.service';
|
||||
import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model';
|
||||
import { CollectionDataService } from '../core/data/collection-data.service';
|
||||
import { PaginatedList } from '../core/data/paginated-list';
|
||||
|
@@ -9,9 +9,8 @@ import { CreateCollectionPageComponent } from './create-collection-page/create-c
|
||||
import { CollectionFormComponent } from './collection-form/collection-form.component';
|
||||
import { EditCollectionPageComponent } from './edit-collection-page/edit-collection-page.component';
|
||||
import { DeleteCollectionPageComponent } from './delete-collection-page/delete-collection-page.component';
|
||||
import { SearchService } from '../+search-page/search-service/search.service';
|
||||
import { CollectionItemMapperComponent } from './collection-item-mapper/collection-item-mapper.component';
|
||||
import { SearchFixedFilterService } from '../+search-page/search-filters/search-filter/search-fixed-filter.service';
|
||||
import { SearchService } from '../core/shared/search/search.service';
|
||||
import { StatisticsModule } from '../statistics/statistics.module';
|
||||
|
||||
@NgModule({
|
||||
@@ -31,7 +30,6 @@ import { StatisticsModule } from '../statistics/statistics.module';
|
||||
],
|
||||
providers: [
|
||||
SearchService,
|
||||
SearchFixedFilterService
|
||||
]
|
||||
})
|
||||
export class CollectionPageModule {
|
||||
|
@@ -1,15 +1,12 @@
|
||||
import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ItemCollectionMapperComponent } from './item-collection-mapper.component';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { SearchConfigurationService } from '../../../+search-page/search-service/search-configuration.service';
|
||||
import { SearchService } from '../../../+search-page/search-service/search.service';
|
||||
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||
import { ItemDataService } from '../../../core/data/item-data.service';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { PaginatedSearchOptions } from '../../../+search-page/paginated-search-options.model';
|
||||
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
||||
import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model';
|
||||
import { RouterStub } from '../../../shared/testing/router-stub';
|
||||
@@ -19,7 +16,6 @@ import { SearchServiceStub } from '../../../shared/testing/search-service-stub';
|
||||
import { PaginatedList } from '../../../core/data/paginated-list';
|
||||
import { PageInfo } from '../../../core/shared/page-info.model';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { SharedModule } from '../../../shared/shared.module';
|
||||
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
||||
import { NotificationsServiceStub } from '../../../shared/testing/notifications-service-stub';
|
||||
import { HostWindowService } from '../../../shared/host-window.service';
|
||||
@@ -28,7 +24,6 @@ import { By } from '@angular/platform-browser';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { ObjectSelectService } from '../../../shared/object-select/object-select.service';
|
||||
import { ObjectSelectServiceStub } from '../../../shared/testing/object-select-service-stub';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { of } from 'rxjs/internal/observable/of';
|
||||
import { RestResponse } from '../../../core/cache/response.models';
|
||||
import { CollectionSelectComponent } from '../../../shared/object-select/collection-select/collection-select.component';
|
||||
@@ -39,6 +34,9 @@ import { SearchFormComponent } from '../../../shared/search-form/search-form.com
|
||||
import { Collection } from '../../../core/shared/collection.model';
|
||||
import { ErrorComponent } from '../../../shared/error/error.component';
|
||||
import { LoadingComponent } from '../../../shared/loading/loading.component';
|
||||
import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service';
|
||||
import { SearchService } from '../../../core/shared/search/search.service';
|
||||
import { PaginatedSearchOptions } from '../../../shared/search/paginated-search-options.model';
|
||||
|
||||
describe('ItemCollectionMapperComponent', () => {
|
||||
let comp: ItemCollectionMapperComponent;
|
||||
|
@@ -2,15 +2,12 @@ import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
|
||||
|
||||
import { ChangeDetectionStrategy, Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { fadeIn, fadeInOut } from '../../../shared/animations/fade';
|
||||
import { PaginatedSearchOptions } from '../../../+search-page/paginated-search-options.model';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { PaginatedList } from '../../../core/data/paginated-list';
|
||||
import { Collection } from '../../../core/shared/collection.model';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { getRemoteDataPayload, getSucceededRemoteData, toDSpaceObjectListRD } from '../../../core/shared/operators';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { SearchService } from '../../../+search-page/search-service/search.service';
|
||||
import { SearchConfigurationService } from '../../../+search-page/search-service/search-configuration.service';
|
||||
import { map, startWith, switchMap, take } from 'rxjs/operators';
|
||||
import { ItemDataService } from '../../../core/data/item-data.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
@@ -19,6 +16,9 @@ import { DSpaceObjectType } from '../../../core/shared/dspace-object-type.model'
|
||||
import { isNotEmpty } from '../../../shared/empty.util';
|
||||
import { RestResponse } from '../../../core/cache/response.models';
|
||||
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
|
||||
import { PaginatedSearchOptions } from '../../../shared/search/paginated-search-options.model';
|
||||
import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service';
|
||||
import { SearchService } from '../../../core/shared/search/search.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-item-collection-mapper',
|
||||
|
@@ -9,7 +9,6 @@ import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { ItemMoveComponent } from './item-move.component';
|
||||
import { NotificationsServiceStub } from '../../../shared/testing/notifications-service-stub';
|
||||
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||
import { SearchService } from '../../../+search-page/search-service/search.service';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { ItemDataService } from '../../../core/data/item-data.service';
|
||||
@@ -18,6 +17,7 @@ import { PaginatedList } from '../../../core/data/paginated-list';
|
||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { RestResponse } from '../../../core/cache/response.models';
|
||||
import { Collection } from '../../../core/shared/collection.model';
|
||||
import { SearchService } from '../../../core/shared/search/search.service';
|
||||
|
||||
describe('ItemMoveComponent', () => {
|
||||
let comp: ItemMoveComponent;
|
||||
|
@@ -1,12 +1,9 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { SearchService } from '../../../+search-page/search-service/search.service';
|
||||
import { first, map } from 'rxjs/operators';
|
||||
import { DSpaceObjectType } from '../../../core/shared/dspace-object-type.model';
|
||||
import { SearchOptions } from '../../../+search-page/search-options.model';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
||||
import { PaginatedList } from '../../../core/data/paginated-list';
|
||||
import { SearchResult } from '../../../+search-page/search-result.model';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||
@@ -17,9 +14,10 @@ import { getItemEditPath } from '../../item-page-routing.module';
|
||||
import { Observable, of as observableOf } from 'rxjs';
|
||||
import { RestResponse } from '../../../core/cache/response.models';
|
||||
import { Collection } from '../../../core/shared/collection.model';
|
||||
import { tap } from 'rxjs/internal/operators/tap';
|
||||
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
||||
import { PaginatedSearchOptions } from '../../../+search-page/paginated-search-options.model';
|
||||
import { SearchService } from '../../../core/shared/search/search.service';
|
||||
import { PaginatedSearchOptions } from '../../../shared/search/paginated-search-options.model';
|
||||
import { SearchResult } from '../../../shared/search/search-result.model';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-item-move',
|
||||
|
@@ -156,7 +156,9 @@ describe('ItemRelationshipsComponent', () => {
|
||||
getRelatedItemsByLabel: observableOf([author1, author2]),
|
||||
getItemRelationshipsArray: observableOf(relationships),
|
||||
deleteRelationship: observableOf(new RestResponse(true, 200, 'OK')),
|
||||
getItemResolvedRelatedItemsAndRelationships: observableCombineLatest(observableOf([author1, author2]), observableOf([item, item]), observableOf(relationships))
|
||||
getItemResolvedRelatedItemsAndRelationships: observableCombineLatest(observableOf([author1, author2]), observableOf([item, item]), observableOf(relationships)),
|
||||
getRelationshipsByRelatedItemIds: observableOf(relationships),
|
||||
getRelationshipTypeLabelsByItem: observableOf([relationshipType.leftwardType])
|
||||
}
|
||||
);
|
||||
|
||||
|
@@ -2,8 +2,8 @@ import { ChangeDetectorRef, Component, Inject, OnDestroy } from '@angular/core';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { FieldUpdate, FieldUpdates } from '../../../core/data/object-updates/object-updates.reducer';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { filter, map, switchMap, take, tap } from 'rxjs/operators';
|
||||
import { combineLatest as observableCombineLatest, zip as observableZip } from 'rxjs';
|
||||
import { filter, flatMap, map, switchMap, take, tap } from 'rxjs/operators';
|
||||
import { zip as observableZip } from 'rxjs';
|
||||
import { AbstractItemUpdateComponent } from '../abstract-item-update/abstract-item-update.component';
|
||||
import { ItemDataService } from '../../../core/data/item-data.service';
|
||||
import { ObjectUpdatesService } from '../../../core/data/object-updates/object-updates.service';
|
||||
@@ -21,7 +21,6 @@ import { ObjectCacheService } from '../../../core/cache/object-cache.service';
|
||||
import { getSucceededRemoteData } from '../../../core/shared/operators';
|
||||
import { RequestService } from '../../../core/data/request.service';
|
||||
import { Subscription } from 'rxjs/internal/Subscription';
|
||||
import { getRelationsByRelatedItemIds } from '../../simple/item-types/shared/item-relationships-utils';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-item-relationships',
|
||||
@@ -65,7 +64,7 @@ export class ItemRelationshipsComponent extends AbstractItemUpdateComponent impl
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
super.ngOnInit();
|
||||
this.relationLabels$ = this.relationshipService.getItemRelationshipLabels(this.item);
|
||||
this.relationLabels$ = this.relationshipService.getRelationshipTypeLabelsByItem(this.item);
|
||||
this.initializeItemUpdate();
|
||||
}
|
||||
|
||||
@@ -113,8 +112,9 @@ export class ItemRelationshipsComponent extends AbstractItemUpdateComponent impl
|
||||
);
|
||||
// Get all the relationships that should be removed
|
||||
const removedRelationships$ = removedItemIds$.pipe(
|
||||
getRelationsByRelatedItemIds(this.item, this.relationshipService)
|
||||
flatMap((uuids) => this.relationshipService.getRelationshipsByRelatedItemIds(this.item, uuids))
|
||||
);
|
||||
// const removedRelationships$ = removedItemIds$.pipe(flatMap((uuids: string[]) => this.relationshipService.getRelationshipsByRelatedItemIds(this.item, uuids)));
|
||||
// Request a delete for every relationship found in the observable created above
|
||||
removedRelationships$.pipe(
|
||||
take(1),
|
||||
|
@@ -4,7 +4,6 @@ import { MockTranslateLoader } from '../../../../shared/mocks/mock-translate-loa
|
||||
import { GenericItemPageFieldComponent } from '../../field-components/specific-field/generic/generic-item-page-field.component';
|
||||
import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
|
||||
import { ItemDataService } from '../../../../core/data/item-data.service';
|
||||
import { SearchFixedFilterService } from '../../../../+search-page/search-filters/search-filter/search-fixed-filter.service';
|
||||
import { TruncatableService } from '../../../../shared/truncatable/truncatable.service';
|
||||
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
@@ -15,6 +14,7 @@ import { createRelationshipsObservable } from '../shared/item.component.spec';
|
||||
import { PublicationComponent } from './publication.component';
|
||||
import { MetadataMap } from '../../../../core/shared/metadata.models';
|
||||
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/testing/utils';
|
||||
import { RelationshipService } from '../../../../core/data/relationship.service';
|
||||
|
||||
const mockItem: Item = Object.assign(new Item(), {
|
||||
bundles: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
|
||||
@@ -26,12 +26,6 @@ describe('PublicationComponent', () => {
|
||||
let comp: PublicationComponent;
|
||||
let fixture: ComponentFixture<PublicationComponent>;
|
||||
|
||||
const searchFixedFilterServiceStub = {
|
||||
/* tslint:disable:no-empty */
|
||||
getQueryByRelations: () => {}
|
||||
/* tslint:enable:no-empty */
|
||||
};
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [TranslateModule.forRoot({
|
||||
@@ -43,8 +37,8 @@ describe('PublicationComponent', () => {
|
||||
declarations: [PublicationComponent, GenericItemPageFieldComponent, TruncatePipe],
|
||||
providers: [
|
||||
{provide: ItemDataService, useValue: {}},
|
||||
{provide: SearchFixedFilterService, useValue: searchFixedFilterServiceStub},
|
||||
{provide: TruncatableService, useValue: {}}
|
||||
{provide: TruncatableService, useValue: {}},
|
||||
{provide: RelationshipService, useValue: {}}
|
||||
],
|
||||
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
|
@@ -1,8 +1,8 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { ItemComponent } from '../shared/item.component';
|
||||
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import { ItemComponent } from '../shared/item.component';
|
||||
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
||||
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
||||
|
||||
/**
|
||||
* Component that represents a publication Item page
|
||||
|
@@ -1,14 +1,12 @@
|
||||
import { getRemoteDataPayload, getSucceededRemoteData } from '../../../../core/shared/operators';
|
||||
import { hasNoValue, hasValue } from '../../../../shared/empty.util';
|
||||
import { getSucceededRemoteData } from '../../../../core/shared/operators';
|
||||
import { hasValue } from '../../../../shared/empty.util';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
|
||||
import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model';
|
||||
import { distinctUntilChanged, flatMap, map, switchMap } from 'rxjs/operators';
|
||||
import { zip as observableZip, combineLatest as observableCombineLatest } from 'rxjs';
|
||||
import { combineLatest as observableCombineLatest, zip as observableZip } from 'rxjs';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import { RemoteData } from '../../../../core/data/remote-data';
|
||||
import { RelationshipService } from '../../../../core/data/relationship.service';
|
||||
import { PaginatedList } from '../../../../core/data/paginated-list';
|
||||
import { RemoteData } from '../../../../core/data/remote-data';
|
||||
|
||||
/**
|
||||
* Operator for comparing arrays using a mapping function
|
||||
@@ -37,36 +35,6 @@ export const compareArraysUsing = <T>(mapFn: (t: T) => any) =>
|
||||
export const compareArraysUsingIds = <T extends { id: string }>() =>
|
||||
compareArraysUsing((t: T) => hasValue(t) ? t.id : undefined);
|
||||
|
||||
/**
|
||||
* Fetch the relationships which match the type label given
|
||||
* @param {string} label Type label
|
||||
* @param thisId The item's id of which the relations belong to
|
||||
* @returns {(source: Observable<[Relationship[] , RelationshipType[]]>) => Observable<Relationship[]>}
|
||||
*/
|
||||
export const filterRelationsByTypeLabel = (label: string, thisId?: string) =>
|
||||
(source: Observable<[Relationship[], RelationshipType[]]>): Observable<Relationship[]> =>
|
||||
source.pipe(
|
||||
switchMap(([relsCurrentPage, relTypesCurrentPage]) => {
|
||||
const relatedItems$ = observableZip(...relsCurrentPage.map((rel: Relationship) =>
|
||||
observableCombineLatest(
|
||||
rel.leftItem.pipe(getSucceededRemoteData(), getRemoteDataPayload()),
|
||||
rel.rightItem.pipe(getSucceededRemoteData(), getRemoteDataPayload()))
|
||||
)
|
||||
);
|
||||
return relatedItems$.pipe(
|
||||
map((arr) => relsCurrentPage.filter((rel: Relationship, idx: number) =>
|
||||
hasValue(relTypesCurrentPage[idx]) && (
|
||||
(hasNoValue(thisId) && (relTypesCurrentPage[idx].leftwardType === label ||
|
||||
relTypesCurrentPage[idx].rightwardType === label)) ||
|
||||
(thisId === arr[idx][0].id && relTypesCurrentPage[idx].leftwardType === label) ||
|
||||
(thisId === arr[idx][1].id && relTypesCurrentPage[idx].rightwardType === label)
|
||||
)
|
||||
))
|
||||
);
|
||||
}),
|
||||
distinctUntilChanged(compareArraysUsingIds())
|
||||
);
|
||||
|
||||
/**
|
||||
* Operator for turning a list of relationships into a list of the relevant items
|
||||
* @param {string} thisId The item's id of which the relations belong to
|
||||
@@ -128,17 +96,3 @@ export const paginatedRelationsToItems = (thisId: string) =>
|
||||
)
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* Operator for fetching an item's relationships, but filtered by related item IDs (essentially performing a reverse lookup)
|
||||
* Only relationships where leftItem or rightItem's ID is present in the list provided will be returned
|
||||
* @param item
|
||||
* @param relationshipService
|
||||
*/
|
||||
export const getRelationsByRelatedItemIds = (item: Item, relationshipService: RelationshipService) =>
|
||||
(source: Observable<string[]>): Observable<Relationship[]> =>
|
||||
source.pipe(
|
||||
flatMap((relatedItemIds: string[]) => relationshipService.getItemResolvedRelatedItemsAndRelationships(item).pipe(
|
||||
map(([leftItems, rightItems, rels]) => rels.filter((rel: Relationship, index: number) => relatedItemIds.indexOf(leftItems[index].uuid) > -1 || relatedItemIds.indexOf(rightItems[index].uuid) > -1))
|
||||
))
|
||||
);
|
||||
|
@@ -9,7 +9,6 @@ import { MockTranslateLoader } from '../../../../shared/mocks/mock-translate-loa
|
||||
import { ChangeDetectionStrategy, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
|
||||
import { isNotEmpty } from '../../../../shared/empty.util';
|
||||
import { SearchFixedFilterService } from '../../../../+search-page/search-filters/search-filter/search-fixed-filter.service';
|
||||
import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model';
|
||||
import { PaginatedList } from '../../../../core/data/paginated-list';
|
||||
import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
|
||||
@@ -24,6 +23,7 @@ import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-rep
|
||||
import { MetadataMap, MetadataValue } from '../../../../core/shared/metadata.models';
|
||||
import { compareArraysUsing, compareArraysUsingIds } from './item-relationships-utils';
|
||||
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/testing/utils';
|
||||
import { RelationshipService } from '../../../../core/data/relationship.service';
|
||||
|
||||
/**
|
||||
* Create a generic test for an item-page-fields component using a mockItem and the type of component
|
||||
@@ -37,12 +37,6 @@ export function getItemPageFieldsTest(mockItem: Item, component) {
|
||||
let comp: any;
|
||||
let fixture: ComponentFixture<any>;
|
||||
|
||||
const searchFixedFilterServiceStub = {
|
||||
/* tslint:disable:no-empty */
|
||||
getQueryByRelations: () => {}
|
||||
/* tslint:enable:no-empty */
|
||||
};
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [TranslateModule.forRoot({
|
||||
@@ -54,8 +48,8 @@ export function getItemPageFieldsTest(mockItem: Item, component) {
|
||||
declarations: [component, GenericItemPageFieldComponent, TruncatePipe],
|
||||
providers: [
|
||||
{provide: ItemDataService, useValue: {}},
|
||||
{provide: SearchFixedFilterService, useValue: searchFixedFilterServiceStub},
|
||||
{provide: TruncatableService, useValue: {}}
|
||||
{provide: TruncatableService, useValue: {}},
|
||||
{provide: RelationshipService, useValue: {}}
|
||||
],
|
||||
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { Component, Inject, Input } from '@angular/core';
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
|
||||
@Component({
|
||||
|
@@ -1,17 +1,15 @@
|
||||
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { MetadataRepresentation } from '../../../core/shared/metadata-representation/metadata-representation.model';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { combineLatest as observableCombineLatest, Observable, of as observableOf, zip as observableZip } from 'rxjs';
|
||||
import { RelationshipService } from '../../../core/data/relationship.service';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { combineLatest as observableCombineLatest, of as observableOf, zip as observableZip } from 'rxjs';
|
||||
import { MetadataValue } from '../../../core/shared/metadata.models';
|
||||
import { MetadatumRepresentation } from '../../../core/shared/metadata-representation/metadatum/metadatum-representation.model';
|
||||
import { filter, map, switchMap } from 'rxjs/operators';
|
||||
import { getSucceededRemoteData } from '../../../core/shared/operators';
|
||||
import { filter, map, switchMap } from 'rxjs/operators';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { Relationship } from '../../../core/shared/item-relationships/relationship.model';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { MetadatumRepresentation } from '../../../core/shared/metadata-representation/metadatum/metadatum-representation.model';
|
||||
import { ItemMetadataRepresentation } from '../../../core/shared/metadata-representation/item/item-metadata-representation.model';
|
||||
import { Subscription } from 'rxjs/internal/Subscription';
|
||||
import { AbstractIncrementalListComponent } from '../abstract-incremental-list/abstract-incremental-list.component';
|
||||
|
||||
@Component({
|
||||
|
@@ -4,13 +4,11 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { SearchFixedFilterService } from '../../../../+search-page/search-filters/search-filter/search-fixed-filter.service';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
|
||||
describe('RelatedEntitiesSearchComponent', () => {
|
||||
let comp: RelatedEntitiesSearchComponent;
|
||||
let fixture: ComponentFixture<RelatedEntitiesSearchComponent>;
|
||||
let fixedFilterService: SearchFixedFilterService;
|
||||
|
||||
const mockItem = Object.assign(new Item(), {
|
||||
id: 'id1'
|
||||
@@ -18,17 +16,11 @@ describe('RelatedEntitiesSearchComponent', () => {
|
||||
const mockRelationType = 'publicationsOfAuthor';
|
||||
const mockConfiguration = 'publication';
|
||||
const mockFilter= `f.${mockRelationType}=${mockItem.id}`;
|
||||
const fixedFilterServiceStub = {
|
||||
getFilterByRelation: () => mockFilter
|
||||
};
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [TranslateModule.forRoot(), NoopAnimationsModule, FormsModule],
|
||||
declarations: [RelatedEntitiesSearchComponent],
|
||||
providers: [
|
||||
{ provide: SearchFixedFilterService, useValue: fixedFilterServiceStub }
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).compileComponents();
|
||||
}));
|
||||
@@ -36,7 +28,6 @@ describe('RelatedEntitiesSearchComponent', () => {
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(RelatedEntitiesSearchComponent);
|
||||
comp = fixture.componentInstance;
|
||||
fixedFilterService = (comp as any).fixedFilterService;
|
||||
comp.relationType = mockRelationType;
|
||||
comp.item = mockItem;
|
||||
comp.configuration = mockConfiguration;
|
||||
|
@@ -1,9 +1,9 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import { SearchFixedFilterService } from '../../../../+search-page/search-filters/search-filter/search-fixed-filter.service';
|
||||
import { isNotEmpty } from '../../../../shared/empty.util';
|
||||
import { of } from 'rxjs/internal/observable/of';
|
||||
import { getFilterByRelation } from '../../../../shared/utils/relation-query.utils';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-related-entities-search',
|
||||
@@ -47,12 +47,9 @@ export class RelatedEntitiesSearchComponent implements OnInit {
|
||||
fixedFilter: string;
|
||||
configuration$: Observable<string>;
|
||||
|
||||
constructor(private fixedFilterService: SearchFixedFilterService) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (isNotEmpty(this.relationType) && isNotEmpty(this.item)) {
|
||||
this.fixedFilter = this.fixedFilterService.getFilterByRelation(this.relationType, this.item.id);
|
||||
this.fixedFilter = getFilterByRelation(this.relationType, this.item.id);
|
||||
}
|
||||
if (isNotEmpty(this.configuration)) {
|
||||
this.configuration$ = of(this.configuration);
|
||||
|
@@ -3,9 +3,9 @@ import { Item } from '../../../core/shared/item.model';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { PaginatedList } from '../../../core/data/paginated-list';
|
||||
import { RelationshipService } from '../../../core/data/relationship.service';
|
||||
import { FindListOptions } from '../../../core/data/request.models';
|
||||
import { ViewMode } from '../../../core/shared/view-mode.model';
|
||||
import { RelationshipService } from '../../../core/data/relationship.service';
|
||||
import { AbstractIncrementalListComponent } from '../abstract-incremental-list/abstract-incremental-list.component';
|
||||
|
||||
@Component({
|
||||
|
@@ -1,10 +1,10 @@
|
||||
import { of as observableOf } from 'rxjs';
|
||||
|
||||
import { MyDSpaceConfigurationService } from './my-dspace-configuration.service';
|
||||
import { PaginatedSearchOptions } from '../+search-page/paginated-search-options.model';
|
||||
import { PaginatedSearchOptions } from '../shared/search/paginated-search-options.model';
|
||||
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
|
||||
import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model';
|
||||
import { SearchFilter } from '../+search-page/search-filter.model';
|
||||
import { SearchFilter } from '../shared/search/search-filter.model';
|
||||
import { ActivatedRouteStub } from '../shared/testing/active-router-stub';
|
||||
import { MockRoleService } from '../shared/mocks/mock-role-service';
|
||||
import { cold, hot } from 'jasmine-marbles';
|
||||
@@ -38,12 +38,8 @@ describe('MyDSpaceConfigurationService', () => {
|
||||
|
||||
const roleService: any = new MockRoleService();
|
||||
|
||||
const fixedFilterService = jasmine.createSpyObj('SearchFixedFilterService', {
|
||||
getQueryByFilterName: observableOf(''),
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
service = new MyDSpaceConfigurationService(roleService, fixedFilterService, spy, activatedRoute);
|
||||
service = new MyDSpaceConfigurationService(roleService, spy, activatedRoute);
|
||||
});
|
||||
|
||||
describe('when the scope is called', () => {
|
||||
|
@@ -6,12 +6,11 @@ import { first, map } from 'rxjs/operators';
|
||||
|
||||
import { MyDSpaceConfigurationValueType } from './my-dspace-configuration-value-type';
|
||||
import { RoleService } from '../core/roles/role.service';
|
||||
import { SearchConfigurationOption } from '../+search-page/search-switch-configuration/search-configuration-option.model';
|
||||
import { SearchConfigurationService } from '../+search-page/search-service/search-configuration.service';
|
||||
import { RouteService } from '../core/services/route.service';
|
||||
import { SearchConfigurationOption } from '../shared/search/search-switch-configuration/search-configuration-option.model';
|
||||
import { SearchConfigurationService } from '../core/shared/search/search-configuration.service';
|
||||
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
|
||||
import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model';
|
||||
import { SearchFixedFilterService } from '../+search-page/search-filters/search-filter/search-fixed-filter.service';
|
||||
import { RouteService } from '../core/services/route.service';
|
||||
|
||||
/**
|
||||
* Service that performs all actions that have to do with the current mydspace configuration
|
||||
@@ -55,16 +54,14 @@ export class MyDSpaceConfigurationService extends SearchConfigurationService {
|
||||
* Initialize class
|
||||
*
|
||||
* @param {roleService} roleService
|
||||
* @param {SearchFixedFilterService} fixedFilterService
|
||||
* @param {RouteService} routeService
|
||||
* @param {ActivatedRoute} route
|
||||
*/
|
||||
constructor(protected roleService: RoleService,
|
||||
protected fixedFilterService: SearchFixedFilterService,
|
||||
protected routeService: RouteService,
|
||||
protected route: ActivatedRoute) {
|
||||
|
||||
super(routeService, fixedFilterService, route);
|
||||
super(routeService, route);
|
||||
|
||||
// override parent class initialization
|
||||
this._defaults = null;
|
||||
|
@@ -14,7 +14,7 @@ import { UploaderOptions } from '../../shared/uploader/uploader-options.model';
|
||||
import { HALEndpointService } from '../../core/shared/hal-endpoint.service';
|
||||
import { NotificationType } from '../../shared/notifications/models/notification-type';
|
||||
import { hasValue } from '../../shared/empty.util';
|
||||
import { SearchResult } from '../../+search-page/search-result.model';
|
||||
import { SearchResult } from '../../shared/search/search-result.model';
|
||||
|
||||
/**
|
||||
* This component represents the whole mydspace page header
|
||||
|
@@ -19,15 +19,14 @@ import { MyDSpacePageComponent, SEARCH_CONFIG_SERVICE } from './my-dspace-page.c
|
||||
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 '../+search-page/search-service/search.service';
|
||||
import { SearchConfigurationService } from '../+search-page/search-service/search-configuration.service';
|
||||
import { PaginatedSearchOptions } from '../+search-page/paginated-search-options.model';
|
||||
import { SearchService } from '../core/shared/search/search.service';
|
||||
import { SearchConfigurationService } from '../core/shared/search/search-configuration.service';
|
||||
import { PaginatedSearchOptions } from '../shared/search/paginated-search-options.model';
|
||||
import { SidebarService } from '../shared/sidebar/sidebar.service';
|
||||
import { SearchFilterService } from '../+search-page/search-filters/search-filter/search-filter.service';
|
||||
import { SearchFilterService } from '../core/shared/search/search-filter.service';
|
||||
import { RoleDirective } from '../shared/roles/role.directive';
|
||||
import { RoleService } from '../core/roles/role.service';
|
||||
import { MockRoleService } from '../shared/mocks/mock-role-service';
|
||||
import { SearchFixedFilterService } from '../+search-page/search-filters/search-filter/search-fixed-filter.service';
|
||||
import { createSuccessfulRemoteDataObject$ } from '../shared/testing/utils';
|
||||
|
||||
describe('MyDSpacePageComponent', () => {
|
||||
@@ -82,8 +81,6 @@ describe('MyDSpacePageComponent', () => {
|
||||
collapse: () => this.isCollapsed = observableOf(true),
|
||||
expand: () => this.isCollapsed = observableOf(false)
|
||||
};
|
||||
const mockFixedFilterService: SearchFixedFilterService = {
|
||||
} as SearchFixedFilterService;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
@@ -123,10 +120,6 @@ describe('MyDSpacePageComponent', () => {
|
||||
provide: RoleService,
|
||||
useValue: new MockRoleService()
|
||||
},
|
||||
{
|
||||
provide: SearchFixedFilterService,
|
||||
useValue: mockFixedFilterService
|
||||
}
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).overrideComponent(MyDSpacePageComponent, {
|
||||
|
@@ -15,19 +15,19 @@ import { RemoteData } from '../core/data/remote-data';
|
||||
import { DSpaceObject } from '../core/shared/dspace-object.model';
|
||||
import { pushInOut } from '../shared/animations/push';
|
||||
import { HostWindowService } from '../shared/host-window.service';
|
||||
import { PaginatedSearchOptions } from '../+search-page/paginated-search-options.model';
|
||||
import { SearchService } from '../+search-page/search-service/search.service';
|
||||
import { PaginatedSearchOptions } from '../shared/search/paginated-search-options.model';
|
||||
import { SearchService } from '../core/shared/search/search.service';
|
||||
import { SidebarService } from '../shared/sidebar/sidebar.service';
|
||||
import { hasValue } from '../shared/empty.util';
|
||||
import { getSucceededRemoteData } from '../core/shared/operators';
|
||||
import { MyDSpaceResponseParsingService } from '../core/data/mydspace-response-parsing.service';
|
||||
import { SearchConfigurationOption } from '../+search-page/search-switch-configuration/search-configuration-option.model';
|
||||
import { SearchConfigurationOption } from '../shared/search/search-switch-configuration/search-configuration-option.model';
|
||||
import { RoleType } from '../core/roles/role-types';
|
||||
import { SearchConfigurationService } from '../+search-page/search-service/search-configuration.service';
|
||||
import { SearchConfigurationService } from '../core/shared/search/search-configuration.service';
|
||||
import { MyDSpaceConfigurationService } from './my-dspace-configuration.service';
|
||||
import { ViewMode } from '../core/shared/view-mode.model';
|
||||
import { MyDSpaceRequest } from '../core/data/request.models';
|
||||
import { SearchResult } from '../+search-page/search-result.model';
|
||||
import { SearchResult } from '../shared/search/search-result.model';
|
||||
import { Context } from '../core/shared/context.model';
|
||||
|
||||
export const MYDSPACE_ROUTE = '/mydspace';
|
||||
|
@@ -5,7 +5,6 @@ import { SharedModule } from '../shared/shared.module';
|
||||
|
||||
import { MyDspacePageRoutingModule } from './my-dspace-page-routing.module';
|
||||
import { MyDSpacePageComponent } from './my-dspace-page.component';
|
||||
import { SearchPageModule } from '../+search-page/search-page.module';
|
||||
import { MyDSpaceResultsComponent } from './my-dspace-results/my-dspace-results.component';
|
||||
import { WorkspaceItemSearchResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/workspace-item-search-result/workspace-item-search-result-list-element.component';
|
||||
import { ClaimedSearchResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component';
|
||||
@@ -27,7 +26,6 @@ import { PoolSearchResultDetailElementComponent } from '../shared/object-detail/
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
MyDspacePageRoutingModule,
|
||||
SearchPageModule
|
||||
],
|
||||
declarations: [
|
||||
MyDSpacePageComponent,
|
||||
|
@@ -2,12 +2,12 @@ import { Component, Input } from '@angular/core';
|
||||
import { RemoteData } from '../../core/data/remote-data';
|
||||
import { DSpaceObject } from '../../core/shared/dspace-object.model';
|
||||
import { fadeIn, fadeInOut } from '../../shared/animations/fade';
|
||||
import { SearchOptions } from '../../+search-page/search-options.model';
|
||||
import { SearchOptions } from '../../shared/search/search-options.model';
|
||||
import { PaginatedList } from '../../core/data/paginated-list';
|
||||
import { ViewMode } from '../../core/shared/view-mode.model';
|
||||
import { isEmpty } from '../../shared/empty.util';
|
||||
import { SearchResult } from '../../+search-page/search-result.model';
|
||||
import { Context } from '../../core/shared/context.model';
|
||||
import { SearchResult } from '../../shared/search/search-result.model';
|
||||
|
||||
/**
|
||||
* Component that represents all results for mydspace page
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { configureSearchComponentTestingModule } from './search.component.spec';
|
||||
import { SearchConfigurationService } from './search-service/search-configuration.service';
|
||||
import { ConfigurationSearchPageComponent } from './configuration-search-page.component';
|
||||
import { SearchConfigurationService } from '../core/shared/search/search-configuration.service';
|
||||
|
||||
describe('ConfigurationSearchPageComponent', () => {
|
||||
let comp: ConfigurationSearchPageComponent;
|
||||
|
@@ -1,15 +1,14 @@
|
||||
import { HostWindowService } from '../shared/host-window.service';
|
||||
import { SearchService } from './search-service/search.service';
|
||||
import { SidebarService } from '../shared/sidebar/sidebar.service';
|
||||
import { SearchComponent } from './search.component';
|
||||
import { ChangeDetectionStrategy, Component, Inject, Input, OnInit } from '@angular/core';
|
||||
import { pushInOut } from '../shared/animations/push';
|
||||
import { SearchConfigurationService } from './search-service/search-configuration.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { PaginatedSearchOptions } from './paginated-search-options.model';
|
||||
import { SEARCH_CONFIG_SERVICE } from '../+my-dspace-page/my-dspace-page.component';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { SearchConfigurationService } from '../core/shared/search/search-configuration.service';
|
||||
import { Router } from '@angular/router';
|
||||
import { hasValue } from '../shared/empty.util';
|
||||
import { RouteService } from '../core/services/route.service';
|
||||
import { SearchService } from '../core/shared/search/search.service';
|
||||
|
||||
/**
|
||||
* This component renders a search page using a configuration as input.
|
||||
@@ -45,8 +44,9 @@ export class ConfigurationSearchPageComponent extends SearchComponent implements
|
||||
protected sidebarService: SidebarService,
|
||||
protected windowService: HostWindowService,
|
||||
@Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService,
|
||||
protected routeService: RouteService) {
|
||||
super(service, sidebarService, windowService, searchConfigService, routeService);
|
||||
protected routeService: RouteService,
|
||||
protected router: Router) {
|
||||
super(service, sidebarService, windowService, searchConfigService, routeService, router);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,24 +58,8 @@ export class ConfigurationSearchPageComponent extends SearchComponent implements
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
super.ngOnInit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current paginated search options after updating the configuration using the configuration input
|
||||
* This is to make sure the configuration is included in the paginated search options, as it is not part of any
|
||||
* query or route parameters
|
||||
* @returns {Observable<PaginatedSearchOptions>}
|
||||
*/
|
||||
protected getSearchOptions(): Observable<PaginatedSearchOptions> {
|
||||
return this.searchConfigService.paginatedSearchOptions.pipe(
|
||||
map((options: PaginatedSearchOptions) => {
|
||||
const config = this.configuration || options.configuration;
|
||||
const filter = this.fixedFilterQuery || options.fixedFilter;
|
||||
return Object.assign(options, {
|
||||
configuration: config,
|
||||
fixedFilter: filter
|
||||
});
|
||||
})
|
||||
);
|
||||
if (hasValue(this.configuration)) {
|
||||
this.routeService.setParameter('configuration', this.configuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,38 +0,0 @@
|
||||
import { SearchFixedFilterService } from './search-fixed-filter.service';
|
||||
import { RequestService } from '../../../core/data/request.service';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { RequestEntry } from '../../../core/data/request.reducer';
|
||||
import { FilteredDiscoveryQueryResponse } from '../../../core/cache/response.models';
|
||||
|
||||
describe('SearchFixedFilterService', () => {
|
||||
let service: SearchFixedFilterService;
|
||||
|
||||
const filterQuery = 'filter:query';
|
||||
|
||||
const requestServiceStub = Object.assign({
|
||||
/* tslint:disable:no-empty */
|
||||
configure: () => {
|
||||
},
|
||||
/* tslint:enable:no-empty */
|
||||
generateRequestId: () => 'fake-id',
|
||||
getByHref: () => observableOf(Object.assign(new RequestEntry(), {
|
||||
response: new FilteredDiscoveryQueryResponse(filterQuery, 200, 'OK')
|
||||
}))
|
||||
}) as RequestService;
|
||||
|
||||
beforeEach(() => {
|
||||
service = new SearchFixedFilterService();
|
||||
});
|
||||
|
||||
describe('when getQueryByRelations is called', () => {
|
||||
const relationType = 'isRelationOf';
|
||||
const itemUUID = 'c5b277e6-2477-48bb-8993-356710c285f3';
|
||||
|
||||
it('should contain the relationType and itemUUID', () => {
|
||||
const query = service.getQueryByRelations(relationType, itemUUID);
|
||||
expect(query.length).toBeGreaterThan(relationType.length + itemUUID.length);
|
||||
expect(query).toContain(relationType);
|
||||
expect(query).toContain(itemUUID);
|
||||
});
|
||||
});
|
||||
});
|
@@ -1,27 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
/**
|
||||
* Service for performing actions on the filtered-discovery-pages REST endpoint
|
||||
*/
|
||||
@Injectable()
|
||||
export class SearchFixedFilterService {
|
||||
|
||||
/**
|
||||
* Get the query for looking up items by relation type
|
||||
* @param {string} relationType Relation type
|
||||
* @param {string} itemUUID Item UUID
|
||||
* @returns {string} Query
|
||||
*/
|
||||
getQueryByRelations(relationType: string, itemUUID: string): string {
|
||||
return `query=relation.${relationType}:${itemUUID}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the filter for a relation with the item's UUID
|
||||
* @param relationType The type of relation e.g. 'isAuthorOfPublication'
|
||||
* @param itemUUID The item's UUID
|
||||
*/
|
||||
getFilterByRelation(relationType: string, itemUUID: string): string {
|
||||
return `f.${relationType}=${itemUUID}`;
|
||||
}
|
||||
}
|
@@ -1,7 +0,0 @@
|
||||
<div class="row mb-3 mb-md-1">
|
||||
<div class="labels col-sm-9 offset-sm-3">
|
||||
<ng-container *ngFor="let key of ((appliedFilters | async) | dsObjectKeys)">
|
||||
<ds-search-label *ngFor="let value of (appliedFilters | async)[key]" [inPlaceSearch]="inPlaceSearch" [key]="key" [value]="value" [appliedFilters]="appliedFilters"></ds-search-label>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
@@ -1,7 +1,6 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule } from '@angular/router';
|
||||
|
||||
import { SearchComponent } from './search.component';
|
||||
import { ConfigurationSearchPageGuard } from './configuration-search-page.guard';
|
||||
import { ConfigurationSearchPageComponent } from './configuration-search-page.component';
|
||||
import { SearchPageComponent } from './search-page.component';
|
||||
|
@@ -3,68 +3,18 @@ import { CommonModule } from '@angular/common';
|
||||
import { CoreModule } from '../core/core.module';
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
import { SearchPageRoutingModule } from './search-page-routing.module';
|
||||
import { SearchComponent } from './search.component';
|
||||
import { SearchResultsComponent } from './search-results/search-results.component';
|
||||
import { CommunitySearchResultGridElementComponent } from '../shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component'
|
||||
import { CollectionSearchResultGridElementComponent } from '../shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component';
|
||||
import { SearchSidebarComponent } from './search-sidebar/search-sidebar.component';
|
||||
import { SidebarService } from '../shared/sidebar/sidebar.service';
|
||||
import { SidebarEffects } from '../shared/sidebar/sidebar-effects.service';
|
||||
import { SearchSettingsComponent } from './search-settings/search-settings.component';
|
||||
import { EffectsModule } from '@ngrx/effects';
|
||||
import { SearchFiltersComponent } from './search-filters/search-filters.component';
|
||||
import { SearchFilterComponent } from './search-filters/search-filter/search-filter.component';
|
||||
import { SearchFacetFilterComponent } from './search-filters/search-filter/search-facet-filter/search-facet-filter.component';
|
||||
import { SearchFilterService } from './search-filters/search-filter/search-filter.service';
|
||||
import { SearchFixedFilterService } from './search-filters/search-filter/search-fixed-filter.service';
|
||||
import { SearchLabelsComponent } from './search-labels/search-labels.component';
|
||||
import { SearchRangeFilterComponent } from './search-filters/search-filter/search-range-filter/search-range-filter.component';
|
||||
import { SearchTextFilterComponent } from './search-filters/search-filter/search-text-filter/search-text-filter.component';
|
||||
import { SearchFacetFilterWrapperComponent } from './search-filters/search-filter/search-facet-filter-wrapper/search-facet-filter-wrapper.component';
|
||||
import { SearchBooleanFilterComponent } from './search-filters/search-filter/search-boolean-filter/search-boolean-filter.component';
|
||||
import { SearchHierarchyFilterComponent } from './search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component';
|
||||
import { SearchConfigurationService } from './search-service/search-configuration.service';
|
||||
import { SearchFacetOptionComponent } from './search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component';
|
||||
import { SearchFacetSelectedOptionComponent } from './search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component';
|
||||
import { SearchFacetRangeOptionComponent } from './search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component';
|
||||
import { SearchSwitchConfigurationComponent } from './search-switch-configuration/search-switch-configuration.component';
|
||||
import { SearchAuthorityFilterComponent } from './search-filters/search-filter/search-authority-filter/search-authority-filter.component';
|
||||
import { SearchLabelComponent } from './search-labels/search-label/search-label.component';
|
||||
import { SearchPageComponent } from './search-page.component';
|
||||
import { ConfigurationSearchPageComponent } from './configuration-search-page.component';
|
||||
import { ConfigurationSearchPageGuard } from './configuration-search-page.guard';
|
||||
import { SearchPageComponent } from './search-page.component';
|
||||
import { SidebarFilterService } from '../shared/sidebar/filter/sidebar-filter.service';
|
||||
import { StatisticsModule } from '../statistics/statistics.module';
|
||||
import { SearchTrackerComponent } from './search-tracker.component';
|
||||
|
||||
const effects = [
|
||||
SidebarEffects
|
||||
];
|
||||
import { StatisticsModule } from '../statistics/statistics.module';
|
||||
import { SearchComponent } from './search.component';
|
||||
|
||||
const components = [
|
||||
SearchPageComponent,
|
||||
SearchComponent,
|
||||
SearchResultsComponent,
|
||||
SearchSidebarComponent,
|
||||
SearchSettingsComponent,
|
||||
SearchFiltersComponent,
|
||||
SearchFilterComponent,
|
||||
SearchFacetFilterComponent,
|
||||
SearchLabelsComponent,
|
||||
SearchLabelComponent,
|
||||
SearchFacetFilterComponent,
|
||||
SearchFacetFilterWrapperComponent,
|
||||
SearchRangeFilterComponent,
|
||||
SearchTextFilterComponent,
|
||||
SearchHierarchyFilterComponent,
|
||||
SearchBooleanFilterComponent,
|
||||
SearchFacetOptionComponent,
|
||||
SearchFacetSelectedOptionComponent,
|
||||
SearchFacetRangeOptionComponent,
|
||||
SearchSwitchConfigurationComponent,
|
||||
SearchAuthorityFilterComponent,
|
||||
ConfigurationSearchPageComponent,
|
||||
SearchTrackerComponent,
|
||||
SearchTrackerComponent
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
@@ -72,30 +22,11 @@ const components = [
|
||||
SearchPageRoutingModule,
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
EffectsModule.forFeature(effects),
|
||||
CoreModule.forRoot(),
|
||||
StatisticsModule.forRoot(),
|
||||
],
|
||||
providers: [ConfigurationSearchPageGuard],
|
||||
declarations: components,
|
||||
providers: [
|
||||
SidebarService,
|
||||
SidebarFilterService,
|
||||
SearchFilterService,
|
||||
SearchFixedFilterService,
|
||||
ConfigurationSearchPageGuard,
|
||||
SearchConfigurationService
|
||||
],
|
||||
entryComponents: [
|
||||
SearchFacetFilterComponent,
|
||||
SearchRangeFilterComponent,
|
||||
SearchTextFilterComponent,
|
||||
SearchHierarchyFilterComponent,
|
||||
SearchBooleanFilterComponent,
|
||||
SearchFacetOptionComponent,
|
||||
SearchFacetSelectedOptionComponent,
|
||||
SearchFacetRangeOptionComponent,
|
||||
SearchAuthorityFilterComponent
|
||||
],
|
||||
exports: components
|
||||
})
|
||||
|
||||
|
@@ -1,26 +0,0 @@
|
||||
import { DSpaceObject } from '../core/shared/dspace-object.model';
|
||||
import { MetadataMap } from '../core/shared/metadata.models';
|
||||
import { ListableObject } from '../shared/object-collection/shared/listable-object.model';
|
||||
import { GenericConstructor } from '../core/shared/generic-constructor';
|
||||
|
||||
/**
|
||||
* Represents a search result object of a certain (<T>) DSpaceObject
|
||||
*/
|
||||
export class SearchResult<T extends DSpaceObject> implements ListableObject {
|
||||
/**
|
||||
* The DSpaceObject that was found
|
||||
*/
|
||||
indexableObject: T;
|
||||
|
||||
/**
|
||||
* The metadata that was used to find this item, hithighlighted
|
||||
*/
|
||||
hitHighlights: MetadataMap;
|
||||
|
||||
/**
|
||||
* Method that returns as which type of object this object should be rendered
|
||||
*/
|
||||
getRenderTypes(): Array<string | GenericConstructor<ListableObject>> {
|
||||
return [this.constructor as GenericConstructor<ListableObject>];
|
||||
}
|
||||
}
|
@@ -1,19 +0,0 @@
|
||||
<h2 *ngIf="!disableHeader">{{ (configuration ? configuration + '.search.results.head' : 'search.results.head') | translate }}</h2>
|
||||
<div *ngIf="searchResults?.hasSucceeded && !searchResults?.isLoading && searchResults?.payload?.page.length > 0" @fadeIn>
|
||||
<ds-viewable-collection
|
||||
[config]="searchConfig.pagination"
|
||||
[sortConfig]="searchConfig.sort"
|
||||
[objects]="searchResults"
|
||||
[linkType]="linkType"
|
||||
[hideGear]="true">
|
||||
</ds-viewable-collection></div>
|
||||
<ds-loading *ngIf="hasNoValue(searchResults) || hasNoValue(searchResults.payload) || searchResults.isLoading" message="{{'loading.search-results' | translate}}"></ds-loading>
|
||||
<ds-error *ngIf="searchResults?.hasFailed && (!searchResults?.error || searchResults?.error?.statusCode != 400)" message="{{'error.search-results' | translate}}"></ds-error>
|
||||
<div *ngIf="searchResults?.payload?.page.length == 0 || searchResults?.error?.statusCode == 400">
|
||||
{{ 'search.results.no-results' | translate }}
|
||||
<a [routerLink]="['/search']"
|
||||
[queryParams]="{ query: surroundStringWithQuotes(searchConfig?.query) }"
|
||||
queryParamsHandling="merge">
|
||||
{{"search.results.no-results-link" | translate}}
|
||||
</a>
|
||||
</div>
|
@@ -1,32 +0,0 @@
|
||||
<ng-container *ngVar="(searchOptions$ | async) as config">
|
||||
<h3>{{ 'search.sidebar.settings.title' | translate}}</h3>
|
||||
|
||||
<div class="result-order-settings">
|
||||
<ds-sidebar-dropdown
|
||||
*ngIf="config?.sort"
|
||||
[id]="'search-sidebar-sort'"
|
||||
[label]="'search.sidebar.settings.sort-by'"
|
||||
(change)="reloadOrder($event)"
|
||||
>
|
||||
<option *ngFor="let sortOption of searchOptionPossibilities"
|
||||
[value]="sortOption.field + ',' + sortOption.direction.toString()"
|
||||
[selected]="sortOption.field === config?.sort.field && sortOption.direction === (config?.sort.direction)? 'selected': null">
|
||||
{{'sorting.' + sortOption.field + '.' + sortOption.direction | translate}}
|
||||
</option>
|
||||
</ds-sidebar-dropdown>
|
||||
</div>
|
||||
|
||||
<div class="page-size-settings">
|
||||
<ds-sidebar-dropdown
|
||||
[id]="'search-sidebar-rpp'"
|
||||
[label]="'search.sidebar.settings.rpp'"
|
||||
(change)="reloadRPP($event)"
|
||||
>
|
||||
<option *ngFor="let pageSizeOption of config?.pagination.pageSizeOptions"
|
||||
[value]="pageSizeOption"
|
||||
[selected]="pageSizeOption === +config?.pagination.pageSize ? 'selected': null">
|
||||
{{pageSizeOption}}
|
||||
</option>
|
||||
</ds-sidebar-dropdown>
|
||||
</div>
|
||||
</ng-container>
|
@@ -2,16 +2,17 @@ import { Component, Inject, OnInit } from '@angular/core';
|
||||
import { Angulartics2 } from 'angulartics2';
|
||||
import { filter, map, switchMap } from 'rxjs/operators';
|
||||
import { SearchComponent } from './search.component';
|
||||
import { SearchService } from './search-service/search.service';
|
||||
import { SidebarService } from '../shared/sidebar/sidebar.service';
|
||||
import { SearchConfigurationService } from './search-service/search-configuration.service';
|
||||
import { HostWindowService } from '../shared/host-window.service';
|
||||
import { SEARCH_CONFIG_SERVICE } from '../+my-dspace-page/my-dspace-page.component';
|
||||
import { RouteService } from '../core/services/route.service';
|
||||
import { hasValue } from '../shared/empty.util';
|
||||
import { SearchQueryResponse } from './search-service/search-query-response.model';
|
||||
import { SearchSuccessResponse } from '../core/cache/response.models';
|
||||
import { PaginatedSearchOptions } from './paginated-search-options.model';
|
||||
import { SearchConfigurationService } from '../core/shared/search/search-configuration.service';
|
||||
import { Router } from '@angular/router';
|
||||
import { SearchService } from '../core/shared/search/search.service';
|
||||
import { PaginatedSearchOptions } from '../shared/search/paginated-search-options.model';
|
||||
import { SearchQueryResponse } from '../shared/search/search-query-response.model';
|
||||
|
||||
/**
|
||||
* This component triggers a page view statistic
|
||||
@@ -30,14 +31,15 @@ import { PaginatedSearchOptions } from './paginated-search-options.model';
|
||||
export class SearchTrackerComponent extends SearchComponent implements OnInit {
|
||||
|
||||
constructor(
|
||||
protected service:SearchService,
|
||||
protected sidebarService:SidebarService,
|
||||
protected windowService:HostWindowService,
|
||||
@Inject(SEARCH_CONFIG_SERVICE) public searchConfigService:SearchConfigurationService,
|
||||
protected routeService:RouteService,
|
||||
public angulartics2:Angulartics2
|
||||
protected service: SearchService,
|
||||
protected sidebarService: SidebarService,
|
||||
protected windowService: HostWindowService,
|
||||
@Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService,
|
||||
protected routeService: RouteService,
|
||||
public angulartics2: Angulartics2,
|
||||
protected router: Router
|
||||
) {
|
||||
super(service, sidebarService, windowService, searchConfigService, routeService);
|
||||
super(service, sidebarService, windowService, searchConfigService, routeService, router);
|
||||
}
|
||||
|
||||
ngOnInit():void {
|
||||
@@ -58,9 +60,9 @@ export class SearchTrackerComponent extends SearchComponent implements OnInit {
|
||||
)
|
||||
)
|
||||
.subscribe((entry) => {
|
||||
const config:PaginatedSearchOptions = entry.searchOptions;
|
||||
const searchQueryResponse:SearchQueryResponse = entry.response;
|
||||
const filters:Array<{ filter:string, operator:string, value:string, label:string; }> = [];
|
||||
const config: PaginatedSearchOptions = entry.searchOptions;
|
||||
const searchQueryResponse: SearchQueryResponse = entry.response;
|
||||
const filters:Array<{ filter: string, operator: string, value: string, label: string; }> = [];
|
||||
const appliedFilters = searchQueryResponse.appliedFilters || [];
|
||||
for (let i = 0, filtersLength = appliedFilters.length; i < filtersLength; i++) {
|
||||
const appliedFilter = appliedFilters[i];
|
||||
|
@@ -46,5 +46,9 @@
|
||||
[scopes]="(scopeListRD$ | async)"
|
||||
[inPlaceSearch]="inPlaceSearch">
|
||||
</ds-search-form>
|
||||
<ds-search-labels *ngIf="searchEnabled" [inPlaceSearch]="inPlaceSearch"></ds-search-labels>
|
||||
<div class="row mb-3 mb-md-1">
|
||||
<div class="labels col-sm-9 offset-sm-3">
|
||||
<ds-search-labels *ngIf="searchEnabled" [inPlaceSearch]="inPlaceSearch"></ds-search-labels>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
@@ -11,21 +11,19 @@ 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 { SearchComponent } from './search.component';
|
||||
import { SearchService } from './search-service/search.service';
|
||||
import { SearchService } from '../core/shared/search/search.service';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { NgbCollapseModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { SidebarService } from '../shared/sidebar/sidebar.service';
|
||||
import { SearchFilterService } from './search-filters/search-filter/search-filter.service';
|
||||
import { SearchConfigurationService } from './search-service/search-configuration.service';
|
||||
import { RemoteData } from '../core/data/remote-data';
|
||||
import { SearchFilterService } from '../core/shared/search/search-filter.service';
|
||||
import { SearchConfigurationService } from '../core/shared/search/search-configuration.service';
|
||||
import { SEARCH_CONFIG_SERVICE } from '../+my-dspace-page/my-dspace-page.component';
|
||||
import { RouteService } from '../core/services/route.service';
|
||||
import { SearchConfigurationServiceStub } from '../shared/testing/search-configuration-service-stub';
|
||||
import { PaginatedSearchOptions } from './paginated-search-options.model';
|
||||
import { SearchFixedFilterService } from './search-filters/search-filter/search-fixed-filter.service';
|
||||
import { createSuccessfulRemoteDataObject$ } from '../shared/testing/utils';
|
||||
import { PaginatedSearchOptions } from '../shared/search/paginated-search-options.model';
|
||||
|
||||
let comp: SearchComponent;
|
||||
let fixture: ComponentFixture<SearchComponent>;
|
||||
@@ -89,7 +87,6 @@ const routeServiceStub = {
|
||||
return observableOf('')
|
||||
}
|
||||
};
|
||||
const mockFixedFilterService: SearchFixedFilterService = {} as SearchFixedFilterService;
|
||||
|
||||
export function configureSearchComponentTestingModule(compType) {
|
||||
TestBed.configureTestingModule({
|
||||
@@ -122,10 +119,6 @@ export function configureSearchComponentTestingModule(compType) {
|
||||
provide: SearchFilterService,
|
||||
useValue: {}
|
||||
},
|
||||
{
|
||||
provide: SearchFixedFilterService,
|
||||
useValue: mockFixedFilterService
|
||||
},
|
||||
{
|
||||
provide: SearchConfigurationService,
|
||||
useValue: {
|
||||
@@ -158,6 +151,7 @@ describe('SearchComponent', () => {
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SearchComponent);
|
||||
comp = fixture.componentInstance; // SearchComponent test instance
|
||||
comp.inPlaceSearch = false;
|
||||
fixture.detectChanges();
|
||||
searchServiceObject = (comp as any).service;
|
||||
searchConfigurationServiceObject = (comp as any).searchConfigService;
|
||||
|
@@ -1,20 +1,22 @@
|
||||
import { ChangeDetectionStrategy, Component, Inject, Input, OnInit } from '@angular/core';
|
||||
import { BehaviorSubject, Observable, of as observableOf, Subscription } from 'rxjs';
|
||||
import { BehaviorSubject, Observable, Subscription } from 'rxjs';
|
||||
import { startWith, switchMap, } from 'rxjs/operators';
|
||||
import { PaginatedList } from '../core/data/paginated-list';
|
||||
import { RemoteData } from '../core/data/remote-data';
|
||||
import { DSpaceObject } from '../core/shared/dspace-object.model';
|
||||
import { pushInOut } from '../shared/animations/push';
|
||||
import { HostWindowService } from '../shared/host-window.service';
|
||||
import { PaginatedSearchOptions } from './paginated-search-options.model';
|
||||
import { SearchResult } from './search-result.model';
|
||||
import { SearchService } from './search-service/search.service';
|
||||
import { SidebarService } from '../shared/sidebar/sidebar.service';
|
||||
import { hasValue, isNotEmpty } from '../shared/empty.util';
|
||||
import { SearchConfigurationService } from './search-service/search-configuration.service';
|
||||
import { getSucceededRemoteData } from '../core/shared/operators';
|
||||
import { RouteService } from '../core/services/route.service';
|
||||
import { SEARCH_CONFIG_SERVICE } from '../+my-dspace-page/my-dspace-page.component';
|
||||
import { SearchConfigurationService } from '../core/shared/search/search-configuration.service';
|
||||
import { SearchResult } from '../shared/search/search-result.model';
|
||||
import { PaginatedSearchOptions } from '../shared/search/paginated-search-options.model';
|
||||
import { SearchService } from '../core/shared/search/search.service';
|
||||
import { currentPath } from '../shared/utils/route.utils';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-search',
|
||||
@@ -96,7 +98,8 @@ export class SearchComponent implements OnInit {
|
||||
protected sidebarService: SidebarService,
|
||||
protected windowService: HostWindowService,
|
||||
@Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService,
|
||||
protected routeService: RouteService) {
|
||||
protected routeService: RouteService,
|
||||
protected router: Router) {
|
||||
this.isXsOrSm$ = this.windowService.isXsOrSm();
|
||||
}
|
||||
|
||||
@@ -159,7 +162,7 @@ export class SearchComponent implements OnInit {
|
||||
*/
|
||||
private getSearchLink(): string {
|
||||
if (this.inPlaceSearch) {
|
||||
return './';
|
||||
return currentPath(this.router);
|
||||
}
|
||||
return this.service.getSearchLink();
|
||||
}
|
||||
|
@@ -20,7 +20,7 @@ import { Store, StoreModule } from '@ngrx/store';
|
||||
// Load the implementations that should be tested
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
import { HostWindowState } from './shared/host-window.reducer';
|
||||
import { HostWindowState } from './shared/search/host-window.reducer';
|
||||
import { HostWindowResizeAction } from './shared/host-window.actions';
|
||||
|
||||
import { MetadataService } from './core/metadata/metadata.service';
|
||||
|
@@ -1,13 +1,5 @@
|
||||
import { filter, map, take } from 'rxjs/operators';
|
||||
import {
|
||||
AfterViewInit,
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
HostListener,
|
||||
Inject,
|
||||
OnInit,
|
||||
ViewEncapsulation
|
||||
} from '@angular/core';
|
||||
import { AfterViewInit, ChangeDetectionStrategy, Component, HostListener, Inject, OnInit, ViewEncapsulation } from '@angular/core';
|
||||
import { NavigationCancel, NavigationEnd, NavigationStart, Router } from '@angular/router';
|
||||
|
||||
import { select, Store } from '@ngrx/store';
|
||||
@@ -18,12 +10,11 @@ import { GLOBAL_CONFIG, GlobalConfig } from '../config';
|
||||
|
||||
import { MetadataService } from './core/metadata/metadata.service';
|
||||
import { HostWindowResizeAction } from './shared/host-window.actions';
|
||||
import { HostWindowState } from './shared/host-window.reducer';
|
||||
import { HostWindowState } from './shared/search/host-window.reducer';
|
||||
import { NativeWindowRef, NativeWindowService } from './core/services/window.service';
|
||||
import { isAuthenticated } from './core/auth/selectors';
|
||||
import { AuthService } from './core/auth/auth.service';
|
||||
import { Angulartics2GoogleAnalytics } from 'angulartics2/ga';
|
||||
import { RouteService } from './core/services/route.service';
|
||||
import variables from '../styles/_exposed_variables.scss';
|
||||
import { CSSVariableService } from './shared/sass-helper/sass-helper.service';
|
||||
import { MenuService } from './shared/menu/menu.service';
|
||||
|
@@ -1,9 +1,13 @@
|
||||
import { StoreEffects } from './store.effects';
|
||||
import { NotificationsEffects } from './shared/notifications/notifications.effects';
|
||||
import { NavbarEffects } from './navbar/navbar.effects';
|
||||
import { SidebarEffects } from './shared/sidebar/sidebar-effects.service';
|
||||
import { RelationshipEffects } from './shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/relationship.effects';
|
||||
|
||||
export const appEffects = [
|
||||
StoreEffects,
|
||||
NavbarEffects,
|
||||
NotificationsEffects,
|
||||
SidebarEffects,
|
||||
RelationshipEffects
|
||||
];
|
||||
|
@@ -37,9 +37,9 @@ import { AdminSidebarComponent } from './+admin/admin-sidebar/admin-sidebar.comp
|
||||
import { AdminSidebarSectionComponent } from './+admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component';
|
||||
import { ExpandableAdminSidebarSectionComponent } from './+admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component';
|
||||
import { NavbarModule } from './navbar/navbar.module';
|
||||
import { ClientCookieService } from './core/services/client-cookie.service';
|
||||
import { JournalEntitiesModule } from './entity-groups/journal-entities/journal-entities.module';
|
||||
import { ResearchEntitiesModule } from './entity-groups/research-entities/research-entities.module';
|
||||
import { ClientCookieService } from './core/services/client-cookie.service';
|
||||
|
||||
export function getConfig() {
|
||||
return ENV_CONFIG;
|
||||
@@ -76,7 +76,7 @@ const ENTITY_IMPORTS = [
|
||||
|
||||
IMPORTS.push(
|
||||
StoreDevtoolsModule.instrument({
|
||||
maxAge: 100,
|
||||
maxAge: 1000,
|
||||
logOnly: ENV_CONFIG.production,
|
||||
})
|
||||
);
|
||||
|
@@ -1,38 +1,22 @@
|
||||
import { ActionReducerMap, createSelector, MemoizedSelector, State } from '@ngrx/store';
|
||||
import { ActionReducerMap, createSelector, MemoizedSelector } from '@ngrx/store';
|
||||
import * as fromRouter from '@ngrx/router-store';
|
||||
import { hostWindowReducer, HostWindowState } from './shared/search/host-window.reducer';
|
||||
import { CommunityListReducer, CommunityListState } from './community-list-page/community-list.reducer';
|
||||
import { hostWindowReducer, HostWindowState } from './shared/host-window.reducer';
|
||||
import { formReducer, FormState } from './shared/form/form.reducer';
|
||||
import {
|
||||
SidebarState,
|
||||
sidebarReducer
|
||||
} from './shared/sidebar/sidebar.reducer';
|
||||
import {
|
||||
SidebarFilterState,
|
||||
sidebarFilterReducer, SidebarFiltersState
|
||||
} from './shared/sidebar/filter/sidebar-filter.reducer';
|
||||
import {
|
||||
filterReducer,
|
||||
SearchFiltersState
|
||||
} from './+search-page/search-filters/search-filter/search-filter.reducer';
|
||||
import {
|
||||
notificationsReducer,
|
||||
NotificationsState
|
||||
} from './shared/notifications/notifications.reducers';
|
||||
import { sidebarReducer, SidebarState } from './shared/sidebar/sidebar.reducer';
|
||||
import { sidebarFilterReducer, SidebarFiltersState } from './shared/sidebar/filter/sidebar-filter.reducer';
|
||||
import { filterReducer, SearchFiltersState } from './shared/search/search-filters/search-filter/search-filter.reducer';
|
||||
import { notificationsReducer, NotificationsState } from './shared/notifications/notifications.reducers';
|
||||
import { truncatableReducer, TruncatablesState } from './shared/truncatable/truncatable.reducer';
|
||||
import {
|
||||
metadataRegistryReducer,
|
||||
MetadataRegistryState
|
||||
} from './+admin/admin-registries/metadata-registry/metadata-registry.reducers';
|
||||
import { metadataRegistryReducer, MetadataRegistryState } from './+admin/admin-registries/metadata-registry/metadata-registry.reducers';
|
||||
import { hasValue } from './shared/empty.util';
|
||||
import { cssVariablesReducer, CSSVariablesState } from './shared/sass-helper/sass-helper.reducer';
|
||||
import { menusReducer, MenusState } from './shared/menu/menu.reducer';
|
||||
import { historyReducer, HistoryState } from './shared/history/history.reducer';
|
||||
import {
|
||||
bitstreamFormatReducer,
|
||||
BitstreamFormatRegistryState
|
||||
} from './+admin/admin-registries/bitstream-formats/bitstream-format.reducers';
|
||||
import { selectableListReducer, SelectableListsState } from './shared/object-list/selectable-list/selectable-list.reducer';
|
||||
import { bitstreamFormatReducer, BitstreamFormatRegistryState } from './+admin/admin-registries/bitstream-formats/bitstream-format.reducers';
|
||||
import { ObjectSelectionListState, objectSelectionReducer } from './shared/object-select/object-select.reducer';
|
||||
import { NameVariantListsState, nameVariantReducer } from './shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/name-variant.reducer';
|
||||
|
||||
export interface AppState {
|
||||
router: fromRouter.RouterReducerState;
|
||||
@@ -49,6 +33,8 @@ export interface AppState {
|
||||
cssVariables: CSSVariablesState;
|
||||
menus: MenusState;
|
||||
objectSelection: ObjectSelectionListState;
|
||||
selectableLists: SelectableListsState;
|
||||
relationshipLists: NameVariantListsState;
|
||||
communityList: CommunityListState;
|
||||
}
|
||||
|
||||
@@ -67,6 +53,8 @@ export const appReducers: ActionReducerMap<AppState> = {
|
||||
cssVariables: cssVariablesReducer,
|
||||
menus: menusReducer,
|
||||
objectSelection: objectSelectionReducer,
|
||||
selectableLists: selectableListReducer,
|
||||
relationshipLists: nameVariantReducer,
|
||||
communityList: CommunityListReducer,
|
||||
};
|
||||
|
||||
|
@@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
|
||||
import { NormalizedObject } from '../models/normalized-object.model';
|
||||
import { getMapsToType, getRelationships } from './build-decorators';
|
||||
import { hasValue, isNotEmpty } from '../../../shared/empty.util';
|
||||
import { TypedObject } from '../object-cache.reducer';
|
||||
import { CacheableObject, TypedObject } from '../object-cache.reducer';
|
||||
|
||||
/**
|
||||
* Return true if halObj has a value for `_links.self`
|
||||
@@ -34,14 +34,13 @@ export class NormalizedObjectBuildService {
|
||||
*
|
||||
* @param {TDomain} domainModel a domain model
|
||||
*/
|
||||
normalize<T extends TypedObject>(domainModel: T): NormalizedObject<T> {
|
||||
normalize<T extends CacheableObject>(domainModel: T): NormalizedObject<T> {
|
||||
const normalizedConstructor = getMapsToType((domainModel as any).type);
|
||||
const relationships = getRelationships(normalizedConstructor) || [];
|
||||
|
||||
const normalizedModel = Object.assign({}, domainModel) as any;
|
||||
relationships.forEach((key: string) => {
|
||||
if (hasValue(domainModel[key])) {
|
||||
domainModel[key] = undefined;
|
||||
if (hasValue(normalizedModel[key])) {
|
||||
normalizedModel[key] = normalizedModel._links[key];
|
||||
}
|
||||
});
|
||||
return normalizedModel;
|
||||
|
@@ -116,7 +116,7 @@ export class RemoteDataBuildService {
|
||||
const requestEntry$ = href$.pipe(getRequestFromRequestHref(this.requestService));
|
||||
const tDomainList$ = requestEntry$.pipe(
|
||||
getResourceLinksFromResponse(),
|
||||
flatMap((resourceUUIDs: string[]) => {
|
||||
switchMap((resourceUUIDs: string[]) => {
|
||||
return this.objectCache.getList(resourceUUIDs).pipe(
|
||||
map((normList: Array<NormalizedObject<T>>) => {
|
||||
return normList.map((normalized: NormalizedObject<T>) => {
|
||||
@@ -273,12 +273,14 @@ export class RemoteDataBuildService {
|
||||
private toPaginatedList<T>(input: Observable<RemoteData<T[] | PaginatedList<T>>>, pageInfo: PageInfo): Observable<RemoteData<PaginatedList<T>>> {
|
||||
return input.pipe(
|
||||
map((rd: RemoteData<T[] | PaginatedList<T>>) => {
|
||||
const rdAny = rd as any;
|
||||
const newRD = new RemoteData(rdAny.requestPending, rdAny.responsePending, rdAny.isSuccessful, rd.error, undefined);
|
||||
if (Array.isArray(rd.payload)) {
|
||||
return Object.assign(rd, { payload: new PaginatedList(pageInfo, rd.payload) })
|
||||
return Object.assign(newRD, { payload: new PaginatedList(pageInfo, rd.payload) })
|
||||
} else if (isNotUndefined(rd.payload)) {
|
||||
return Object.assign(rd, { payload: new PaginatedList(pageInfo, rd.payload.page) });
|
||||
return Object.assign(newRD, { payload: new PaginatedList(pageInfo, rd.payload.page) });
|
||||
} else {
|
||||
return Object.assign(rd, { payload: new PaginatedList(pageInfo, []) });
|
||||
return Object.assign(newRD, { payload: new PaginatedList(pageInfo, []) });
|
||||
}
|
||||
})
|
||||
);
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize';
|
||||
import { autoserialize, deserialize, deserializeAs, inheritSerialization } from 'cerialize';
|
||||
import { Relationship } from '../../../shared/item-relationships/relationship.model';
|
||||
import { mapsTo, relationship } from '../../builders/build-decorators';
|
||||
import { NormalizedObject } from '../normalized-object.model';
|
||||
@@ -16,20 +16,20 @@ export class NormalizedRelationship extends NormalizedObject<Relationship> {
|
||||
/**
|
||||
* The identifier of this Relationship
|
||||
*/
|
||||
@autoserialize
|
||||
@deserialize
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* The item to the left of this relationship
|
||||
*/
|
||||
@autoserialize
|
||||
@deserialize
|
||||
@relationship(Item, false)
|
||||
leftItem: string;
|
||||
|
||||
/**
|
||||
* The item to the right of this relationship
|
||||
*/
|
||||
@autoserialize
|
||||
@deserialize
|
||||
@relationship(Item, false)
|
||||
rightItem: string;
|
||||
|
||||
@@ -46,15 +46,27 @@ export class NormalizedRelationship extends NormalizedObject<Relationship> {
|
||||
rightPlace: number;
|
||||
|
||||
/**
|
||||
* The type of Relationship
|
||||
* The name variant of the Item to the left side of this Relationship
|
||||
*/
|
||||
@autoserialize
|
||||
leftwardValue: string;
|
||||
|
||||
/**
|
||||
* The name variant of the Item to the right side of this Relationship
|
||||
*/
|
||||
@autoserialize
|
||||
rightwardValue: string;
|
||||
|
||||
/**
|
||||
* The type of Relationship
|
||||
*/
|
||||
@deserialize
|
||||
@relationship(RelationshipType, false)
|
||||
relationshipType: string;
|
||||
|
||||
/**
|
||||
* The universally unique identifier of this Relationship
|
||||
*/
|
||||
@autoserializeAs(new IDToUUIDSerializer(Relationship.type.value), 'id')
|
||||
@deserializeAs(new IDToUUIDSerializer(Relationship.type.value), 'id')
|
||||
uuid: string;
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { CacheableObject, TypedObject } from '../object-cache.reducer';
|
||||
import { autoserialize } from 'cerialize';
|
||||
import { autoserialize, deserialize } from 'cerialize';
|
||||
import { ResourceType } from '../../shared/resource-type';
|
||||
/**
|
||||
* An abstract model class for a NormalizedObject.
|
||||
@@ -8,10 +8,10 @@ export abstract class NormalizedObject<T extends TypedObject> implements Cacheab
|
||||
/**
|
||||
* The link to the rest endpoint where this object can be found
|
||||
*/
|
||||
@autoserialize
|
||||
@deserialize
|
||||
self: string;
|
||||
|
||||
@autoserialize
|
||||
@deserialize
|
||||
_links: {
|
||||
[name: string]: string
|
||||
};
|
||||
@@ -19,6 +19,6 @@ export abstract class NormalizedObject<T extends TypedObject> implements Cacheab
|
||||
/**
|
||||
* A string representing the kind of object
|
||||
*/
|
||||
@autoserialize
|
||||
@deserialize
|
||||
type: string;
|
||||
}
|
||||
|
3
src/app/core/cache/object-cache.service.ts
vendored
3
src/app/core/cache/object-cache.service.ts
vendored
@@ -196,8 +196,9 @@ export class ObjectCacheService {
|
||||
* false otherwise
|
||||
*/
|
||||
hasByUUID(uuid: string): boolean {
|
||||
let result: boolean;
|
||||
let result = false;
|
||||
|
||||
/* NB: that this is only a solution because the select method is synchronous, see: https://github.com/ngrx/store/issues/296#issuecomment-269032571*/
|
||||
this.store.pipe(
|
||||
select(selfLinkFromUuidSelector(uuid)),
|
||||
take(1)
|
||||
|
6
src/app/core/cache/response.models.ts
vendored
6
src/app/core/cache/response.models.ts
vendored
@@ -1,9 +1,9 @@
|
||||
import { SearchQueryResponse } from '../../+search-page/search-service/search-query-response.model';
|
||||
import { SearchQueryResponse } from '../../shared/search/search-query-response.model';
|
||||
import { RequestError } from '../data/request.models';
|
||||
import { PageInfo } from '../shared/page-info.model';
|
||||
import { ConfigObject } from '../config/models/config.model';
|
||||
import { FacetValue } from '../../+search-page/search-service/facet-value.model';
|
||||
import { SearchFilterConfig } from '../../+search-page/search-service/search-filter-config.model';
|
||||
import { FacetValue } from '../../shared/search/facet-value.model';
|
||||
import { SearchFilterConfig } from '../../shared/search/search-filter-config.model';
|
||||
import { IntegrationModel } from '../integration/models/integration.model';
|
||||
import { RegistryMetadataschemasResponse } from '../registry/registry-metadataschemas-response.model';
|
||||
import { RegistryMetadatafieldsResponse } from '../registry/registry-metadatafields-response.model';
|
||||
|
@@ -6,6 +6,7 @@ import { JsonPatchOperationsEffects } from './json-patch/json-patch-operations.e
|
||||
import { ServerSyncBufferEffects } from './cache/server-sync-buffer.effects';
|
||||
import { ObjectUpdatesEffects } from './data/object-updates/object-updates.effects';
|
||||
import { RouteEffects } from './services/route.effects';
|
||||
import { RouterEffects } from './router/router.effects';
|
||||
|
||||
export const coreEffects = [
|
||||
RequestEffects,
|
||||
@@ -15,5 +16,6 @@ export const coreEffects = [
|
||||
JsonPatchOperationsEffects,
|
||||
ServerSyncBufferEffects,
|
||||
ObjectUpdatesEffects,
|
||||
RouteEffects
|
||||
RouteEffects,
|
||||
RouterEffects
|
||||
];
|
||||
|
@@ -53,7 +53,7 @@ import { UUIDService } from './shared/uuid.service';
|
||||
import { AuthenticatedGuard } from './auth/authenticated.guard';
|
||||
import { AuthRequestService } from './auth/auth-request.service';
|
||||
import { AuthResponseParsingService } from './auth/auth-response-parsing.service';
|
||||
import { HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||
import { HTTP_INTERCEPTORS, HttpClient } from '@angular/common/http';
|
||||
import { AuthInterceptor } from './auth/auth.interceptor';
|
||||
import { HALEndpointService } from './shared/hal-endpoint.service';
|
||||
import { FacetValueResponseParsingService } from './data/facet-value-response-parsing.service';
|
||||
@@ -80,7 +80,8 @@ import { NormalizedObjectBuildService } from './cache/builders/normalized-object
|
||||
import { DSOChangeAnalyzer } from './data/dso-change-analyzer.service';
|
||||
import { ObjectUpdatesService } from './data/object-updates/object-updates.service';
|
||||
import { DefaultChangeAnalyzer } from './data/default-change-analyzer.service';
|
||||
import { SearchService } from '../+search-page/search-service/search.service';
|
||||
import { SearchService } from './shared/search/search.service';
|
||||
import { RelationshipService } from './data/relationship.service';
|
||||
import { NormalizedCollection } from './cache/models/normalized-collection.model';
|
||||
import { NormalizedCommunity } from './cache/models/normalized-community.model';
|
||||
import { NormalizedDSpaceObject } from './cache/models/normalized-dspace-object.model';
|
||||
@@ -101,7 +102,6 @@ import { NormalizedSubmissionFormsModel } from './config/models/normalized-confi
|
||||
import { NormalizedSubmissionSectionModel } from './config/models/normalized-config-submission-section.model';
|
||||
import { NormalizedAuthStatus } from './auth/models/normalized-auth-status.model';
|
||||
import { NormalizedAuthorityValue } from './integration/models/normalized-authority-value.model';
|
||||
import { RelationshipService } from './data/relationship.service';
|
||||
import { RoleService } from './roles/role.service';
|
||||
import { MyDSpaceGuard } from '../+my-dspace-page/my-dspace.guard';
|
||||
import { MyDSpaceResponseParsingService } from './data/mydspace-response-parsing.service';
|
||||
@@ -124,6 +124,31 @@ import { ObjectSelectService } from '../shared/object-select/object-select.servi
|
||||
import { SiteDataService } from './data/site-data.service';
|
||||
import { NormalizedSite } from './cache/models/normalized-site.model';
|
||||
|
||||
import {
|
||||
MOCK_RESPONSE_MAP,
|
||||
MockResponseMap,
|
||||
mockResponseMap
|
||||
} from '../shared/mocks/dspace-rest-v2/mocks/mock-response-map';
|
||||
import { EndpointMockingRestService } from '../shared/mocks/dspace-rest-v2/endpoint-mocking-rest.service';
|
||||
import { ENV_CONFIG, GLOBAL_CONFIG, GlobalConfig } from '../../config';
|
||||
import { SearchFilterService } from './shared/search/search-filter.service';
|
||||
import { SearchConfigurationService } from './shared/search/search-configuration.service';
|
||||
import { SelectableListService } from '../shared/object-list/selectable-list/selectable-list.service';
|
||||
import { RelationshipTypeService } from './data/relationship-type.service';
|
||||
import { SidebarService } from '../shared/sidebar/sidebar.service';
|
||||
|
||||
/**
|
||||
* When not in production, endpoint responses can be mocked for testing purposes
|
||||
* If there is no mock version available for the endpoint, the actual REST response will be used just like in production mode
|
||||
*/
|
||||
export const restServiceFactory = (cfg: GlobalConfig, mocks: MockResponseMap, http: HttpClient) => {
|
||||
if (ENV_CONFIG.production) {
|
||||
return new DSpaceRESTv2Service(http);
|
||||
} else {
|
||||
return new EndpointMockingRestService(cfg, mocks, http);
|
||||
}
|
||||
};
|
||||
|
||||
const IMPORTS = [
|
||||
CommonModule,
|
||||
StoreModule.forFeature('core', coreReducers, {}),
|
||||
@@ -143,7 +168,8 @@ const PROVIDERS = [
|
||||
CollectionDataService,
|
||||
SiteDataService,
|
||||
DSOResponseParsingService,
|
||||
DSpaceRESTv2Service,
|
||||
{ provide: MOCK_RESPONSE_MAP, useValue: mockResponseMap },
|
||||
{ provide: DSpaceRESTv2Service, useFactory: restServiceFactory, deps: [GLOBAL_CONFIG, MOCK_RESPONSE_MAP, HttpClient]},
|
||||
DynamicFormLayoutService,
|
||||
DynamicFormService,
|
||||
DynamicFormValidationService,
|
||||
@@ -214,6 +240,13 @@ const PROVIDERS = [
|
||||
TaskResponseParsingService,
|
||||
ClaimedTaskDataService,
|
||||
PoolTaskDataService,
|
||||
SearchService,
|
||||
SidebarService,
|
||||
SearchFilterService,
|
||||
SearchFilterService,
|
||||
SearchConfigurationService,
|
||||
SelectableListService,
|
||||
RelationshipTypeService,
|
||||
// register AuthInterceptor as HttpInterceptor
|
||||
{
|
||||
provide: HTTP_INTERCEPTORS,
|
||||
|
@@ -25,8 +25,8 @@ import { ResponseParsingService } from './parsing.service';
|
||||
import { GenericConstructor } from '../shared/generic-constructor';
|
||||
import { hasValue, isNotEmptyOperator } from '../../shared/empty.util';
|
||||
import { DSpaceObject } from '../shared/dspace-object.model';
|
||||
import { PaginatedSearchOptions } from '../../+search-page/paginated-search-options.model';
|
||||
import { SearchParam } from '../cache/models/search-param.model';
|
||||
import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model';
|
||||
|
||||
@Injectable()
|
||||
export class CollectionDataService extends ComColDataService<Collection> {
|
||||
@@ -71,7 +71,9 @@ export class CollectionDataService extends ComColDataService<Collection> {
|
||||
*/
|
||||
getAuthorizedCollectionByCommunity(communityId: string, options: FindListOptions = {}): Observable<RemoteData<PaginatedList<Collection>>> {
|
||||
const searchHref = 'findAuthorizedByCommunity';
|
||||
options.searchParams = [new SearchParam('uuid', communityId)];
|
||||
options = Object.assign({}, options, {
|
||||
searchParams: [new SearchParam('uuid', communityId)]
|
||||
});
|
||||
|
||||
return this.searchBy(searchHref, options).pipe(
|
||||
filter((collections: RemoteData<PaginatedList<Collection>>) => !collections.isResponsePending));
|
||||
|
@@ -16,6 +16,7 @@ import { HttpClient } from '@angular/common/http';
|
||||
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
|
||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||
import { Item } from '../shared/item.model';
|
||||
import * as uuidv4 from 'uuid/v4';
|
||||
|
||||
const endpoint = 'https://rest.api/core';
|
||||
|
||||
@@ -51,10 +52,11 @@ class DummyChangeAnalyzer implements ChangeAnalyzer<NormalizedTestObject> {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
describe('DataService', () => {
|
||||
let service: TestService;
|
||||
let options: FindListOptions;
|
||||
const requestService = {} as RequestService;
|
||||
const requestService = {generateRequestId: () => uuidv4()} as RequestService;
|
||||
const halService = {} as HALEndpointService;
|
||||
const rdbService = {} as RemoteDataBuildService;
|
||||
const notificationsService = {} as NotificationsService;
|
||||
@@ -87,6 +89,7 @@ describe('DataService', () => {
|
||||
comparator,
|
||||
);
|
||||
}
|
||||
|
||||
service = initTestService();
|
||||
|
||||
describe('getFindAllHref', () => {
|
||||
@@ -188,7 +191,7 @@ describe('DataService', () => {
|
||||
dso2.self = selfLink;
|
||||
dso2.metadata = [{ key: 'dc.title', value: name2 }];
|
||||
|
||||
spyOn(service, 'findById').and.returnValues(observableOf(dso));
|
||||
spyOn(service, 'findByHref').and.returnValues(observableOf(dso));
|
||||
spyOn(objectCache, 'getObjectBySelfLink').and.returnValues(observableOf(dso));
|
||||
spyOn(objectCache, 'addPatch');
|
||||
});
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
|
||||
import { Observable } from 'rxjs';
|
||||
import { distinctUntilChanged, filter, find, first, map, mergeMap, switchMap, take } from 'rxjs/operators';
|
||||
import { distinctUntilChanged, filter, find, first, map, mergeMap, skipWhile, switchMap, take, tap } from 'rxjs/operators';
|
||||
import { Store } from '@ngrx/store';
|
||||
|
||||
import { hasValue, isNotEmpty, isNotEmptyOperator } from '../../shared/empty.util';
|
||||
@@ -157,7 +157,7 @@ export abstract class DataService<T extends CacheableObject> {
|
||||
findById(id: string): Observable<RemoteData<T>> {
|
||||
|
||||
const hrefObs = this.halService.getEndpoint(this.linkPath).pipe(
|
||||
map((endpoint: string) => this.getIDHref(endpoint, encodeURIComponent(id))));
|
||||
map((endpoint: string) => this.getIDHref(endpoint, encodeURIComponent(id))));
|
||||
|
||||
hrefObs.pipe(
|
||||
find((href: string) => hasValue(href)))
|
||||
@@ -204,15 +204,22 @@ export abstract class DataService<T extends CacheableObject> {
|
||||
|
||||
const hrefObs = this.getSearchByHref(searchMethod, options);
|
||||
|
||||
hrefObs.pipe(
|
||||
first((href: string) => hasValue(href)))
|
||||
.subscribe((href: string) => {
|
||||
const request = new FindListRequest(this.requestService.generateRequestId(), href, options);
|
||||
request.responseMsToLive = 10 * 1000;
|
||||
this.requestService.configure(request);
|
||||
});
|
||||
return hrefObs.pipe(
|
||||
find((href: string) => hasValue(href)),
|
||||
tap((href: string) => {
|
||||
this.requestService.removeByHrefSubstring(href);
|
||||
const request = new FindListRequest(this.requestService.generateRequestId(), href, options);
|
||||
request.responseMsToLive = 10 * 1000;
|
||||
|
||||
return this.rdbService.buildList<T>(hrefObs) as Observable<RemoteData<PaginatedList<T>>>;
|
||||
this.requestService.configure(request);
|
||||
}
|
||||
),
|
||||
switchMap((href) => this.requestService.getByHref(href)),
|
||||
skipWhile((requestEntry) => hasValue(requestEntry) && requestEntry.completed),
|
||||
switchMap((href) =>
|
||||
this.rdbService.buildList<T>(hrefObs) as Observable<RemoteData<PaginatedList<T>>>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -236,7 +243,7 @@ export abstract class DataService<T extends CacheableObject> {
|
||||
if (isNotEmpty(operations)) {
|
||||
this.objectCache.addPatch(object.self, operations);
|
||||
}
|
||||
return this.findById(object.uuid);
|
||||
return this.findByHref(object.self);
|
||||
}
|
||||
));
|
||||
|
||||
|
@@ -4,6 +4,7 @@ import { ChangeAnalyzer } from './change-analyzer';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CacheableObject } from '../cache/object-cache.reducer';
|
||||
import { NormalizedObject } from '../cache/models/normalized-object.model';
|
||||
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
|
||||
|
||||
/**
|
||||
* A class to determine what differs between two
|
||||
@@ -11,6 +12,8 @@ import { NormalizedObject } from '../cache/models/normalized-object.model';
|
||||
*/
|
||||
@Injectable()
|
||||
export class DefaultChangeAnalyzer<T extends CacheableObject> implements ChangeAnalyzer<T> {
|
||||
constructor(private normalizeService: NormalizedObjectBuildService) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare the metadata of two CacheableObject and return the differences as
|
||||
@@ -22,6 +25,6 @@ export class DefaultChangeAnalyzer<T extends CacheableObject> implements ChangeA
|
||||
* The second object to compare
|
||||
*/
|
||||
diff(object1: T | NormalizedObject<T>, object2: T | NormalizedObject<T>): Operation[] {
|
||||
return compare(object1, object2);
|
||||
return compare(this.normalizeService.normalize(object1), this.normalizeService.normalize(object2));
|
||||
}
|
||||
}
|
||||
|
@@ -7,7 +7,7 @@ import { ResponseParsingService } from './parsing.service';
|
||||
import { RestRequest } from './request.models';
|
||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
|
||||
import { SearchFilterConfig } from '../../+search-page/search-service/search-filter-config.model';
|
||||
import { SearchFilterConfig } from '../../shared/search/search-filter-config.model';
|
||||
import { BaseResponseParsingService } from './base-response-parsing.service';
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
import { GlobalConfig } from '../../../config/global-config.interface';
|
||||
|
@@ -9,7 +9,7 @@ import { ResponseParsingService } from './parsing.service';
|
||||
import { RestRequest } from './request.models';
|
||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
|
||||
import { FacetValue } from '../../+search-page/search-service/facet-value.model';
|
||||
import { FacetValue } from '../../shared/search/facet-value.model';
|
||||
import { BaseResponseParsingService } from './base-response-parsing.service';
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
import { GlobalConfig } from '../../../config/global-config.interface';
|
||||
|
@@ -4,7 +4,7 @@ import { ResponseParsingService } from './parsing.service';
|
||||
import { RestRequest } from './request.models';
|
||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
|
||||
import { FacetValue } from '../../+search-page/search-service/facet-value.model';
|
||||
import { FacetValue } from '../../shared/search/facet-value.model';
|
||||
import { BaseResponseParsingService } from './base-response-parsing.service';
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
import { GLOBAL_CONFIG } from '../../../config';
|
||||
|
@@ -6,7 +6,7 @@ import { RestRequest } from './request.models';
|
||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
|
||||
import { hasValue } from '../../shared/empty.util';
|
||||
import { SearchQueryResponse } from '../../+search-page/search-service/search-query-response.model';
|
||||
import { SearchQueryResponse } from '../../shared/search/search-query-response.model';
|
||||
import { MetadataMap, MetadataValue } from '../shared/metadata.models';
|
||||
|
||||
@Injectable()
|
||||
|
@@ -3,7 +3,7 @@ import { hasValue } from '../../shared/empty.util';
|
||||
|
||||
export class PaginatedList<T> {
|
||||
|
||||
constructor(private pageInfo: PageInfo,
|
||||
constructor(public pageInfo: PageInfo,
|
||||
public page: T[]) {
|
||||
}
|
||||
|
||||
|
99
src/app/core/data/relationship-type.service.spec.ts
Normal file
99
src/app/core/data/relationship-type.service.spec.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
import { RequestService } from './request.service';
|
||||
import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service-stub';
|
||||
import { getMockRemoteDataBuildService } from '../../shared/mocks/mock-remote-data-build.service';
|
||||
import { RelationshipType } from '../shared/item-relationships/relationship-type.model';
|
||||
import { getMockRequestService } from '../../shared/mocks/mock-request.service';
|
||||
import { PaginatedList } from './paginated-list';
|
||||
import { PageInfo } from '../shared/page-info.model';
|
||||
import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../shared/testing/utils';
|
||||
import { RelationshipTypeService } from './relationship-type.service';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { ItemType } from '../shared/item-relationships/item-type.model';
|
||||
|
||||
describe('RelationshipTypeService', () => {
|
||||
let service: RelationshipTypeService;
|
||||
let requestService: RequestService;
|
||||
let restEndpointURL;
|
||||
let halService: any;
|
||||
let publicationTypeString;
|
||||
let personTypeString;
|
||||
let orgUnitTypeString;
|
||||
let publicationType;
|
||||
let personType;
|
||||
let orgUnitType;
|
||||
|
||||
let relationshipType1;
|
||||
let relationshipType2;
|
||||
|
||||
let buildList;
|
||||
let rdbService;
|
||||
|
||||
function init() {
|
||||
restEndpointURL = 'https://rest.api/relationshiptypes';
|
||||
halService = new HALEndpointServiceStub(restEndpointURL);
|
||||
publicationTypeString = 'Publication';
|
||||
personTypeString = 'Person';
|
||||
orgUnitTypeString = 'OrgUnit';
|
||||
publicationType = Object.assign(new ItemType(), {label: publicationTypeString});
|
||||
personType = Object.assign(new ItemType(), {label: personTypeString});
|
||||
orgUnitType = Object.assign(new ItemType(), {label: orgUnitTypeString});
|
||||
|
||||
relationshipType1 = Object.assign(new RelationshipType(), {
|
||||
id: '1',
|
||||
uuid: '1',
|
||||
leftwardType: 'isAuthorOfPublication',
|
||||
rightwardType: 'isPublicationOfAuthor',
|
||||
leftType: createSuccessfulRemoteDataObject$(publicationType),
|
||||
rightType: createSuccessfulRemoteDataObject$(personType)
|
||||
});
|
||||
|
||||
relationshipType2 = Object.assign(new RelationshipType(), {
|
||||
id: '2',
|
||||
uuid: '2',
|
||||
leftwardType: 'isOrgUnitOfPublication',
|
||||
rightwardType: 'isPublicationOfOrgUnit',
|
||||
leftType: createSuccessfulRemoteDataObject$(publicationType),
|
||||
rightType: createSuccessfulRemoteDataObject$(orgUnitType)
|
||||
});
|
||||
|
||||
buildList = createSuccessfulRemoteDataObject(new PaginatedList(new PageInfo(), [relationshipType1, relationshipType2]));
|
||||
rdbService = getMockRemoteDataBuildService(undefined, observableOf(buildList));
|
||||
|
||||
}
|
||||
function initTestService() {
|
||||
return new RelationshipTypeService(
|
||||
requestService,
|
||||
halService,
|
||||
rdbService
|
||||
);
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
init();
|
||||
requestService = getMockRequestService();
|
||||
service = initTestService();
|
||||
});
|
||||
|
||||
describe('getAllRelationshipTypes', () => {
|
||||
|
||||
it('should return all relationshipTypes', (done) => {
|
||||
const expected = service.getAllRelationshipTypes({});
|
||||
expected.subscribe((e) => {
|
||||
expect(e).toBe(buildList);
|
||||
done();
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
describe('getRelationshipTypeByLabelAndTypes', () => {
|
||||
|
||||
it('should return the type filtered by label and type strings', (done) => {
|
||||
const expected = service.getRelationshipTypeByLabelAndTypes(relationshipType1.leftwardType, publicationTypeString, personTypeString);
|
||||
expected.subscribe((e) => {
|
||||
expect(e).toBe(relationshipType1);
|
||||
done();
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
});
|
81
src/app/core/data/relationship-type.service.ts
Normal file
81
src/app/core/data/relationship-type.service.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { RequestService } from './request.service';
|
||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { filter, find, map, switchMap, tap } from 'rxjs/operators';
|
||||
import { configureRequest, getSucceededRemoteData } from '../shared/operators';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { RelationshipType } from '../shared/item-relationships/relationship-type.model';
|
||||
import { RemoteData } from './remote-data';
|
||||
import { PaginatedList } from './paginated-list';
|
||||
import { combineLatest as observableCombineLatest } from 'rxjs';
|
||||
import { ItemType } from '../shared/item-relationships/item-type.model';
|
||||
import { isNotUndefined } from '../../shared/empty.util';
|
||||
import { FindListOptions, FindListRequest } from './request.models';
|
||||
|
||||
/**
|
||||
* The service handling all relationship requests
|
||||
*/
|
||||
@Injectable()
|
||||
export class RelationshipTypeService {
|
||||
protected linkPath = 'relationshiptypes';
|
||||
|
||||
constructor(protected requestService: RequestService,
|
||||
protected halService: HALEndpointService,
|
||||
protected rdbService: RemoteDataBuildService) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the endpoint for a relationship type by ID
|
||||
* @param id
|
||||
*/
|
||||
getRelationshipTypeEndpoint(id: number) {
|
||||
return this.halService.getEndpoint(this.linkPath).pipe(
|
||||
map((href: string) => `${href}/${id}`)
|
||||
);
|
||||
}
|
||||
|
||||
getAllRelationshipTypes(options: FindListOptions): Observable<RemoteData<PaginatedList<RelationshipType>>> {
|
||||
const link$ = this.halService.getEndpoint(this.linkPath);
|
||||
return link$
|
||||
.pipe(
|
||||
map((endpointURL: string) => new FindListRequest(this.requestService.generateRequestId(), endpointURL, options)),
|
||||
configureRequest(this.requestService),
|
||||
switchMap(() => this.rdbService.buildList(link$))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the RelationshipType for a relationship type by label
|
||||
* @param label
|
||||
*/
|
||||
getRelationshipTypeByLabelAndTypes(label: string, firstType: string, secondType: string): Observable<RelationshipType> {
|
||||
return this.getAllRelationshipTypes({ currentPage: 1, elementsPerPage: Number.MAX_VALUE })
|
||||
.pipe(
|
||||
getSucceededRemoteData(),
|
||||
/* Flatten the page so we can treat it like an observable */
|
||||
switchMap((typeListRD: RemoteData<PaginatedList<RelationshipType>>) => typeListRD.payload.page),
|
||||
switchMap((type: RelationshipType) => {
|
||||
if (type.leftwardType === label) {
|
||||
return this.checkType(type, firstType, secondType);
|
||||
} else if (type.rightwardType === label) {
|
||||
return this.checkType(type, secondType, firstType);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// Check if relationship type matches the given types
|
||||
// returns a void observable if there's not match
|
||||
// returns an observable that emits the relationship type when there is a match
|
||||
private checkType(type: RelationshipType, firstType: string, secondType: string): Observable<RelationshipType> {
|
||||
const entityTypes = observableCombineLatest(type.leftType.pipe(getSucceededRemoteData()), type.rightType.pipe(getSucceededRemoteData()));
|
||||
return entityTypes.pipe(
|
||||
find(([leftTypeRD, rightTypeRD]: [RemoteData<ItemType>, RemoteData<ItemType>]) => leftTypeRD.payload.label === firstType && rightTypeRD.payload.label === secondType),
|
||||
filter((types) => isNotUndefined(types)),
|
||||
map(() => type)
|
||||
);
|
||||
}
|
||||
}
|
@@ -71,12 +71,14 @@ describe('RelationshipService', () => {
|
||||
const rdbService = getMockRemoteDataBuildService(undefined, buildList$);
|
||||
const objectCache = Object.assign({
|
||||
/* tslint:disable:no-empty */
|
||||
remove: () => {}
|
||||
remove: () => {},
|
||||
hasBySelfLinkObservable: () => observableOf(false)
|
||||
/* tslint:enable:no-empty */
|
||||
}) as ObjectCacheService;
|
||||
|
||||
const itemService = jasmine.createSpyObj('itemService', {
|
||||
findById: (uuid) => new RemoteData(false, false, true, undefined, relatedItems.filter((relatedItem) => relatedItem.id === uuid)[0])
|
||||
findById: (uuid) => new RemoteData(false, false, true, undefined, relatedItems.find((relatedItem) => relatedItem.id === uuid)),
|
||||
findByHref: createSuccessfulRemoteDataObject$(relatedItems[0])
|
||||
});
|
||||
|
||||
function initTestService() {
|
||||
@@ -90,6 +92,7 @@ describe('RelationshipService', () => {
|
||||
objectCache,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
@@ -133,14 +136,6 @@ describe('RelationshipService', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('getItemRelationshipLabels', () => {
|
||||
it('should return the correct labels', () => {
|
||||
service.getItemRelationshipLabels(item).subscribe((result) => {
|
||||
expect(result).toEqual([relationshipType.rightwardType]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getRelatedItems', () => {
|
||||
it('should return the related items', () => {
|
||||
service.getRelatedItems(item).subscribe((result) => {
|
||||
|
@@ -2,38 +2,43 @@ import { Injectable } from '@angular/core';
|
||||
import { RequestService } from './request.service';
|
||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { hasValue, hasValueOperator, isNotEmptyOperator } from '../../shared/empty.util';
|
||||
import { distinctUntilChanged, filter, flatMap, map, switchMap, take, tap } from 'rxjs/operators';
|
||||
import {
|
||||
configureRequest,
|
||||
filterSuccessfulResponses,
|
||||
getRemoteDataPayload, getResponseFromEntry,
|
||||
getSucceededRemoteData
|
||||
} from '../shared/operators';
|
||||
import { DeleteRequest, FindListOptions, RestRequest } from './request.models';
|
||||
import { hasValue, hasValueOperator, isNotEmpty, isNotEmptyOperator } from '../../shared/empty.util';
|
||||
import { distinctUntilChanged, filter, map, mergeMap, skipWhile, startWith, switchMap, take, tap } from 'rxjs/operators';
|
||||
import { configureRequest, getRemoteDataPayload, getResponseFromEntry, getSucceededRemoteData } from '../shared/operators';
|
||||
import { DeleteRequest, FindListOptions, PostRequest, RestRequest } from './request.models';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { RestResponse } from '../cache/response.models';
|
||||
import { Item } from '../shared/item.model';
|
||||
import { Relationship } from '../shared/item-relationships/relationship.model';
|
||||
import { RelationshipType } from '../shared/item-relationships/relationship-type.model';
|
||||
import { RemoteData } from './remote-data';
|
||||
import { combineLatest as observableCombineLatest } from 'rxjs/internal/observable/combineLatest';
|
||||
import { zip as observableZip } from 'rxjs';
|
||||
import { combineLatest, combineLatest as observableCombineLatest } from 'rxjs';
|
||||
import { PaginatedList } from './paginated-list';
|
||||
import { ItemDataService } from './item-data.service';
|
||||
import {
|
||||
compareArraysUsingIds, filterRelationsByTypeLabel, paginatedRelationsToItems,
|
||||
relationsToItems
|
||||
} from '../../+item-page/simple/item-types/shared/item-relationships-utils';
|
||||
import { compareArraysUsingIds, paginatedRelationsToItems, relationsToItems } from '../../+item-page/simple/item-types/shared/item-relationships-utils';
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
import { DataService } from './data.service';
|
||||
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { MemoizedSelector, select, Store } from '@ngrx/store';
|
||||
import { CoreState } from '../core.reducers';
|
||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||
import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
|
||||
import { SearchParam } from '../cache/models/search-param.model';
|
||||
import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
|
||||
import { AppState, keySelector } from '../../app.reducer';
|
||||
import { NameVariantListState } from '../../shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/name-variant.reducer';
|
||||
import { RemoveNameVariantAction, SetNameVariantAction } from '../../shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/name-variant.actions';
|
||||
|
||||
const relationshipListsStateSelector = (state: AppState) => state.relationshipLists;
|
||||
|
||||
const relationshipListStateSelector = (listID: string): MemoizedSelector<AppState, NameVariantListState> => {
|
||||
return keySelector<NameVariantListState>(listID, relationshipListsStateSelector);
|
||||
};
|
||||
|
||||
const relationshipStateSelector = (listID: string, itemID: string): MemoizedSelector<AppState, string> => {
|
||||
return keySelector<string>(itemID, relationshipListStateSelector(listID));
|
||||
};
|
||||
|
||||
/**
|
||||
* The service handling all relationship requests
|
||||
@@ -52,7 +57,8 @@ export class RelationshipService extends DataService<Relationship> {
|
||||
protected objectCache: ObjectCacheService,
|
||||
protected notificationsService: NotificationsService,
|
||||
protected http: HttpClient,
|
||||
protected comparator: DefaultChangeAnalyzer<Relationship>) {
|
||||
protected comparator: DefaultChangeAnalyzer<Relationship>,
|
||||
protected appStore: Store<AppState>) {
|
||||
super();
|
||||
}
|
||||
|
||||
@@ -65,76 +71,94 @@ export class RelationshipService extends DataService<Relationship> {
|
||||
* @param uuid
|
||||
*/
|
||||
getRelationshipEndpoint(uuid: string) {
|
||||
return this.halService.getEndpoint(this.linkPath).pipe(
|
||||
return this.getBrowseEndpoint().pipe(
|
||||
map((href: string) => `${href}/${uuid}`)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a relationship by its UUID
|
||||
* @param uuid
|
||||
*/
|
||||
findById(uuid: string): Observable<RemoteData<Relationship>> {
|
||||
const href$ = this.getRelationshipEndpoint(uuid);
|
||||
return this.rdbService.buildSingle<Relationship>(href$);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a delete request for a relationship by ID
|
||||
* @param uuid
|
||||
* @param id
|
||||
*/
|
||||
deleteRelationship(uuid: string): Observable<RestResponse> {
|
||||
return this.getRelationshipEndpoint(uuid).pipe(
|
||||
deleteRelationship(id: string): Observable<RestResponse> {
|
||||
return this.getRelationshipEndpoint(id).pipe(
|
||||
isNotEmptyOperator(),
|
||||
distinctUntilChanged(),
|
||||
take(1),
|
||||
map((endpointURL: string) => new DeleteRequest(this.requestService.generateRequestId(), endpointURL)),
|
||||
configureRequest(this.requestService),
|
||||
switchMap((restRequest: RestRequest) => this.requestService.getByUUID(restRequest.uuid)),
|
||||
getResponseFromEntry(),
|
||||
tap(() => this.clearRelatedCache(uuid))
|
||||
tap(() => this.removeRelationshipItemsFromCacheByRelationship(id))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a combined observable containing an array of all relationships in an item, as well as an array of the relationships their types
|
||||
* This is used for easier access of a relationship's type because they exist as observables
|
||||
* @param item
|
||||
* Method to create a new relationship
|
||||
* @param typeId The identifier of the relationship type
|
||||
* @param item1 The first item of the relationship
|
||||
* @param item2 The second item of the relationship
|
||||
* @param leftwardValue The leftward value of the relationship
|
||||
* @param rightwardValue The rightward value of the relationship
|
||||
*/
|
||||
getItemResolvedRelsAndTypes(item: Item): Observable<[Relationship[], RelationshipType[]]> {
|
||||
return observableCombineLatest(
|
||||
this.getItemRelationshipsArray(item),
|
||||
this.getItemRelationshipTypesArray(item)
|
||||
addRelationship(typeId: string, item1: Item, item2: Item, leftwardValue?: string, rightwardValue?: string): Observable<RestResponse> {
|
||||
const options: HttpOptions = Object.create({});
|
||||
let headers = new HttpHeaders();
|
||||
headers = headers.append('Content-Type', 'text/uri-list');
|
||||
options.headers = headers;
|
||||
return this.halService.getEndpoint(this.linkPath).pipe(
|
||||
isNotEmptyOperator(),
|
||||
take(1),
|
||||
map((endpointUrl: string) => `${endpointUrl}?relationshipType=${typeId}`),
|
||||
map((endpointUrl: string) => isNotEmpty(leftwardValue) ? `${endpointUrl}&leftwardValue=${leftwardValue}` : endpointUrl),
|
||||
map((endpointUrl: string) => isNotEmpty(rightwardValue) ? `${endpointUrl}&rightwardValue=${rightwardValue}` : endpointUrl),
|
||||
map((endpointURL: string) => new PostRequest(this.requestService.generateRequestId(), endpointURL, `${item1.self} \n ${item2.self}`, options)),
|
||||
configureRequest(this.requestService),
|
||||
switchMap((restRequest: RestRequest) => this.requestService.getByUUID(restRequest.uuid)),
|
||||
getResponseFromEntry(),
|
||||
tap(() => this.removeRelationshipItemsFromCache(item1)),
|
||||
tap(() => this.removeRelationshipItemsFromCache(item2))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a combined observable containing an array of all the item's relationship's left- and right-side items, as well as an array of the relationships their types
|
||||
* This is used for easier access of a relationship's type and left and right items because they exist as observables
|
||||
* @param item
|
||||
* Method to remove two items of a relationship from the cache using the identifier of the relationship
|
||||
* @param relationshipId The identifier of the relationship
|
||||
*/
|
||||
getItemResolvedRelatedItemsAndTypes(item: Item): Observable<[Item[], Item[], RelationshipType[]]> {
|
||||
return observableCombineLatest(
|
||||
this.getItemLeftRelatedItemArray(item),
|
||||
this.getItemRightRelatedItemArray(item),
|
||||
this.getItemRelationshipTypesArray(item)
|
||||
);
|
||||
private removeRelationshipItemsFromCacheByRelationship(relationshipId: string) {
|
||||
this.findById(relationshipId).pipe(
|
||||
getSucceededRemoteData(),
|
||||
getRemoteDataPayload(),
|
||||
switchMap((relationship: Relationship) => combineLatest(
|
||||
relationship.leftItem.pipe(getSucceededRemoteData(), getRemoteDataPayload()),
|
||||
relationship.rightItem.pipe(getSucceededRemoteData(), getRemoteDataPayload())
|
||||
)
|
||||
),
|
||||
take(1)
|
||||
).subscribe(([item1, item2]) => {
|
||||
this.removeRelationshipItemsFromCache(item1);
|
||||
this.removeRelationshipItemsFromCache(item2);
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a combined observable containing an array of all the item's relationship's left- and right-side items, as well as an array of the relationships themselves
|
||||
* This is used for easier access of the relationship and their left and right items because they exist as observables
|
||||
* @param item
|
||||
* Method to remove an item that's part of a relationship from the cache
|
||||
* @param item The item to remove from the cache
|
||||
*/
|
||||
getItemResolvedRelatedItemsAndRelationships(item: Item): Observable<[Item[], Item[], Relationship[]]> {
|
||||
return observableCombineLatest(
|
||||
this.getItemLeftRelatedItemArray(item),
|
||||
this.getItemRightRelatedItemArray(item),
|
||||
this.getItemRelationshipsArray(item)
|
||||
);
|
||||
private removeRelationshipItemsFromCache(item) {
|
||||
this.objectCache.remove(item.self);
|
||||
this.requestService.removeByHrefSubstring(item.self);
|
||||
combineLatest(
|
||||
this.objectCache.hasBySelfLinkObservable(item.self),
|
||||
this.requestService.hasByHrefObservable(item.self)
|
||||
).pipe(
|
||||
filter(([existsInOC, existsInRC]) => !existsInOC && !existsInRC),
|
||||
take(1),
|
||||
switchMap(() => this.itemService.findByHref(item.self).pipe(take(1)))
|
||||
).subscribe();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an item their relationships in the form of an array
|
||||
* Get an item its relationships in the form of an array
|
||||
* @param item
|
||||
*/
|
||||
getItemRelationshipsArray(item: Item): Observable<Relationship[]> {
|
||||
@@ -148,67 +172,33 @@ export class RelationshipService extends DataService<Relationship> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an item their relationship types in the form of an array
|
||||
* @param item
|
||||
*/
|
||||
getItemRelationshipTypesArray(item: Item): Observable<RelationshipType[]> {
|
||||
return this.getItemRelationshipsArray(item).pipe(
|
||||
flatMap((rels: Relationship[]) =>
|
||||
observableZip(...rels.map((rel: Relationship) => rel.relationshipType)).pipe(
|
||||
map(([...arr]: Array<RemoteData<RelationshipType>>) => arr.map((d: RemoteData<RelationshipType>) => d.payload).filter((type) => hasValue(type))),
|
||||
filter((arr) => arr.length === rels.length)
|
||||
)
|
||||
),
|
||||
distinctUntilChanged(compareArraysUsingIds())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an item his relationship's left-side related items in the form of an array
|
||||
* @param item
|
||||
*/
|
||||
getItemLeftRelatedItemArray(item: Item): Observable<Item[]> {
|
||||
return this.getItemRelationshipsArray(item).pipe(
|
||||
flatMap((rels: Relationship[]) => observableZip(...rels.map((rel: Relationship) => rel.leftItem)).pipe(
|
||||
map(([...arr]: Array<RemoteData<Item>>) => arr.map((rd: RemoteData<Item>) => rd.payload).filter((i) => hasValue(i))),
|
||||
filter((arr) => arr.length === rels.length)
|
||||
)),
|
||||
distinctUntilChanged(compareArraysUsingIds())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an item his relationship's right-side related items in the form of an array
|
||||
* @param item
|
||||
*/
|
||||
getItemRightRelatedItemArray(item: Item): Observable<Item[]> {
|
||||
return this.getItemRelationshipsArray(item).pipe(
|
||||
flatMap((rels: Relationship[]) => observableZip(...rels.map((rel: Relationship) => rel.rightItem)).pipe(
|
||||
map(([...arr]: Array<RemoteData<Item>>) => arr.map((rd: RemoteData<Item>) => rd.payload).filter((i) => hasValue(i))),
|
||||
filter((arr) => arr.length === rels.length)
|
||||
)),
|
||||
distinctUntilChanged(compareArraysUsingIds())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of an item their unique relationship type's labels
|
||||
* Get an array of the labels of an item’s unique relationship types
|
||||
* The array doesn't contain any duplicate labels
|
||||
* @param item
|
||||
*/
|
||||
getItemRelationshipLabels(item: Item): Observable<string[]> {
|
||||
return this.getItemResolvedRelatedItemsAndTypes(item).pipe(
|
||||
map(([leftItems, rightItems, relTypesCurrentPage]) => {
|
||||
return relTypesCurrentPage.map((type, index) => {
|
||||
if (leftItems[index].uuid === item.uuid) {
|
||||
return type.leftwardType;
|
||||
} else {
|
||||
return type.rightwardType;
|
||||
}
|
||||
});
|
||||
}),
|
||||
getRelationshipTypeLabelsByItem(item: Item): Observable<string[]> {
|
||||
return this.getItemRelationshipsArray(item).pipe(
|
||||
switchMap((relationships: Relationship[]) => observableCombineLatest(relationships.map((relationship: Relationship) => this.getRelationshipTypeLabelByRelationshipAndItem(relationship, item)))),
|
||||
map((labels: string[]) => Array.from(new Set(labels)))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private getRelationshipTypeLabelByRelationshipAndItem(relationship: Relationship, item: Item): Observable<string> {
|
||||
return relationship.leftItem.pipe(
|
||||
getSucceededRemoteData(),
|
||||
map((itemRD: RemoteData<Item>) => itemRD.payload),
|
||||
switchMap((otherItem: Item) => relationship.relationshipType.pipe(
|
||||
getSucceededRemoteData(),
|
||||
map((relationshipTypeRD) => relationshipTypeRD.payload),
|
||||
map((relationshipType: RelationshipType) => {
|
||||
if (otherItem.uuid === item.uuid) {
|
||||
return relationshipType.leftwardType;
|
||||
} else {
|
||||
return relationshipType.rightwardType;
|
||||
}
|
||||
})
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -244,7 +234,7 @@ export class RelationshipService extends DataService<Relationship> {
|
||||
if (options) {
|
||||
findListOptions = Object.assign(new FindListOptions(), options);
|
||||
}
|
||||
const searchParams = [ new SearchParam('label', label), new SearchParam('dso', item.id) ];
|
||||
const searchParams = [new SearchParam('label', label), new SearchParam('dso', item.id)];
|
||||
if (findListOptions.searchParams) {
|
||||
findListOptions.searchParams = [...findListOptions.searchParams, ...searchParams];
|
||||
} else {
|
||||
@@ -254,20 +244,146 @@ export class RelationshipService extends DataService<Relationship> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear object and request caches of the items related to a relationship (left and right items)
|
||||
* @param uuid
|
||||
* Method for fetching an item's relationships, but filtered by related item IDs (essentially performing a reverse lookup)
|
||||
* Only relationships where leftItem or rightItem's ID is present in the list provided will be returned
|
||||
* @param item
|
||||
* @param uuids
|
||||
*/
|
||||
clearRelatedCache(uuid: string) {
|
||||
this.findById(uuid).pipe(
|
||||
getRelationshipsByRelatedItemIds(item: Item, uuids: string[]): Observable<Relationship[]> {
|
||||
return this.getItemRelationshipsArray(item).pipe(
|
||||
switchMap((relationships: Relationship[]) => {
|
||||
return observableCombineLatest(...relationships.map((relationship: Relationship) => {
|
||||
const isLeftItem$ = this.isItemInUUIDArray(relationship.leftItem, uuids);
|
||||
const isRightItem$ = this.isItemInUUIDArray(relationship.rightItem, uuids);
|
||||
return observableCombineLatest(isLeftItem$, isRightItem$).pipe(
|
||||
filter(([isLeftItem, isRightItem]) => isLeftItem || isRightItem),
|
||||
map(() => relationship),
|
||||
startWith(undefined)
|
||||
);
|
||||
}))
|
||||
}),
|
||||
map((relationships: Relationship[]) => relationships.filter(((relationship) => hasValue(relationship)))),
|
||||
)
|
||||
}
|
||||
|
||||
private isItemInUUIDArray(itemRD$: Observable<RemoteData<Item>>, uuids: string[]) {
|
||||
return itemRD$.pipe(
|
||||
getSucceededRemoteData(),
|
||||
flatMap((rd: RemoteData<Relationship>) => observableCombineLatest(rd.payload.leftItem.pipe(getSucceededRemoteData()), rd.payload.rightItem.pipe(getSucceededRemoteData()))),
|
||||
take(1)
|
||||
).subscribe(([leftItem, rightItem]) => {
|
||||
this.objectCache.remove(leftItem.payload.self);
|
||||
this.objectCache.remove(rightItem.payload.self);
|
||||
this.requestService.removeByHrefSubstring(leftItem.payload.self);
|
||||
this.requestService.removeByHrefSubstring(rightItem.payload.self);
|
||||
});
|
||||
map((itemRD: RemoteData<Item>) => itemRD.payload),
|
||||
map((item: Item) => uuids.includes(item.uuid))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to retrieve a relationship based on two items and a relationship type label
|
||||
* @param item1 The first item in the relationship
|
||||
* @param item2 The second item in the relationship
|
||||
* @param label The rightward or leftward type of the relationship
|
||||
*/
|
||||
getRelationshipByItemsAndLabel(item1: Item, item2: Item, label: string): Observable<Relationship> {
|
||||
return this.getItemRelationshipsByLabel(item1, label)
|
||||
.pipe(
|
||||
getSucceededRemoteData(),
|
||||
isNotEmptyOperator(),
|
||||
map((relationshipListRD: RemoteData<PaginatedList<Relationship>>) => relationshipListRD.payload.page),
|
||||
mergeMap((relationships: Relationship[]) => {
|
||||
return observableCombineLatest(...relationships.map((relationship: Relationship) => {
|
||||
return observableCombineLatest(
|
||||
this.isItemMatchWithItemRD(relationship.leftItem, item2),
|
||||
this.isItemMatchWithItemRD(relationship.rightItem, item2)
|
||||
).pipe(
|
||||
map(([isLeftItem, isRightItem]) => isLeftItem || isRightItem),
|
||||
map((isMatch) => isMatch ? relationship : undefined)
|
||||
);
|
||||
}))
|
||||
}),
|
||||
map((relationships: Relationship[]) => relationships.find(((relationship) => hasValue(relationship))))
|
||||
)
|
||||
}
|
||||
|
||||
private isItemMatchWithItemRD(itemRD$: Observable<RemoteData<Item>>, itemCheck: Item): Observable<boolean> {
|
||||
return itemRD$.pipe(
|
||||
getSucceededRemoteData(),
|
||||
map((itemRD: RemoteData<Item>) => itemRD.payload),
|
||||
map((item: Item) => item.uuid === itemCheck.uuid)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to set the name variant for specific list and item
|
||||
* @param listID The list for which to save the name variant
|
||||
* @param itemID The item ID for which to save the name variant
|
||||
* @param nameVariant The name variant to save
|
||||
*/
|
||||
public setNameVariant(listID: string, itemID: string, nameVariant: string) {
|
||||
this.appStore.dispatch(new SetNameVariantAction(listID, itemID, nameVariant));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to retrieve the name variant for a specific list and item
|
||||
* @param listID The list for which to retrieve the name variant
|
||||
* @param itemID The item ID for which to retrieve the name variant
|
||||
*/
|
||||
public getNameVariant(listID: string, itemID: string): Observable<string> {
|
||||
return this.appStore.pipe(
|
||||
select(relationshipStateSelector(listID, itemID))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to remove the name variant for specific list and item
|
||||
* @param listID The list for which to remove the name variant
|
||||
* @param itemID The item ID for which to remove the name variant
|
||||
*/
|
||||
public removeNameVariant(listID: string, itemID: string) {
|
||||
this.appStore.dispatch(new RemoveNameVariantAction(listID, itemID));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to retrieve all name variants for a single list
|
||||
* @param listID The id of the list
|
||||
*/
|
||||
public getNameVariantsByListID(listID: string) {
|
||||
return this.appStore.pipe(select(relationshipListStateSelector(listID)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to update the name variant on the server
|
||||
* @param item1 The first item of the relationship
|
||||
* @param item2 The second item of the relationship
|
||||
* @param relationshipLabel The leftward or rightward type of the relationship
|
||||
* @param nameVariant The name variant to set for the matching relationship
|
||||
*/
|
||||
public updateNameVariant(item1: Item, item2: Item, relationshipLabel: string, nameVariant: string): Observable<RemoteData<Relationship>> {
|
||||
return this.getRelationshipByItemsAndLabel(item1, item2, relationshipLabel)
|
||||
.pipe(
|
||||
switchMap((relation: Relationship) =>
|
||||
relation.relationshipType.pipe(
|
||||
getSucceededRemoteData(),
|
||||
getRemoteDataPayload(),
|
||||
map((type) => {
|
||||
return { relation, type }
|
||||
})
|
||||
)
|
||||
),
|
||||
switchMap((relationshipAndType: { relation: Relationship, type: RelationshipType }) => {
|
||||
const { relation, type } = relationshipAndType;
|
||||
let updatedRelationship;
|
||||
if (relationshipLabel === type.leftwardType) {
|
||||
updatedRelationship = Object.assign(new Relationship(), relation, { rightwardValue: nameVariant });
|
||||
} else {
|
||||
updatedRelationship = Object.assign(new Relationship(), relation, { leftwardValue: nameVariant });
|
||||
}
|
||||
return this.update(updatedRelationship);
|
||||
}),
|
||||
// skipWhile((relationshipRD: RemoteData<Relationship>) => !relationshipRD.isSuccessful)
|
||||
tap((relationshipRD: RemoteData<Relationship>) => {
|
||||
if (relationshipRD.hasSucceeded) {
|
||||
this.removeRelationshipItemsFromCache(item1);
|
||||
this.removeRelationshipItemsFromCache(item2);
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -13,11 +13,11 @@ export enum RemoteDataState {
|
||||
*/
|
||||
export class RemoteData<T> {
|
||||
constructor(
|
||||
private requestPending: boolean,
|
||||
private responsePending: boolean,
|
||||
private isSuccessful: boolean,
|
||||
public error: RemoteDataError,
|
||||
public payload: T
|
||||
private requestPending?: boolean,
|
||||
private responsePending?: boolean,
|
||||
private isSuccessful?: boolean,
|
||||
public error?: RemoteDataError,
|
||||
public payload?: T
|
||||
) {
|
||||
}
|
||||
|
||||
|
@@ -3,7 +3,7 @@ import { HttpHeaders } from '@angular/common/http';
|
||||
|
||||
import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store';
|
||||
import { Observable, race as observableRace } from 'rxjs';
|
||||
import { filter, map, mergeMap, take } from 'rxjs/operators';
|
||||
import { filter, map, mergeMap, switchMap, take } from 'rxjs/operators';
|
||||
import { cloneDeep, remove } from 'lodash';
|
||||
|
||||
import { AppState } from '../../app.reducer';
|
||||
@@ -304,6 +304,7 @@ export class RequestService {
|
||||
*/
|
||||
hasByHref(href: string): boolean {
|
||||
let result = false;
|
||||
/* NB: that this is only a solution because the select method is synchronous, see: https://github.com/ngrx/store/issues/296#issuecomment-269032571*/
|
||||
this.getByHref(href).pipe(
|
||||
take(1)
|
||||
).subscribe((requestEntry: RequestEntry) => result = this.isValid(requestEntry));
|
||||
|
@@ -6,7 +6,7 @@ import { RestRequest } from './request.models';
|
||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
|
||||
import { hasValue } from '../../shared/empty.util';
|
||||
import { SearchQueryResponse } from '../../+search-page/search-service/search-query-response.model';
|
||||
import { SearchQueryResponse } from '../../shared/search/search-query-response.model';
|
||||
import { MetadataMap, MetadataValue } from '../shared/metadata.models';
|
||||
|
||||
@Injectable()
|
||||
|
@@ -26,7 +26,7 @@ export interface HttpOptions {
|
||||
@Injectable()
|
||||
export class DSpaceRESTv2Service {
|
||||
|
||||
constructor(private http: HttpClient) {
|
||||
constructor(protected http: HttpClient) {
|
||||
|
||||
}
|
||||
|
||||
|
@@ -9,6 +9,7 @@ import { Group } from './group.model';
|
||||
@mapsTo(EPerson)
|
||||
@inheritSerialization(NormalizedDSpaceObject)
|
||||
export class NormalizedEPerson extends NormalizedDSpaceObject<EPerson> implements CacheableObject {
|
||||
|
||||
/**
|
||||
* A string representing the unique handle of this EPerson
|
||||
*/
|
||||
|
@@ -7,7 +7,7 @@ import { GenericConstructor } from '../shared/generic-constructor';
|
||||
/**
|
||||
* Class the represents a metadata field
|
||||
*/
|
||||
export class MetadataField implements ListableObject {
|
||||
export class MetadataField extends ListableObject {
|
||||
static type = new ResourceType('metadatafield');
|
||||
|
||||
/**
|
||||
|
@@ -5,7 +5,7 @@ import { GenericConstructor } from '../shared/generic-constructor';
|
||||
/**
|
||||
* Class that represents a metadata schema
|
||||
*/
|
||||
export class MetadataSchema implements ListableObject {
|
||||
export class MetadataSchema extends ListableObject {
|
||||
static type = new ResourceType('metadataschema');
|
||||
|
||||
/**
|
||||
|
@@ -2,7 +2,6 @@ import { autoserialize, deserialize, inheritSerialization } from 'cerialize';
|
||||
import { mapsTo, relationship } from '../cache/builders/build-decorators';
|
||||
import { MetadataField } from './metadata-field.model';
|
||||
import { NormalizedObject } from '../cache/models/normalized-object.model';
|
||||
import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
|
||||
import { MetadataSchema } from './metadata-schema.model';
|
||||
|
||||
/**
|
||||
|
@@ -1,7 +1,6 @@
|
||||
import { autoserialize, inheritSerialization } from 'cerialize';
|
||||
import { NormalizedObject } from '../cache/models/normalized-object.model';
|
||||
import { mapsTo } from '../cache/builders/build-decorators';
|
||||
import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
|
||||
import { MetadataSchema } from './metadata-schema.model';
|
||||
|
||||
/**
|
||||
@@ -33,4 +32,5 @@ export class NormalizedMetadataSchema extends NormalizedObject<MetadataSchema> {
|
||||
*/
|
||||
@autoserialize
|
||||
namespace: string;
|
||||
|
||||
}
|
||||
|
22
src/app/core/router/router.actions.ts
Normal file
22
src/app/core/router/router.actions.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Action } from '@ngrx/store';
|
||||
import { type } from '../../shared/ngrx/type';
|
||||
|
||||
/**
|
||||
* The list of HrefIndexAction type definitions
|
||||
*/
|
||||
export const RouterActionTypes = {
|
||||
ROUTE_UPDATE: type('dspace/core/router/ROUTE_UPDATE'),
|
||||
};
|
||||
|
||||
/* tslint:disable:max-classes-per-file */
|
||||
/**
|
||||
* An ngrx action to be fired when the route is updated
|
||||
* Note that, contrary to the router-store.ROUTER_NAVIGATION action,
|
||||
* this action will only be fired when the path changes,
|
||||
* not when just the query parameters change
|
||||
*/
|
||||
export class RouteUpdateAction implements Action {
|
||||
type = RouterActionTypes.ROUTE_UPDATE;
|
||||
}
|
||||
|
||||
/* tslint:enable:max-classes-per-file */
|
31
src/app/core/router/router.effects.ts
Normal file
31
src/app/core/router/router.effects.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { filter, map, pairwise } from 'rxjs/operators';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Actions, Effect, ofType } from '@ngrx/effects'
|
||||
import * as fromRouter from '@ngrx/router-store';
|
||||
import { RouterNavigationAction } from '@ngrx/router-store';
|
||||
import { Router } from '@angular/router';
|
||||
import { RouteUpdateAction } from './router.actions';
|
||||
|
||||
@Injectable()
|
||||
export class RouterEffects {
|
||||
/**
|
||||
* Effect that fires a new RouteUpdateAction when then path of route is changed
|
||||
* @type {Observable<RouteUpdateAction>}
|
||||
*/
|
||||
@Effect() routeChange$ = this.actions$
|
||||
.pipe(
|
||||
ofType(fromRouter.ROUTER_NAVIGATION),
|
||||
pairwise(),
|
||||
map((actions: RouterNavigationAction[]) =>
|
||||
actions.map((navigateAction) => {
|
||||
const urlTree = this.router.parseUrl(navigateAction.payload.routerState.url);
|
||||
return urlTree.root.children.primary.segments.map((it) => it.path).join('/');
|
||||
})),
|
||||
filter((actions: string[]) => actions[0] !== actions[1]),
|
||||
map(() => new RouteUpdateAction())
|
||||
);
|
||||
|
||||
constructor(private actions$: Actions, private router: Router) {
|
||||
}
|
||||
|
||||
}
|
@@ -10,6 +10,8 @@ export const RouteActionTypes = {
|
||||
SET_PARAMETERS: type('dspace/core/route/SET_PARAMETERS'),
|
||||
ADD_QUERY_PARAMETER: type('dspace/core/route/ADD_QUERY_PARAMETER'),
|
||||
ADD_PARAMETER: type('dspace/core/route/ADD_PARAMETER'),
|
||||
SET_QUERY_PARAMETER: type('dspace/core/route/SET_QUERY_PARAMETER'),
|
||||
SET_PARAMETER: type('dspace/core/route/SET_PARAMETER'),
|
||||
RESET: type('dspace/core/route/RESET'),
|
||||
};
|
||||
|
||||
@@ -96,6 +98,52 @@ export class AddParameterAction implements Action {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An ngrx action to set a query parameter
|
||||
*/
|
||||
export class SetQueryParameterAction implements Action {
|
||||
type = RouteActionTypes.SET_QUERY_PARAMETER;
|
||||
payload: {
|
||||
key: string;
|
||||
value: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new SetQueryParameterAction
|
||||
*
|
||||
* @param key
|
||||
* the key to set
|
||||
* @param value
|
||||
* the value of this key
|
||||
*/
|
||||
constructor(key: string, value: string) {
|
||||
this.payload = { key, value };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An ngrx action to set a parameter
|
||||
*/
|
||||
export class SetParameterAction implements Action {
|
||||
type = RouteActionTypes.SET_PARAMETER;
|
||||
payload: {
|
||||
key: string;
|
||||
value: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new SetParameterAction
|
||||
*
|
||||
* @param key
|
||||
* the key to set
|
||||
* @param value
|
||||
* the value of this key
|
||||
*/
|
||||
constructor(key: string, value: string) {
|
||||
this.payload = { key, value };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An ngrx action to reset the route state
|
||||
*/
|
||||
@@ -113,4 +161,5 @@ export type RouteActions =
|
||||
| SetParametersAction
|
||||
| AddQueryParameterAction
|
||||
| AddParameterAction
|
||||
| ResetRouteStateAction;
|
||||
| ResetRouteStateAction
|
||||
| SetParameterAction;
|
||||
|
@@ -1,8 +1,9 @@
|
||||
import { map } from 'rxjs/operators';
|
||||
import { map, tap } from 'rxjs/operators';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Actions, Effect, ofType } from '@ngrx/effects'
|
||||
import * as fromRouter from '@ngrx/router-store';
|
||||
import { ResetRouteStateAction } from './route.actions';
|
||||
import { ResetRouteStateAction, RouteActionTypes } from './route.actions';
|
||||
import { RouterActionTypes } from '../../core/router/router.actions';
|
||||
import { RouteService } from './route.service';
|
||||
|
||||
@Injectable()
|
||||
export class RouteEffects {
|
||||
@@ -12,12 +13,16 @@ export class RouteEffects {
|
||||
*/
|
||||
@Effect() routeChange$ = this.actions$
|
||||
.pipe(
|
||||
ofType(fromRouter.ROUTER_NAVIGATION),
|
||||
map(() => new ResetRouteStateAction())
|
||||
ofType(RouterActionTypes.ROUTE_UPDATE),
|
||||
map(() => new ResetRouteStateAction()),
|
||||
);
|
||||
|
||||
constructor(private actions$: Actions) {
|
||||
@Effect({dispatch: false }) afterResetChange$ = this.actions$
|
||||
.pipe(
|
||||
ofType(RouteActionTypes.RESET),
|
||||
tap(() => this.service.setCurrentRouteInfo()),
|
||||
);
|
||||
|
||||
constructor(private actions$: Actions, private service: RouteService) {
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -3,7 +3,11 @@ import {
|
||||
AddParameterAction,
|
||||
AddQueryParameterAction,
|
||||
RouteActions,
|
||||
RouteActionTypes, SetParametersAction, SetQueryParametersAction
|
||||
RouteActionTypes,
|
||||
SetParameterAction,
|
||||
SetParametersAction,
|
||||
SetQueryParameterAction,
|
||||
SetQueryParametersAction
|
||||
} from './route.actions';
|
||||
|
||||
/**
|
||||
@@ -44,6 +48,12 @@ export function routeReducer(state = initialState, action: RouteActions): RouteS
|
||||
case RouteActionTypes.ADD_QUERY_PARAMETER: {
|
||||
return addParameter(state, action as AddQueryParameterAction, 'queryParams');
|
||||
}
|
||||
case RouteActionTypes.SET_PARAMETER: {
|
||||
return setParameter(state, action as SetParameterAction, 'params');
|
||||
}
|
||||
case RouteActionTypes.SET_QUERY_PARAMETER: {
|
||||
return setParameter(state, action as SetQueryParameterAction, 'queryParams');
|
||||
}
|
||||
default: {
|
||||
return state;
|
||||
}
|
||||
@@ -60,9 +70,10 @@ function addParameter(state: RouteState, action: AddParameterAction | AddQueryPa
|
||||
const subState = state[paramType];
|
||||
const existingValues = subState[action.payload.key] || [];
|
||||
const newValues = [...existingValues, action.payload.value];
|
||||
const newSubstate = Object.assign(subState, { [action.payload.key]: newValues });
|
||||
const newSubstate = Object.assign({}, subState, { [action.payload.key]: newValues });
|
||||
return Object.assign({}, state, { [paramType]: newSubstate });
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a route or query parameter in the store
|
||||
* @param state The current state
|
||||
@@ -70,5 +81,17 @@ function addParameter(state: RouteState, action: AddParameterAction | AddQueryPa
|
||||
* @param paramType The type of parameter to set: route or query parameter
|
||||
*/
|
||||
function setParameters(state: RouteState, action: SetParametersAction | SetQueryParametersAction, paramType: string): RouteState {
|
||||
return Object.assign({}, state, { [paramType]: action.payload });
|
||||
return Object.assign({}, state, { [paramType]: { [action.payload.key]: action.payload.value } });
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a route or query parameter in the store
|
||||
* @param state The current state
|
||||
* @param action The set action to perform on the current state
|
||||
* @param paramType The type of parameter to set: route or query parameter
|
||||
*/
|
||||
function setParameter(state: RouteState, action: SetParameterAction | SetQueryParameterAction, paramType: string): RouteState {
|
||||
const subState = state[paramType];
|
||||
const newSubstate = Object.assign({}, subState, { [action.payload.key]: action.payload.value });
|
||||
return Object.assign({}, state, { [paramType]: newSubstate });
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { distinctUntilChanged, filter, map, tap } from 'rxjs/operators';
|
||||
import { distinctUntilChanged, filter, map, take, tap } from 'rxjs/operators';
|
||||
import { Injectable } from '@angular/core';
|
||||
import {
|
||||
ActivatedRoute,
|
||||
@@ -12,12 +12,17 @@ import { combineLatest, Observable } from 'rxjs';
|
||||
import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store';
|
||||
import { isEqual } from 'lodash';
|
||||
|
||||
import { AddUrlToHistoryAction } from '../../shared/history/history.actions';
|
||||
import { historySelector } from '../../shared/history/selectors';
|
||||
import { SetParametersAction, SetQueryParametersAction } from './route.actions';
|
||||
import { CoreState } from '../core.reducers';
|
||||
import {
|
||||
AddParameterAction,
|
||||
SetParameterAction,
|
||||
SetParametersAction,
|
||||
SetQueryParametersAction
|
||||
} from './route.actions';
|
||||
import { CoreState } from '../../core/core.reducers';
|
||||
import { coreSelector } from '../../core/core.selectors';
|
||||
import { hasValue } from '../../shared/empty.util';
|
||||
import { coreSelector } from '../core.selectors';
|
||||
import { historySelector } from '../../shared/history/selectors';
|
||||
import { AddUrlToHistoryAction } from '../../shared/history/history.actions';
|
||||
|
||||
/**
|
||||
* Selector to select all route parameters from the store
|
||||
@@ -121,7 +126,7 @@ export class RouteService {
|
||||
}
|
||||
|
||||
getRouteDataValue(datafield: string): Observable<any> {
|
||||
return this.route.data.pipe(map((data) => data[datafield]), distinctUntilChanged(),);
|
||||
return this.route.data.pipe(map((data) => data[datafield]), distinctUntilChanged());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -157,11 +162,9 @@ export class RouteService {
|
||||
}
|
||||
|
||||
public saveRouting(): void {
|
||||
combineLatest(this.router.events, this.getRouteParams(), this.route.queryParams)
|
||||
.pipe(filter(([event, params, queryParams]) => event instanceof NavigationEnd))
|
||||
.subscribe(([event, params, queryParams]: [NavigationEnd, Params, Params]) => {
|
||||
this.store.dispatch(new SetParametersAction(params));
|
||||
this.store.dispatch(new SetQueryParametersAction(queryParams));
|
||||
this.router.events
|
||||
.pipe(filter((event) => event instanceof NavigationEnd))
|
||||
.subscribe((event: NavigationEnd) => {
|
||||
this.store.dispatch(new AddUrlToHistoryAction(event.urlAfterRedirects));
|
||||
});
|
||||
}
|
||||
@@ -183,4 +186,26 @@ export class RouteService {
|
||||
map((history: string[]) => history[history.length - 2] || '')
|
||||
);
|
||||
}
|
||||
|
||||
public addParameter(key, value) {
|
||||
this.store.dispatch(new AddParameterAction(key, value));
|
||||
}
|
||||
|
||||
public setParameter(key, value) {
|
||||
this.store.dispatch(new SetParameterAction(key, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current route parameters and query parameters in the store
|
||||
*/
|
||||
public setCurrentRouteInfo() {
|
||||
combineLatest(this.getRouteParams(), this.route.queryParams)
|
||||
.pipe(take(1))
|
||||
.subscribe(
|
||||
([params, queryParams]: [Params, Params]) => {
|
||||
this.store.dispatch(new SetParametersAction(params));
|
||||
this.store.dispatch(new SetQueryParametersAction(queryParams));
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@@ -2,12 +2,13 @@ import { ListableObject } from '../../shared/object-collection/shared/listable-o
|
||||
import { TypedObject } from '../cache/object-cache.reducer';
|
||||
import { ResourceType } from './resource-type';
|
||||
import { GenericConstructor } from './generic-constructor';
|
||||
import { excludeFromEquals } from '../utilities/equals.decorators';
|
||||
|
||||
/**
|
||||
* Class object representing a browse entry
|
||||
* This class is not normalized because browse entries do not have self links
|
||||
*/
|
||||
export class BrowseEntry implements ListableObject {
|
||||
export class BrowseEntry extends ListableObject implements TypedObject {
|
||||
static type = new ResourceType('browseEntry');
|
||||
|
||||
/**
|
||||
@@ -28,6 +29,7 @@ export class BrowseEntry implements ListableObject {
|
||||
/**
|
||||
* The count of this browse entry
|
||||
*/
|
||||
@excludeFromEquals
|
||||
count: number;
|
||||
|
||||
/**
|
||||
|
@@ -11,25 +11,29 @@ import { hasNoValue, isUndefined } from '../../shared/empty.util';
|
||||
import { CacheableObject } from '../cache/object-cache.reducer';
|
||||
import { RemoteData } from '../data/remote-data';
|
||||
import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
|
||||
import { excludeFromEquals } from '../utilities/equals.decorators';
|
||||
import { ResourceType } from './resource-type';
|
||||
import { GenericConstructor } from './generic-constructor';
|
||||
|
||||
/**
|
||||
* An abstract model class for a DSpaceObject.
|
||||
*/
|
||||
export class DSpaceObject implements CacheableObject, ListableObject {
|
||||
export class DSpaceObject extends ListableObject implements CacheableObject {
|
||||
/**
|
||||
* A string representing the kind of DSpaceObject, e.g. community, item, …
|
||||
*/
|
||||
static type = new ResourceType('dspaceobject');
|
||||
|
||||
@excludeFromEquals
|
||||
private _name: string;
|
||||
|
||||
@excludeFromEquals
|
||||
self: string;
|
||||
|
||||
/**
|
||||
* The human-readable identifier of this DSpaceObject
|
||||
*/
|
||||
@excludeFromEquals
|
||||
id: string;
|
||||
|
||||
/**
|
||||
@@ -37,6 +41,12 @@ export class DSpaceObject implements CacheableObject, ListableObject {
|
||||
*/
|
||||
uuid: string;
|
||||
|
||||
/**
|
||||
* A string representing the kind of DSpaceObject, e.g. community, item, …
|
||||
*/
|
||||
@excludeFromEquals
|
||||
type: ResourceType;
|
||||
|
||||
/**
|
||||
* The name for this DSpaceObject
|
||||
*/
|
||||
@@ -54,6 +64,7 @@ export class DSpaceObject implements CacheableObject, ListableObject {
|
||||
/**
|
||||
* All metadata of this DSpaceObject
|
||||
*/
|
||||
@excludeFromEquals
|
||||
metadata: MetadataMap;
|
||||
|
||||
/**
|
||||
@@ -66,11 +77,13 @@ export class DSpaceObject implements CacheableObject, ListableObject {
|
||||
/**
|
||||
* An array of DSpaceObjects that are direct parents of this DSpaceObject
|
||||
*/
|
||||
@excludeFromEquals
|
||||
parents: Observable<RemoteData<DSpaceObject[]>>;
|
||||
|
||||
/**
|
||||
* The DSpaceObject that owns this DSpaceObject
|
||||
*/
|
||||
@excludeFromEquals
|
||||
owner: Observable<RemoteData<DSpaceObject>>;
|
||||
|
||||
/**
|
||||
|
@@ -12,6 +12,8 @@ export class ItemType implements CacheableObject {
|
||||
*/
|
||||
id: string;
|
||||
|
||||
label: string;
|
||||
|
||||
/**
|
||||
* The link to the rest endpoint where this object can be found
|
||||
*/
|
||||
|
@@ -46,6 +46,16 @@ export class Relationship implements CacheableObject {
|
||||
*/
|
||||
rightPlace: number;
|
||||
|
||||
/**
|
||||
* The name variant of the Item to the left side of this Relationship
|
||||
*/
|
||||
leftwardValue: string;
|
||||
|
||||
/**
|
||||
* The name variant of the Item to the right side of this Relationship
|
||||
*/
|
||||
rightwardValue: string;
|
||||
|
||||
/**
|
||||
* The type of Relationship
|
||||
*/
|
||||
|
@@ -81,6 +81,9 @@ export interface MetadataValueFilter {
|
||||
/** The value constraint. */
|
||||
value?: string;
|
||||
|
||||
/** The authority constraint. */
|
||||
authority?: string;
|
||||
|
||||
/** Whether the value constraint should match without regard to case. */
|
||||
ignoreCase?: boolean;
|
||||
|
||||
|
@@ -8,8 +8,8 @@ import {
|
||||
} from './metadata.models';
|
||||
import { Metadata } from './metadata.utils';
|
||||
|
||||
const mdValue = (value: string, language?: string): MetadataValue => {
|
||||
return Object.assign(new MetadataValue(), { uuid: uuidv4(), value: value, language: isUndefined(language) ? null : language, place: 0, authority: undefined, confidence: undefined });
|
||||
const mdValue = (value: string, language?: string, authority?: string): MetadataValue => {
|
||||
return Object.assign(new MetadataValue(), { uuid: uuidv4(), value: value, language: isUndefined(language) ? null : language, place: 0, authority: isUndefined(authority) ? null : authority, confidence: undefined });
|
||||
};
|
||||
|
||||
const dcDescription = mdValue('Some description');
|
||||
@@ -184,6 +184,8 @@ describe('Metadata', () => {
|
||||
testValueMatches(mdValue('a'), true, { language: null });
|
||||
testValueMatches(mdValue('a'), false, { language: 'en_US' });
|
||||
testValueMatches(mdValue('a', 'en_US'), true, { language: 'en_US' });
|
||||
testValueMatches(mdValue('a', undefined, '4321'), true, { authority: '4321' });
|
||||
testValueMatches(mdValue('a', undefined, '4321'), false, { authority: '1234' });
|
||||
});
|
||||
|
||||
describe('toViewModelList method', () => {
|
||||
|
@@ -127,6 +127,8 @@ export class Metadata {
|
||||
return true;
|
||||
} else if (filter.language && filter.language !== mdValue.language) {
|
||||
return false;
|
||||
} else if (filter.authority && filter.authority !== mdValue.authority) {
|
||||
return false;
|
||||
} else if (filter.value) {
|
||||
let fValue = filter.value;
|
||||
let mValue = mdValue.value;
|
||||
|
@@ -9,8 +9,7 @@ import { RequestService } from '../data/request.service';
|
||||
import { BrowseDefinition } from './browse-definition.model';
|
||||
import { DSpaceObject } from './dspace-object.model';
|
||||
import { PaginatedList } from '../data/paginated-list';
|
||||
import { SearchResult } from '../../+search-page/search-result.model';
|
||||
import { Item } from './item.model';
|
||||
import { SearchResult } from '../../shared/search/search-result.model';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
/**
|
||||
|
@@ -1,9 +1,11 @@
|
||||
import { autoserialize, autoserializeAs } from 'cerialize';
|
||||
import { hasValue } from '../../shared/empty.util';
|
||||
|
||||
/**
|
||||
* Represents the state of a paginated response
|
||||
*/
|
||||
export class PageInfo {
|
||||
|
||||
/**
|
||||
* The number of elements on a page
|
||||
*/
|
||||
@@ -42,4 +44,20 @@ export class PageInfo {
|
||||
|
||||
@autoserialize
|
||||
self: string;
|
||||
|
||||
constructor(
|
||||
options?: {
|
||||
elementsPerPage: number,
|
||||
totalElements: number,
|
||||
totalPages: number,
|
||||
currentPage: number
|
||||
}
|
||||
) {
|
||||
if (hasValue(options)) {
|
||||
this.elementsPerPage = options.elementsPerPage;
|
||||
this.totalElements = options.totalElements;
|
||||
this.totalPages = options.totalPages;
|
||||
this.currentPage = options.currentPage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,9 +1,9 @@
|
||||
import { SearchConfigurationService } from './search-configuration.service';
|
||||
import { ActivatedRouteStub } from '../../shared/testing/active-router-stub';
|
||||
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
||||
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
|
||||
import { PaginatedSearchOptions } from '../paginated-search-options.model';
|
||||
import { SearchFilter } from '../search-filter.model';
|
||||
import { ActivatedRouteStub } from '../../../shared/testing/active-router-stub';
|
||||
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
||||
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';
|
||||
|
||||
describe('SearchConfigurationService', () => {
|
||||
@@ -30,14 +30,10 @@ describe('SearchConfigurationService', () => {
|
||||
getRouteParameterValue: observableOf('')
|
||||
});
|
||||
|
||||
const fixedFilterService = jasmine.createSpyObj('SearchFixedFilterService', {
|
||||
getQueryByFilterName: observableOf(''),
|
||||
});
|
||||
|
||||
const activatedRoute: any = new ActivatedRouteStub();
|
||||
|
||||
beforeEach(() => {
|
||||
service = new SearchConfigurationService(routeService, fixedFilterService, activatedRoute);
|
||||
service = new SearchConfigurationService(routeService, activatedRoute);
|
||||
});
|
||||
describe('when the scope is called', () => {
|
||||
beforeEach(() => {
|
@@ -1,27 +1,19 @@
|
||||
import { Injectable, OnDestroy } from '@angular/core';
|
||||
import { ActivatedRoute, Params } from '@angular/router';
|
||||
|
||||
import {
|
||||
BehaviorSubject,
|
||||
combineLatest as observableCombineLatest,
|
||||
merge as observableMerge,
|
||||
Observable,
|
||||
of as observableOf,
|
||||
Subscription
|
||||
} from 'rxjs';
|
||||
import { filter, flatMap, map, startWith, switchMap, tap } from 'rxjs/operators';
|
||||
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
|
||||
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
||||
import { SearchOptions } from '../search-options.model';
|
||||
import { PaginatedSearchOptions } from '../paginated-search-options.model';
|
||||
import { RouteService } from '../../core/services/route.service';
|
||||
import { hasNoValue, hasValue, isNotEmpty, isNotEmptyOperator } from '../../shared/empty.util';
|
||||
import { RemoteData } from '../../core/data/remote-data';
|
||||
import { getSucceededRemoteData } from '../../core/shared/operators';
|
||||
import { SearchFilter } from '../search-filter.model';
|
||||
import { DSpaceObjectType } from '../../core/shared/dspace-object-type.model';
|
||||
import { SearchFixedFilterService } from '../search-filters/search-filter/search-fixed-filter.service';
|
||||
import { createSuccessfulRemoteDataObject$ } from '../../shared/testing/utils';
|
||||
import { BehaviorSubject, combineLatest as observableCombineLatest, merge as observableMerge, Observable, Subscription } from 'rxjs';
|
||||
import { filter, map, startWith, switchMap } from 'rxjs/operators';
|
||||
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
||||
import { SearchOptions } from '../../../shared/search/search-options.model';
|
||||
import { PaginatedSearchOptions } from '../../../shared/search/paginated-search-options.model';
|
||||
import { SearchFilter } from '../../../shared/search/search-filter.model';
|
||||
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 { getSucceededRemoteData } from '../operators';
|
||||
import { hasNoValue, hasValue, isNotEmpty, isNotEmptyOperator } from '../../../shared/empty.util';
|
||||
import { createSuccessfulRemoteDataObject$ } from '../../../shared/testing/utils';
|
||||
|
||||
/**
|
||||
* Service that performs all actions that have to do with the current search configuration
|
||||
@@ -80,11 +72,9 @@ export class SearchConfigurationService implements OnDestroy {
|
||||
/**
|
||||
* Initialize the search options
|
||||
* @param {RouteService} routeService
|
||||
* @param {SearchFixedFilterService} fixedFilterService
|
||||
* @param {ActivatedRoute} route
|
||||
*/
|
||||
constructor(protected routeService: RouteService,
|
||||
protected fixedFilterService: SearchFixedFilterService,
|
||||
protected route: ActivatedRoute) {
|
||||
|
||||
this.initDefaults();
|
||||
@@ -96,7 +86,7 @@ export class SearchConfigurationService implements OnDestroy {
|
||||
protected initDefaults() {
|
||||
this.defaults
|
||||
.pipe(getSucceededRemoteData())
|
||||
.subscribe((defRD) => {
|
||||
.subscribe((defRD: RemoteData<PaginatedSearchOptions>) => {
|
||||
const defs = defRD.payload;
|
||||
this.paginatedSearchOptions = new BehaviorSubject<PaginatedSearchOptions>(defs);
|
||||
this.searchOptions = new BehaviorSubject<SearchOptions>(defs);
|
||||
@@ -205,6 +195,13 @@ export class SearchConfigurationService implements OnDestroy {
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Observable<string>} Emits the current fixed filter as a string
|
||||
*/
|
||||
getCurrentFixedFilter(): Observable<string> {
|
||||
return this.routeService.getRouteParameterValue('fixedFilterQuery');
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Observable<Params>} Emits the current active filters with their values as they are displayed in the frontend URL
|
||||
*/
|
||||
@@ -224,9 +221,10 @@ export class SearchConfigurationService implements OnDestroy {
|
||||
this.getQueryPart(defaults.query),
|
||||
this.getDSOTypePart(),
|
||||
this.getFiltersPart(),
|
||||
this.getFixedFilterPart()
|
||||
).subscribe((update) => {
|
||||
const currentValue: SearchOptions = this.searchOptions.getValue();
|
||||
const updatedValue: SearchOptions = Object.assign(new SearchOptions({}), currentValue, update);
|
||||
const updatedValue: SearchOptions = Object.assign(new PaginatedSearchOptions({}), currentValue, update);
|
||||
this.searchOptions.next(updatedValue);
|
||||
});
|
||||
}
|
||||
@@ -245,6 +243,7 @@ export class SearchConfigurationService implements OnDestroy {
|
||||
this.getQueryPart(defaults.query),
|
||||
this.getDSOTypePart(),
|
||||
this.getFiltersPart(),
|
||||
this.getFixedFilterPart()
|
||||
).subscribe((update) => {
|
||||
const currentValue: PaginatedSearchOptions = this.paginatedSearchOptions.getValue();
|
||||
const updatedValue: PaginatedSearchOptions = Object.assign(new PaginatedSearchOptions({}), currentValue, update);
|
||||
@@ -341,4 +340,16 @@ export class SearchConfigurationService implements OnDestroy {
|
||||
return { filters }
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Observable<string>} Emits the current fixed filter as a partial SearchOptions object
|
||||
*/
|
||||
private getFixedFilterPart(): Observable<any> {
|
||||
return this.getCurrentFixedFilter().pipe(
|
||||
isNotEmptyOperator(),
|
||||
map((fixedFilter) => {
|
||||
return { fixedFilter }
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
@@ -8,14 +8,13 @@ import {
|
||||
SearchFilterInitializeAction,
|
||||
SearchFilterResetPageAction,
|
||||
SearchFilterToggleAction
|
||||
} from './search-filter.actions';
|
||||
import { SearchFiltersState } from './search-filter.reducer';
|
||||
import { SearchFilterConfig } from '../../search-service/search-filter-config.model';
|
||||
import { FilterType } from '../../search-service/filter-type.model';
|
||||
import { SearchFixedFilterService } from './search-fixed-filter.service';
|
||||
} from '../../../shared/search/search-filters/search-filter/search-filter.actions';
|
||||
import { SearchFiltersState } from '../../../shared/search/search-filters/search-filter/search-filter.reducer';
|
||||
import { SearchFilterConfig } from '../../../shared/search/search-filter-config.model';
|
||||
import { FilterType } from '../../../shared/search/filter-type.model';
|
||||
import { ActivatedRouteStub } from '../../../shared/testing/active-router-stub';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model';
|
||||
import { SortDirection, SortOptions } from '../../cache/models/sort-options.model';
|
||||
|
||||
describe('SearchFilterService', () => {
|
||||
let service: SearchFilterService;
|
||||
@@ -28,7 +27,6 @@ describe('SearchFilterService', () => {
|
||||
pageSize: 2
|
||||
});
|
||||
|
||||
const mockFixedFilterService: SearchFixedFilterService = {} as SearchFixedFilterService
|
||||
const value1 = 'random value';
|
||||
// const value2 = 'another value';
|
||||
const store: Store<SearchFiltersState> = jasmine.createSpyObj('store', {
|
||||
@@ -66,7 +64,7 @@ describe('SearchFilterService', () => {
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
service = new SearchFilterService(store, routeServiceStub, mockFixedFilterService);
|
||||
service = new SearchFilterService(store, routeServiceStub);
|
||||
});
|
||||
|
||||
describe('when the initializeFilter method is triggered', () => {
|
@@ -1,7 +1,7 @@
|
||||
import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
|
||||
import { distinctUntilChanged, map, mergeMap } from 'rxjs/operators';
|
||||
import { Injectable, InjectionToken } from '@angular/core';
|
||||
import { SearchFiltersState, SearchFilterState } from './search-filter.reducer';
|
||||
import { SearchFiltersState, SearchFilterState } from '../../../shared/search/search-filters/search-filter/search-filter.reducer';
|
||||
import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store';
|
||||
import {
|
||||
SearchFilterCollapseAction,
|
||||
@@ -11,13 +11,12 @@ import {
|
||||
SearchFilterInitializeAction,
|
||||
SearchFilterResetPageAction,
|
||||
SearchFilterToggleAction
|
||||
} from './search-filter.actions';
|
||||
} from '../../../shared/search/search-filters/search-filter/search-filter.actions';
|
||||
import { hasValue, isNotEmpty, } from '../../../shared/empty.util';
|
||||
import { SearchFilterConfig } from '../../search-service/search-filter-config.model';
|
||||
import { SearchFilterConfig } from '../../../shared/search/search-filter-config.model';
|
||||
import { SortDirection, SortOptions } from '../../cache/models/sort-options.model';
|
||||
import { RouteService } from '../../../core/services/route.service';
|
||||
import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model';
|
||||
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
||||
import { SearchFixedFilterService } from './search-fixed-filter.service';
|
||||
import { Params } from '@angular/router';
|
||||
|
||||
const filterStateSelector = (state: SearchFiltersState) => state.searchFilter;
|
||||
@@ -32,8 +31,7 @@ export const IN_PLACE_SEARCH: InjectionToken<boolean> = new InjectionToken<boole
|
||||
export class SearchFilterService {
|
||||
|
||||
constructor(private store: Store<SearchFiltersState>,
|
||||
private routeService: RouteService,
|
||||
private fixedFilterService: SearchFixedFilterService) {
|
||||
private routeService: RouteService) {
|
||||
}
|
||||
|
||||
/**
|
@@ -5,27 +5,27 @@ import { CommonModule } from '@angular/common';
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
import { SearchService } from './search.service';
|
||||
import { RemoteDataBuildService } from '../../core/cache/builders/remote-data-build.service';
|
||||
import { Router, UrlTree } from '@angular/router';
|
||||
import { RequestService } from '../../core/data/request.service';
|
||||
import { ActivatedRouteStub } from '../../shared/testing/active-router-stub';
|
||||
import { RouterStub } from '../../shared/testing/router-stub';
|
||||
import { HALEndpointService } from '../../core/shared/hal-endpoint.service';
|
||||
import { RequestService } from '../../data/request.service';
|
||||
import { ActivatedRouteStub } from '../../../shared/testing/active-router-stub';
|
||||
import { RouterStub } from '../../../shared/testing/router-stub';
|
||||
import { HALEndpointService } from '../hal-endpoint.service';
|
||||
import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs';
|
||||
import { PaginatedSearchOptions } from '../paginated-search-options.model';
|
||||
import { RemoteData } from '../../core/data/remote-data';
|
||||
import { RequestEntry } from '../../core/data/request.reducer';
|
||||
import { getMockRequestService } from '../../shared/mocks/mock-request.service';
|
||||
import { FacetConfigSuccessResponse, SearchSuccessResponse } from '../../core/cache/response.models';
|
||||
import { SearchQueryResponse } from './search-query-response.model';
|
||||
import { SearchFilterConfig } from './search-filter-config.model';
|
||||
import { CommunityDataService } from '../../core/data/community-data.service';
|
||||
import { ViewMode } from '../../core/shared/view-mode.model';
|
||||
import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service';
|
||||
import { PaginatedSearchOptions } from '../../../shared/search/paginated-search-options.model';
|
||||
import { RemoteData } from '../../data/remote-data';
|
||||
import { RequestEntry } from '../../data/request.reducer';
|
||||
import { getMockRequestService } from '../../../shared/mocks/mock-request.service';
|
||||
import { FacetConfigSuccessResponse, SearchSuccessResponse } from '../../cache/response.models';
|
||||
import { SearchQueryResponse } from '../../../shared/search/search-query-response.model';
|
||||
import { SearchFilterConfig } from '../../../shared/search/search-filter-config.model';
|
||||
import { CommunityDataService } from '../../data/community-data.service';
|
||||
import { ViewMode } from '../view-mode.model';
|
||||
import { DSpaceObjectDataService } from '../../data/dspace-object-data.service';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { RouteService } from '../../core/services/route.service';
|
||||
import { routeServiceStub } from '../../shared/testing/route-service-stub';
|
||||
import { createSuccessfulRemoteDataObject$ } from '../../shared/testing/utils';
|
||||
import { RouteService } from '../../services/route.service';
|
||||
import { routeServiceStub } from '../../../shared/testing/route-service-stub';
|
||||
import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service';
|
||||
import { createSuccessfulRemoteDataObject$ } from '../../../shared/testing/utils';
|
||||
|
||||
@Component({ template: '' })
|
||||
class DummyComponent {
|
@@ -1,48 +1,39 @@
|
||||
import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs';
|
||||
import { Injectable, OnDestroy } from '@angular/core';
|
||||
import { NavigationExtras, PRIMARY_OUTLET, Router, UrlSegmentGroup } from '@angular/router';
|
||||
import { first, map, switchMap, take, tap } from 'rxjs/operators';
|
||||
import { RemoteDataBuildService } from '../../core/cache/builders/remote-data-build.service';
|
||||
import {
|
||||
FacetConfigSuccessResponse,
|
||||
FacetValueSuccessResponse,
|
||||
SearchSuccessResponse
|
||||
} from '../../core/cache/response.models';
|
||||
import { PaginatedList } from '../../core/data/paginated-list';
|
||||
import { ResponseParsingService } from '../../core/data/parsing.service';
|
||||
import { RemoteData } from '../../core/data/remote-data';
|
||||
import { GetRequest, RestRequest } from '../../core/data/request.models';
|
||||
import { RequestService } from '../../core/data/request.service';
|
||||
import { DSpaceObject } from '../../core/shared/dspace-object.model';
|
||||
import { GenericConstructor } from '../../core/shared/generic-constructor';
|
||||
import { HALEndpointService } from '../../core/shared/hal-endpoint.service';
|
||||
import {
|
||||
configureRequest,
|
||||
filterSuccessfulResponses,
|
||||
getResponseFromEntry,
|
||||
getSucceededRemoteData
|
||||
} from '../../core/shared/operators';
|
||||
import { URLCombiner } from '../../core/url-combiner/url-combiner';
|
||||
import { hasValue, isEmpty, isNotEmpty, isNotUndefined } from '../../shared/empty.util';
|
||||
import { NormalizedSearchResult } from '../normalized-search-result.model';
|
||||
import { SearchOptions } from '../search-options.model';
|
||||
import { SearchResult } from '../search-result.model';
|
||||
import { FacetValue } from './facet-value.model';
|
||||
import { SearchFilterConfig } from './search-filter-config.model';
|
||||
import { SearchResponseParsingService } from '../../core/data/search-response-parsing.service';
|
||||
import { SearchQueryResponse } from './search-query-response.model';
|
||||
import { PageInfo } from '../../core/shared/page-info.model';
|
||||
import { getSearchResultFor } from './search-result-element-decorator';
|
||||
import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
|
||||
import { FacetValueResponseParsingService } from '../../core/data/facet-value-response-parsing.service';
|
||||
import { FacetConfigResponseParsingService } from '../../core/data/facet-config-response-parsing.service';
|
||||
import { PaginatedSearchOptions } from '../paginated-search-options.model';
|
||||
import { Community } from '../../core/shared/community.model';
|
||||
import { CommunityDataService } from '../../core/data/community-data.service';
|
||||
import { ViewMode } from '../../core/shared/view-mode.model';
|
||||
import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service';
|
||||
import { RouteService } from '../../core/services/route.service';
|
||||
import { RequestEntry } from '../../core/data/request.reducer';
|
||||
import { NavigationExtras, Router } from '@angular/router';
|
||||
import { first, map, switchMap, tap } from 'rxjs/operators';
|
||||
import { FacetConfigSuccessResponse, FacetValueSuccessResponse, SearchSuccessResponse } from '../../cache/response.models';
|
||||
import { PaginatedList } from '../../data/paginated-list';
|
||||
import { ResponseParsingService } from '../../data/parsing.service';
|
||||
import { RemoteData } from '../../data/remote-data';
|
||||
import { GetRequest, RestRequest } from '../../data/request.models';
|
||||
import { RequestService } from '../../data/request.service';
|
||||
import { DSpaceObject } from '../dspace-object.model';
|
||||
import { GenericConstructor } from '../generic-constructor';
|
||||
import { HALEndpointService } from '../hal-endpoint.service';
|
||||
import { URLCombiner } from '../../url-combiner/url-combiner';
|
||||
import { hasValue, isEmpty, isNotEmpty, isNotUndefined } from '../../../shared/empty.util';
|
||||
import { NormalizedSearchResult } from '../../../shared/search/normalized-search-result.model';
|
||||
import { SearchOptions } from '../../../shared/search/search-options.model';
|
||||
import { SearchResult } from '../../../shared/search/search-result.model';
|
||||
import { FacetValue } from '../../../shared/search/facet-value.model';
|
||||
import { SearchFilterConfig } from '../../../shared/search/search-filter-config.model';
|
||||
import { SearchResponseParsingService } from '../../data/search-response-parsing.service';
|
||||
import { SearchQueryResponse } from '../../../shared/search/search-query-response.model';
|
||||
import { PageInfo } from '../page-info.model';
|
||||
import { getSearchResultFor } from '../../../shared/search/search-result-element-decorator';
|
||||
import { ListableObject } from '../../../shared/object-collection/shared/listable-object.model';
|
||||
import { FacetValueResponseParsingService } from '../../data/facet-value-response-parsing.service';
|
||||
import { FacetConfigResponseParsingService } from '../../data/facet-config-response-parsing.service';
|
||||
import { PaginatedSearchOptions } from '../../../shared/search/paginated-search-options.model';
|
||||
import { Community } from '../community.model';
|
||||
import { CommunityDataService } from '../../data/community-data.service';
|
||||
import { ViewMode } from '../view-mode.model';
|
||||
import { DSpaceObjectDataService } from '../../data/dspace-object-data.service';
|
||||
import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service';
|
||||
import { configureRequest, filterSuccessfulResponses, getResponseFromEntry, getSucceededRemoteData } from '../operators';
|
||||
import { RouteService } from '../../services/route.service';
|
||||
import { RequestEntry } from '../../data/request.reducer';
|
||||
|
||||
/**
|
||||
* Service that performs all general actions that have to do with the search page
|
||||
@@ -127,7 +118,7 @@ export class SearchService implements OnDestroy {
|
||||
* @returns {Observable<RequestEntry>} Emits an observable with the request entries
|
||||
*/
|
||||
searchEntries(searchOptions?: PaginatedSearchOptions, responseMsToLive?:number)
|
||||
:Observable<{searchOptions:PaginatedSearchOptions, requestEntry:RequestEntry}> {
|
||||
:Observable<{searchOptions: PaginatedSearchOptions, requestEntry: RequestEntry}> {
|
||||
|
||||
const hrefObs = this.getEndpoint(searchOptions);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user