mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 18:14:17 +00:00
Merge branch 'master' into w2p-65240_Community-and-collection-logos
Conflicts: src/app/shared/shared.module.ts
This commit is contained in:
@@ -15,7 +15,11 @@ module.exports = function (config) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var configuration = {
|
var configuration = {
|
||||||
|
client: {
|
||||||
|
jasmine: {
|
||||||
|
random: false
|
||||||
|
}
|
||||||
|
},
|
||||||
// base path that will be used to resolve all patterns (e.g. files, exclude)
|
// base path that will be used to resolve all patterns (e.g. files, exclude)
|
||||||
basePath: '',
|
basePath: '',
|
||||||
|
|
||||||
|
@@ -140,6 +140,7 @@
|
|||||||
"text-mask-core": "5.0.1",
|
"text-mask-core": "5.0.1",
|
||||||
"ts-loader": "^5.2.1",
|
"ts-loader": "^5.2.1",
|
||||||
"ts-md5": "^1.2.4",
|
"ts-md5": "^1.2.4",
|
||||||
|
"url-parse": "^1.4.7",
|
||||||
"uuid": "^3.2.1",
|
"uuid": "^3.2.1",
|
||||||
"webfontloader": "1.6.28",
|
"webfontloader": "1.6.28",
|
||||||
"webpack-cli": "^3.1.0",
|
"webpack-cli": "^3.1.0",
|
||||||
|
@@ -541,6 +541,9 @@
|
|||||||
"footer.link.duraspace": "DuraSpace",
|
"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",
|
"form.cancel": "Cancel",
|
||||||
|
|
||||||
@@ -566,6 +569,10 @@
|
|||||||
|
|
||||||
"form.loading": "Loading...",
|
"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-results": "No results found",
|
||||||
|
|
||||||
"form.no-value": "No value entered",
|
"form.no-value": "No value entered",
|
||||||
@@ -904,9 +911,9 @@
|
|||||||
|
|
||||||
"item.page.person.search.title": "Articles by this author",
|
"item.page.person.search.title": "Articles by this author",
|
||||||
|
|
||||||
"item.page.related-items.view-more": "View more",
|
"item.page.related-items.view-more": "Show {{ amount }} more",
|
||||||
|
|
||||||
"item.page.related-items.view-less": "View less",
|
"item.page.related-items.view-less": "Hide last {{ amount }}",
|
||||||
|
|
||||||
"item.page.relationships.isAuthorOfPublication": "Publications",
|
"item.page.relationships.isAuthorOfPublication": "Publications",
|
||||||
|
|
||||||
@@ -1443,6 +1450,9 @@
|
|||||||
"search.filters.applied.f.subject": "Subject",
|
"search.filters.applied.f.subject": "Subject",
|
||||||
|
|
||||||
"search.filters.applied.f.submitter": "Submitter",
|
"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",
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1611,6 +1621,69 @@
|
|||||||
"submission.general.save-later": "Save for later",
|
"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.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",
|
"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 { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||||
import { OnClickMenuItemModel } from '../../shared/menu/menu-item/models/onclick.model';
|
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 { 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 { 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 { 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';
|
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 { 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 { RouterTestingModule } from '@angular/router/testing';
|
||||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
||||||
import { SearchFormComponent } from '../../shared/search-form/search-form.component';
|
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 { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { ActivatedRouteStub } from '../../shared/testing/active-router-stub';
|
import { ActivatedRouteStub } from '../../shared/testing/active-router-stub';
|
||||||
import { RouterStub } from '../../shared/testing/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 { SearchServiceStub } from '../../shared/testing/search-service-stub';
|
||||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||||
import { NotificationsServiceStub } from '../../shared/testing/notifications-service-stub';
|
import { NotificationsServiceStub } from '../../shared/testing/notifications-service-stub';
|
||||||
import { ItemDataService } from '../../core/data/item-data.service';
|
import { ItemDataService } from '../../core/data/item-data.service';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { SharedModule } from '../../shared/shared.module';
|
|
||||||
import { Collection } from '../../core/shared/collection.model';
|
import { Collection } from '../../core/shared/collection.model';
|
||||||
import { RemoteData } from '../../core/data/remote-data';
|
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 { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
||||||
import { SortDirection, SortOptions } from '../../core/cache/models/sort-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 { HostWindowService } from '../../shared/host-window.service';
|
||||||
import { HostWindowServiceStub } from '../../shared/testing/host-window-service-stub';
|
import { HostWindowServiceStub } from '../../shared/testing/host-window-service-stub';
|
||||||
import { By } from '@angular/platform-browser';
|
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 { ObjectSelectService } from '../../shared/object-select/object-select.service';
|
||||||
import { ObjectSelectServiceStub } from '../../shared/testing/object-select-service-stub';
|
import { ObjectSelectServiceStub } from '../../shared/testing/object-select-service-stub';
|
||||||
import { VarDirective } from '../../shared/utils/var.directive';
|
import { VarDirective } from '../../shared/utils/var.directive';
|
||||||
import { Observable } from 'rxjs/internal/Observable';
|
|
||||||
import { of as observableOf, of } from 'rxjs/internal/observable/of';
|
import { of as observableOf, of } from 'rxjs/internal/observable/of';
|
||||||
import { RestResponse } from '../../core/cache/response.models';
|
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 { RouteService } from '../../core/services/route.service';
|
||||||
import { ErrorComponent } from '../../shared/error/error.component';
|
import { ErrorComponent } from '../../shared/error/error.component';
|
||||||
import { LoadingComponent } from '../../shared/loading/loading.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', () => {
|
describe('CollectionItemMapperComponent', () => {
|
||||||
let comp: CollectionItemMapperComponent;
|
let comp: CollectionItemMapperComponent;
|
||||||
@@ -135,7 +130,6 @@ describe('CollectionItemMapperComponent', () => {
|
|||||||
{ provide: HostWindowService, useValue: new HostWindowServiceStub(0) },
|
{ provide: HostWindowService, useValue: new HostWindowServiceStub(0) },
|
||||||
{ provide: ObjectSelectService, useValue: new ObjectSelectServiceStub() },
|
{ provide: ObjectSelectService, useValue: new ObjectSelectServiceStub() },
|
||||||
{ provide: RouteService, useValue: routeServiceStub },
|
{ provide: RouteService, useValue: routeServiceStub },
|
||||||
{ provide: SearchFixedFilterService, useValue: fixedFilterServiceStub }
|
|
||||||
]
|
]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
}));
|
}));
|
||||||
|
@@ -5,12 +5,9 @@ import { fadeIn, fadeInOut } from '../../shared/animations/fade';
|
|||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { RemoteData } from '../../core/data/remote-data';
|
import { RemoteData } from '../../core/data/remote-data';
|
||||||
import { Collection } from '../../core/shared/collection.model';
|
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 { 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 { 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 { DSpaceObject } from '../../core/shared/dspace-object.model';
|
||||||
import { DSpaceObjectType } from '../../core/shared/dspace-object-type.model';
|
import { DSpaceObjectType } from '../../core/shared/dspace-object-type.model';
|
||||||
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.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 { RestResponse } from '../../core/cache/response.models';
|
||||||
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
|
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
|
||||||
import { SEARCH_CONFIG_SERVICE } from '../../+my-dspace-page/my-dspace-page.component';
|
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({
|
@Component({
|
||||||
selector: 'ds-collection-item-mapper',
|
selector: 'ds-collection-item-mapper',
|
||||||
|
@@ -2,8 +2,8 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
|||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { BehaviorSubject, of as observableOf, Observable, Subject } from 'rxjs';
|
import { BehaviorSubject, of as observableOf, Observable, Subject } from 'rxjs';
|
||||||
import { filter, flatMap, map, startWith, switchMap, take, tap } from 'rxjs/operators';
|
import { filter, flatMap, map, startWith, switchMap, take, tap } from 'rxjs/operators';
|
||||||
import { PaginatedSearchOptions } from '../+search-page/paginated-search-options.model';
|
import { PaginatedSearchOptions } from '../shared/search/paginated-search-options.model';
|
||||||
import { SearchService } from '../+search-page/search-service/search.service';
|
import { SearchService } from '../core/shared/search/search.service';
|
||||||
import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model';
|
import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model';
|
||||||
import { CollectionDataService } from '../core/data/collection-data.service';
|
import { CollectionDataService } from '../core/data/collection-data.service';
|
||||||
import { PaginatedList } from '../core/data/paginated-list';
|
import { PaginatedList } from '../core/data/paginated-list';
|
||||||
|
@@ -8,9 +8,8 @@ import { CollectionPageRoutingModule } from './collection-page-routing.module';
|
|||||||
import { CreateCollectionPageComponent } from './create-collection-page/create-collection-page.component';
|
import { CreateCollectionPageComponent } from './create-collection-page/create-collection-page.component';
|
||||||
import { CollectionFormComponent } from './collection-form/collection-form.component';
|
import { CollectionFormComponent } from './collection-form/collection-form.component';
|
||||||
import { DeleteCollectionPageComponent } from './delete-collection-page/delete-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 { 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';
|
import { StatisticsModule } from '../statistics/statistics.module';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@@ -32,7 +31,6 @@ import { StatisticsModule } from '../statistics/statistics.module';
|
|||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
SearchService,
|
SearchService,
|
||||||
SearchFixedFilterService
|
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class CollectionPageModule {
|
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 { RouterTestingModule } from '@angular/router/testing';
|
||||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { ItemCollectionMapperComponent } from './item-collection-mapper.component';
|
import { ItemCollectionMapperComponent } from './item-collection-mapper.component';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
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 { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||||
import { ItemDataService } from '../../../core/data/item-data.service';
|
import { ItemDataService } from '../../../core/data/item-data.service';
|
||||||
import { RemoteData } from '../../../core/data/remote-data';
|
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 { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
||||||
import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model';
|
import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model';
|
||||||
import { RouterStub } from '../../../shared/testing/router-stub';
|
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 { PaginatedList } from '../../../core/data/paginated-list';
|
||||||
import { PageInfo } from '../../../core/shared/page-info.model';
|
import { PageInfo } from '../../../core/shared/page-info.model';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { SharedModule } from '../../../shared/shared.module';
|
|
||||||
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
||||||
import { NotificationsServiceStub } from '../../../shared/testing/notifications-service-stub';
|
import { NotificationsServiceStub } from '../../../shared/testing/notifications-service-stub';
|
||||||
import { HostWindowService } from '../../../shared/host-window.service';
|
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 { Item } from '../../../core/shared/item.model';
|
||||||
import { ObjectSelectService } from '../../../shared/object-select/object-select.service';
|
import { ObjectSelectService } from '../../../shared/object-select/object-select.service';
|
||||||
import { ObjectSelectServiceStub } from '../../../shared/testing/object-select-service-stub';
|
import { ObjectSelectServiceStub } from '../../../shared/testing/object-select-service-stub';
|
||||||
import { Observable } from 'rxjs/internal/Observable';
|
|
||||||
import { of } from 'rxjs/internal/observable/of';
|
import { of } from 'rxjs/internal/observable/of';
|
||||||
import { RestResponse } from '../../../core/cache/response.models';
|
import { RestResponse } from '../../../core/cache/response.models';
|
||||||
import { CollectionSelectComponent } from '../../../shared/object-select/collection-select/collection-select.component';
|
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 { Collection } from '../../../core/shared/collection.model';
|
||||||
import { ErrorComponent } from '../../../shared/error/error.component';
|
import { ErrorComponent } from '../../../shared/error/error.component';
|
||||||
import { LoadingComponent } from '../../../shared/loading/loading.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', () => {
|
describe('ItemCollectionMapperComponent', () => {
|
||||||
let comp: ItemCollectionMapperComponent;
|
let comp: ItemCollectionMapperComponent;
|
||||||
|
@@ -2,15 +2,12 @@ import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
|
|||||||
|
|
||||||
import { ChangeDetectionStrategy, Component, OnInit, ViewChild } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, OnInit, ViewChild } from '@angular/core';
|
||||||
import { fadeIn, fadeInOut } from '../../../shared/animations/fade';
|
import { fadeIn, fadeInOut } from '../../../shared/animations/fade';
|
||||||
import { PaginatedSearchOptions } from '../../../+search-page/paginated-search-options.model';
|
|
||||||
import { RemoteData } from '../../../core/data/remote-data';
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
import { PaginatedList } from '../../../core/data/paginated-list';
|
import { PaginatedList } from '../../../core/data/paginated-list';
|
||||||
import { Collection } from '../../../core/shared/collection.model';
|
import { Collection } from '../../../core/shared/collection.model';
|
||||||
import { Item } from '../../../core/shared/item.model';
|
import { Item } from '../../../core/shared/item.model';
|
||||||
import { getRemoteDataPayload, getSucceededRemoteData, toDSpaceObjectListRD } from '../../../core/shared/operators';
|
import { getRemoteDataPayload, getSucceededRemoteData, toDSpaceObjectListRD } from '../../../core/shared/operators';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
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 { map, startWith, switchMap, take } from 'rxjs/operators';
|
||||||
import { ItemDataService } from '../../../core/data/item-data.service';
|
import { ItemDataService } from '../../../core/data/item-data.service';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
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 { isNotEmpty } from '../../../shared/empty.util';
|
||||||
import { RestResponse } from '../../../core/cache/response.models';
|
import { RestResponse } from '../../../core/cache/response.models';
|
||||||
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
|
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({
|
@Component({
|
||||||
selector: 'ds-item-collection-mapper',
|
selector: 'ds-item-collection-mapper',
|
||||||
|
@@ -9,7 +9,6 @@ import { ActivatedRoute, Router } from '@angular/router';
|
|||||||
import { ItemMoveComponent } from './item-move.component';
|
import { ItemMoveComponent } from './item-move.component';
|
||||||
import { NotificationsServiceStub } from '../../../shared/testing/notifications-service-stub';
|
import { NotificationsServiceStub } from '../../../shared/testing/notifications-service-stub';
|
||||||
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||||
import { SearchService } from '../../../+search-page/search-service/search.service';
|
|
||||||
import { of as observableOf } from 'rxjs';
|
import { of as observableOf } from 'rxjs';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { ItemDataService } from '../../../core/data/item-data.service';
|
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 { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||||
import { RestResponse } from '../../../core/cache/response.models';
|
import { RestResponse } from '../../../core/cache/response.models';
|
||||||
import { Collection } from '../../../core/shared/collection.model';
|
import { Collection } from '../../../core/shared/collection.model';
|
||||||
|
import { SearchService } from '../../../core/shared/search/search.service';
|
||||||
|
|
||||||
describe('ItemMoveComponent', () => {
|
describe('ItemMoveComponent', () => {
|
||||||
let comp: ItemMoveComponent;
|
let comp: ItemMoveComponent;
|
||||||
|
@@ -1,12 +1,9 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { SearchService } from '../../../+search-page/search-service/search.service';
|
|
||||||
import { first, map } from 'rxjs/operators';
|
import { first, map } from 'rxjs/operators';
|
||||||
import { DSpaceObjectType } from '../../../core/shared/dspace-object-type.model';
|
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 { RemoteData } from '../../../core/data/remote-data';
|
||||||
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
||||||
import { PaginatedList } from '../../../core/data/paginated-list';
|
import { PaginatedList } from '../../../core/data/paginated-list';
|
||||||
import { SearchResult } from '../../../+search-page/search-result.model';
|
|
||||||
import { Item } from '../../../core/shared/item.model';
|
import { Item } from '../../../core/shared/item.model';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
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 { Observable, of as observableOf } from 'rxjs';
|
||||||
import { RestResponse } from '../../../core/cache/response.models';
|
import { RestResponse } from '../../../core/cache/response.models';
|
||||||
import { Collection } from '../../../core/shared/collection.model';
|
import { Collection } from '../../../core/shared/collection.model';
|
||||||
import { tap } from 'rxjs/internal/operators/tap';
|
|
||||||
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
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({
|
@Component({
|
||||||
selector: 'ds-item-move',
|
selector: 'ds-item-move',
|
||||||
|
@@ -156,7 +156,9 @@ describe('ItemRelationshipsComponent', () => {
|
|||||||
getRelatedItemsByLabel: observableOf([author1, author2]),
|
getRelatedItemsByLabel: observableOf([author1, author2]),
|
||||||
getItemRelationshipsArray: observableOf(relationships),
|
getItemRelationshipsArray: observableOf(relationships),
|
||||||
deleteRelationship: observableOf(new RestResponse(true, 200, 'OK')),
|
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 { Item } from '../../../core/shared/item.model';
|
||||||
import { FieldUpdate, FieldUpdates } from '../../../core/data/object-updates/object-updates.reducer';
|
import { FieldUpdate, FieldUpdates } from '../../../core/data/object-updates/object-updates.reducer';
|
||||||
import { Observable } from 'rxjs/internal/Observable';
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
import { filter, map, switchMap, take, tap } from 'rxjs/operators';
|
import { filter, flatMap, map, switchMap, take, tap } from 'rxjs/operators';
|
||||||
import { combineLatest as observableCombineLatest, zip as observableZip } from 'rxjs';
|
import { zip as observableZip } from 'rxjs';
|
||||||
import { AbstractItemUpdateComponent } from '../abstract-item-update/abstract-item-update.component';
|
import { AbstractItemUpdateComponent } from '../abstract-item-update/abstract-item-update.component';
|
||||||
import { ItemDataService } from '../../../core/data/item-data.service';
|
import { ItemDataService } from '../../../core/data/item-data.service';
|
||||||
import { ObjectUpdatesService } from '../../../core/data/object-updates/object-updates.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 { getSucceededRemoteData } from '../../../core/shared/operators';
|
||||||
import { RequestService } from '../../../core/data/request.service';
|
import { RequestService } from '../../../core/data/request.service';
|
||||||
import { Subscription } from 'rxjs/internal/Subscription';
|
import { Subscription } from 'rxjs/internal/Subscription';
|
||||||
import { getRelationsByRelatedItemIds } from '../../simple/item-types/shared/item-relationships-utils';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-item-relationships',
|
selector: 'ds-item-relationships',
|
||||||
@@ -65,7 +64,7 @@ export class ItemRelationshipsComponent extends AbstractItemUpdateComponent impl
|
|||||||
*/
|
*/
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
super.ngOnInit();
|
super.ngOnInit();
|
||||||
this.relationLabels$ = this.relationshipService.getItemRelationshipLabels(this.item);
|
this.relationLabels$ = this.relationshipService.getRelationshipTypeLabelsByItem(this.item);
|
||||||
this.initializeItemUpdate();
|
this.initializeItemUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,8 +112,9 @@ export class ItemRelationshipsComponent extends AbstractItemUpdateComponent impl
|
|||||||
);
|
);
|
||||||
// Get all the relationships that should be removed
|
// Get all the relationships that should be removed
|
||||||
const removedRelationships$ = removedItemIds$.pipe(
|
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
|
// Request a delete for every relationship found in the observable created above
|
||||||
removedRelationships$.pipe(
|
removedRelationships$.pipe(
|
||||||
take(1),
|
take(1),
|
||||||
|
@@ -28,6 +28,7 @@ import { MetadataValuesComponent } from './field-components/metadata-values/meta
|
|||||||
import { MetadataFieldWrapperComponent } from './field-components/metadata-field-wrapper/metadata-field-wrapper.component';
|
import { MetadataFieldWrapperComponent } from './field-components/metadata-field-wrapper/metadata-field-wrapper.component';
|
||||||
import { TabbedRelatedEntitiesSearchComponent } from './simple/related-entities/tabbed-related-entities-search/tabbed-related-entities-search.component';
|
import { TabbedRelatedEntitiesSearchComponent } from './simple/related-entities/tabbed-related-entities-search/tabbed-related-entities-search.component';
|
||||||
import { StatisticsModule } from '../statistics/statistics.module';
|
import { StatisticsModule } from '../statistics/statistics.module';
|
||||||
|
import { AbstractIncrementalListComponent } from './simple/abstract-incremental-list/abstract-incremental-list.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -57,7 +58,8 @@ import { StatisticsModule } from '../statistics/statistics.module';
|
|||||||
GenericItemPageFieldComponent,
|
GenericItemPageFieldComponent,
|
||||||
MetadataRepresentationListComponent,
|
MetadataRepresentationListComponent,
|
||||||
RelatedEntitiesSearchComponent,
|
RelatedEntitiesSearchComponent,
|
||||||
TabbedRelatedEntitiesSearchComponent
|
TabbedRelatedEntitiesSearchComponent,
|
||||||
|
AbstractIncrementalListComponent
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
ItemComponent,
|
ItemComponent,
|
||||||
|
@@ -0,0 +1,73 @@
|
|||||||
|
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||||
|
import { Subscription } from 'rxjs/internal/Subscription';
|
||||||
|
import { hasValue, isNotEmpty } from '../../../shared/empty.util';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-abstract-incremental-list',
|
||||||
|
template: ``,
|
||||||
|
})
|
||||||
|
/**
|
||||||
|
* An abstract component for displaying an incremental list of objects
|
||||||
|
*/
|
||||||
|
export class AbstractIncrementalListComponent<T> implements OnInit, OnDestroy {
|
||||||
|
/**
|
||||||
|
* The amount to increment the list by
|
||||||
|
* Define this amount in the child component overriding this component
|
||||||
|
*/
|
||||||
|
incrementBy: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All pages of objects to display as an array
|
||||||
|
*/
|
||||||
|
objects: T[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of open subscriptions
|
||||||
|
*/
|
||||||
|
subscriptions: Subscription[];
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.objects = [];
|
||||||
|
this.subscriptions = [];
|
||||||
|
this.increase();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a specific page
|
||||||
|
* > Override this method to return a specific page
|
||||||
|
* @param page The page to fetch
|
||||||
|
*/
|
||||||
|
getPage(page: number): T {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increase the amount displayed
|
||||||
|
*/
|
||||||
|
increase() {
|
||||||
|
const page = this.getPage(this.objects.length + 1);
|
||||||
|
if (hasValue(page)) {
|
||||||
|
this.objects.push(page);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrease the amount displayed
|
||||||
|
*/
|
||||||
|
decrease() {
|
||||||
|
if (this.objects.length > 1) {
|
||||||
|
this.objects.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unsubscribe from any open subscriptions
|
||||||
|
*/
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
if (isNotEmpty(this.subscriptions)) {
|
||||||
|
this.subscriptions.forEach((sub: Subscription) => {
|
||||||
|
sub.unsubscribe();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -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 { GenericItemPageFieldComponent } from '../../field-components/specific-field/generic/generic-item-page-field.component';
|
||||||
import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
|
import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
|
||||||
import { ItemDataService } from '../../../../core/data/item-data.service';
|
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 { TruncatableService } from '../../../../shared/truncatable/truncatable.service';
|
||||||
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { Item } from '../../../../core/shared/item.model';
|
import { Item } from '../../../../core/shared/item.model';
|
||||||
@@ -15,6 +14,7 @@ import { createRelationshipsObservable } from '../shared/item.component.spec';
|
|||||||
import { PublicationComponent } from './publication.component';
|
import { PublicationComponent } from './publication.component';
|
||||||
import { MetadataMap } from '../../../../core/shared/metadata.models';
|
import { MetadataMap } from '../../../../core/shared/metadata.models';
|
||||||
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/testing/utils';
|
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/testing/utils';
|
||||||
|
import { RelationshipService } from '../../../../core/data/relationship.service';
|
||||||
|
|
||||||
const mockItem: Item = Object.assign(new Item(), {
|
const mockItem: Item = Object.assign(new Item(), {
|
||||||
bundles: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
|
bundles: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
|
||||||
@@ -26,12 +26,6 @@ describe('PublicationComponent', () => {
|
|||||||
let comp: PublicationComponent;
|
let comp: PublicationComponent;
|
||||||
let fixture: ComponentFixture<PublicationComponent>;
|
let fixture: ComponentFixture<PublicationComponent>;
|
||||||
|
|
||||||
const searchFixedFilterServiceStub = {
|
|
||||||
/* tslint:disable:no-empty */
|
|
||||||
getQueryByRelations: () => {}
|
|
||||||
/* tslint:enable:no-empty */
|
|
||||||
};
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [TranslateModule.forRoot({
|
imports: [TranslateModule.forRoot({
|
||||||
@@ -43,8 +37,8 @@ describe('PublicationComponent', () => {
|
|||||||
declarations: [PublicationComponent, GenericItemPageFieldComponent, TruncatePipe],
|
declarations: [PublicationComponent, GenericItemPageFieldComponent, TruncatePipe],
|
||||||
providers: [
|
providers: [
|
||||||
{provide: ItemDataService, useValue: {}},
|
{provide: ItemDataService, useValue: {}},
|
||||||
{provide: SearchFixedFilterService, useValue: searchFixedFilterServiceStub},
|
{provide: TruncatableService, useValue: {}},
|
||||||
{provide: TruncatableService, useValue: {}}
|
{provide: RelationshipService, useValue: {}}
|
||||||
],
|
],
|
||||||
|
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
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 { Item } from '../../../../core/shared/item.model';
|
||||||
|
import { ItemComponent } from '../shared/item.component';
|
||||||
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
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
|
* Component that represents a publication Item page
|
||||||
|
@@ -1,14 +1,12 @@
|
|||||||
import { getRemoteDataPayload, getSucceededRemoteData } from '../../../../core/shared/operators';
|
import { getSucceededRemoteData } from '../../../../core/shared/operators';
|
||||||
import { hasNoValue, hasValue } from '../../../../shared/empty.util';
|
import { hasValue } from '../../../../shared/empty.util';
|
||||||
import { Observable } from 'rxjs/internal/Observable';
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
|
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 { 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 { 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 { PaginatedList } from '../../../../core/data/paginated-list';
|
||||||
|
import { RemoteData } from '../../../../core/data/remote-data';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Operator for comparing arrays using a mapping function
|
* 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 }>() =>
|
export const compareArraysUsingIds = <T extends { id: string }>() =>
|
||||||
compareArraysUsing((t: T) => hasValue(t) ? t.id : undefined);
|
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
|
* 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
|
* @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 { ChangeDetectionStrategy, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
|
import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
|
||||||
import { isNotEmpty } from '../../../../shared/empty.util';
|
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 { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model';
|
||||||
import { PaginatedList } from '../../../../core/data/paginated-list';
|
import { PaginatedList } from '../../../../core/data/paginated-list';
|
||||||
import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
|
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 { MetadataMap, MetadataValue } from '../../../../core/shared/metadata.models';
|
||||||
import { compareArraysUsing, compareArraysUsingIds } from './item-relationships-utils';
|
import { compareArraysUsing, compareArraysUsingIds } from './item-relationships-utils';
|
||||||
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/testing/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
|
* 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 comp: any;
|
||||||
let fixture: ComponentFixture<any>;
|
let fixture: ComponentFixture<any>;
|
||||||
|
|
||||||
const searchFixedFilterServiceStub = {
|
|
||||||
/* tslint:disable:no-empty */
|
|
||||||
getQueryByRelations: () => {}
|
|
||||||
/* tslint:enable:no-empty */
|
|
||||||
};
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [TranslateModule.forRoot({
|
imports: [TranslateModule.forRoot({
|
||||||
@@ -54,8 +48,8 @@ export function getItemPageFieldsTest(mockItem: Item, component) {
|
|||||||
declarations: [component, GenericItemPageFieldComponent, TruncatePipe],
|
declarations: [component, GenericItemPageFieldComponent, TruncatePipe],
|
||||||
providers: [
|
providers: [
|
||||||
{provide: ItemDataService, useValue: {}},
|
{provide: ItemDataService, useValue: {}},
|
||||||
{provide: SearchFixedFilterService, useValue: searchFixedFilterServiceStub},
|
{provide: TruncatableService, useValue: {}},
|
||||||
{provide: TruncatableService, useValue: {}}
|
{provide: RelationshipService, useValue: {}}
|
||||||
],
|
],
|
||||||
|
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
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';
|
import { Item } from '../../../../core/shared/item.model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@@ -1,11 +1,20 @@
|
|||||||
<ds-metadata-field-wrapper *ngIf="representations$ && (representations$ | async)?.length > 0" [label]="label">
|
<ds-metadata-field-wrapper [label]="label">
|
||||||
<ds-metadata-representation-loader *ngFor="let rep of (representations$ | async)"
|
<ng-container *ngFor="let objectPage of objects; let i = index">
|
||||||
|
<ng-container *ngVar="(objectPage | async) as representations">
|
||||||
|
<ds-metadata-representation-loader *ngFor="let rep of representations"
|
||||||
[mdRepresentation]="rep">
|
[mdRepresentation]="rep">
|
||||||
</ds-metadata-representation-loader>
|
</ds-metadata-representation-loader>
|
||||||
<div *ngIf="(representations$ | async)?.length < total" class="mt-2">
|
<ds-loading *ngIf="(i + 1) === objects.length && (i > 0) && (!representations || representations?.length === 0)" message="{{'loading.default' | translate}}"></ds-loading>
|
||||||
<a [routerLink]="" (click)="viewMore()">{{'item.page.related-items.view-more' | translate}}</a>
|
<div class="d-inline-block w-100 mt-2" *ngIf="(i + 1) === objects.length && representations?.length > 0">
|
||||||
|
<div *ngIf="(objects.length * incrementBy) < total" class="float-left">
|
||||||
|
<a [routerLink]="" (click)="increase()">{{'item.page.related-items.view-more' |
|
||||||
|
translate:{ amount: (total - (objects.length * incrementBy) < incrementBy) ? total - (objects.length * incrementBy) : incrementBy } }}</a>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="limit > originalLimit" class="mt-2">
|
<div *ngIf="objects.length > 1" class="float-right">
|
||||||
<a [routerLink]="" (click)="viewLess()">{{'item.page.related-items.view-less' | translate}}</a>
|
<a [routerLink]="" (click)="decrease()">{{'item.page.related-items.view-less' |
|
||||||
|
translate:{ amount: representations?.length } }}</a>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
</ng-container>
|
||||||
</ds-metadata-field-wrapper>
|
</ds-metadata-field-wrapper>
|
||||||
|
@@ -7,6 +7,8 @@ import { Item } from '../../../core/shared/item.model';
|
|||||||
import { Relationship } from '../../../core/shared/item-relationships/relationship.model';
|
import { Relationship } from '../../../core/shared/item-relationships/relationship.model';
|
||||||
import { createSuccessfulRemoteDataObject$ } from '../../../shared/testing/utils';
|
import { createSuccessfulRemoteDataObject$ } from '../../../shared/testing/utils';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { VarDirective } from '../../../shared/utils/var.directive';
|
||||||
|
import { of as observableOf } from 'rxjs';
|
||||||
|
|
||||||
const itemType = 'Person';
|
const itemType = 'Person';
|
||||||
const metadataField = 'dc.contributor.author';
|
const metadataField = 'dc.contributor.author';
|
||||||
@@ -64,7 +66,7 @@ describe('MetadataRepresentationListComponent', () => {
|
|||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [TranslateModule.forRoot()],
|
imports: [TranslateModule.forRoot()],
|
||||||
declarations: [MetadataRepresentationListComponent],
|
declarations: [MetadataRepresentationListComponent, VarDirective],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: RelationshipService, useValue: relationshipService }
|
{ provide: RelationshipService, useValue: relationshipService }
|
||||||
],
|
],
|
||||||
@@ -88,33 +90,29 @@ describe('MetadataRepresentationListComponent', () => {
|
|||||||
expect(fields.length).toBe(2);
|
expect(fields.length).toBe(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should initialize the original limit', () => {
|
it('should contain one page of items', () => {
|
||||||
expect(comp.originalLimit).toEqual(comp.limit);
|
expect(comp.objects.length).toEqual(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when viewMore is called', () => {
|
describe('when increase is called', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
comp.viewMore();
|
comp.increase();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set the limit to a high number in order to retrieve all metadata representations', () => {
|
it('should add a new page to the list', () => {
|
||||||
expect(comp.limit).toBeGreaterThanOrEqual(999);
|
expect(comp.objects.length).toEqual(2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when viewLess is called', () => {
|
describe('when decrease is called', () => {
|
||||||
let originalLimit;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
// Store the original value of limit
|
// Add a second page
|
||||||
originalLimit = comp.limit;
|
comp.objects.push(observableOf(undefined));
|
||||||
// Set limit to a random number
|
comp.decrease();
|
||||||
comp.limit = 458;
|
|
||||||
comp.viewLess();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should reset the limit to the original value', () => {
|
it('should decrease the list of pages', () => {
|
||||||
expect(comp.limit).toEqual(originalLimit);
|
expect(comp.objects.length).toEqual(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -1,16 +1,16 @@
|
|||||||
import { Component, Input, OnInit } from '@angular/core';
|
import { Component, Input } from '@angular/core';
|
||||||
import { MetadataRepresentation } from '../../../core/shared/metadata-representation/metadata-representation.model';
|
import { MetadataRepresentation } from '../../../core/shared/metadata-representation/metadata-representation.model';
|
||||||
import { Observable } from 'rxjs/internal/Observable';
|
import { combineLatest as observableCombineLatest, Observable, of as observableOf, zip as observableZip } from 'rxjs';
|
||||||
import { RemoteData } from '../../../core/data/remote-data';
|
|
||||||
import { RelationshipService } from '../../../core/data/relationship.service';
|
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 { 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 { 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 { 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 { ItemMetadataRepresentation } from '../../../core/shared/metadata-representation/item/item-metadata-representation.model';
|
||||||
|
import { AbstractIncrementalListComponent } from '../abstract-incremental-list/abstract-incremental-list.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-metadata-representation-list',
|
selector: 'ds-metadata-representation-list',
|
||||||
@@ -22,7 +22,7 @@ import { ItemMetadataRepresentation } from '../../../core/shared/metadata-repres
|
|||||||
* It expects an itemType to resolve the metadata to a an item
|
* It expects an itemType to resolve the metadata to a an item
|
||||||
* It expects a label to put on top of the list
|
* It expects a label to put on top of the list
|
||||||
*/
|
*/
|
||||||
export class MetadataRepresentationListComponent implements OnInit {
|
export class MetadataRepresentationListComponent extends AbstractIncrementalListComponent<Observable<MetadataRepresentation[]>> {
|
||||||
/**
|
/**
|
||||||
* The parent of the list of related items to display
|
* The parent of the list of related items to display
|
||||||
*/
|
*/
|
||||||
@@ -44,22 +44,11 @@ export class MetadataRepresentationListComponent implements OnInit {
|
|||||||
@Input() label: string;
|
@Input() label: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The max amount of representations to display
|
* The amount to increment the list by when clicking "view more"
|
||||||
* Defaults to 10
|
* Defaults to 10
|
||||||
* The default can optionally be overridden by providing the limit as input to the component
|
* The default can optionally be overridden by providing the limit as input to the component
|
||||||
*/
|
*/
|
||||||
@Input() limit = 10;
|
@Input() incrementBy = 10;
|
||||||
|
|
||||||
/**
|
|
||||||
* A list of metadata-representations to display
|
|
||||||
*/
|
|
||||||
representations$: Observable<MetadataRepresentation[]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The originally provided limit
|
|
||||||
* Used for resetting the limit to the original value when collapsing the list
|
|
||||||
*/
|
|
||||||
originalLimit: number;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The total amount of metadata values available
|
* The total amount of metadata values available
|
||||||
@@ -67,30 +56,28 @@ export class MetadataRepresentationListComponent implements OnInit {
|
|||||||
total: number;
|
total: number;
|
||||||
|
|
||||||
constructor(public relationshipService: RelationshipService) {
|
constructor(public relationshipService: RelationshipService) {
|
||||||
}
|
super();
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
this.originalLimit = this.limit;
|
|
||||||
this.setRepresentations();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the metadata representations
|
* Get a specific page
|
||||||
|
* @param page The page to fetch
|
||||||
*/
|
*/
|
||||||
setRepresentations() {
|
getPage(page: number): Observable<MetadataRepresentation[]> {
|
||||||
const metadata = this.parentItem.findMetadataSortedByPlace(this.metadataField);
|
const metadata = this.parentItem.findMetadataSortedByPlace(this.metadataField);
|
||||||
this.total = metadata.length;
|
this.total = metadata.length;
|
||||||
this.representations$ = this.resolveMetadataRepresentations(metadata);
|
return this.resolveMetadataRepresentations(metadata, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve a list of metadata values to a list of metadata representations
|
* Resolve a list of metadata values to a list of metadata representations
|
||||||
* @param metadata
|
* @param metadata The list of all metadata values
|
||||||
|
* @param page The page to return representations for
|
||||||
*/
|
*/
|
||||||
resolveMetadataRepresentations(metadata: MetadataValue[]): Observable<MetadataRepresentation[]> {
|
resolveMetadataRepresentations(metadata: MetadataValue[], page: number): Observable<MetadataRepresentation[]> {
|
||||||
return observableZip(
|
return observableZip(
|
||||||
...metadata
|
...metadata
|
||||||
.slice(0, this.limit)
|
.slice((this.objects.length * this.incrementBy), (this.objects.length * this.incrementBy) + this.incrementBy)
|
||||||
.map((metadatum: any) => Object.assign(new MetadataValue(), metadatum))
|
.map((metadatum: any) => Object.assign(new MetadataValue(), metadatum))
|
||||||
.map((metadatum: MetadataValue) => {
|
.map((metadatum: MetadataValue) => {
|
||||||
if (metadatum.isVirtual) {
|
if (metadatum.isVirtual) {
|
||||||
@@ -115,20 +102,4 @@ export class MetadataRepresentationListComponent implements OnInit {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Expand the list to display all metadata representations
|
|
||||||
*/
|
|
||||||
viewMore() {
|
|
||||||
this.limit = 9999;
|
|
||||||
this.setRepresentations();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Collapse the list to display the originally displayed metadata representations
|
|
||||||
*/
|
|
||||||
viewLess() {
|
|
||||||
this.limit = this.originalLimit;
|
|
||||||
this.setRepresentations();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -4,13 +4,11 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
|||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { FormsModule } from '@angular/forms';
|
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';
|
import { Item } from '../../../../core/shared/item.model';
|
||||||
|
|
||||||
describe('RelatedEntitiesSearchComponent', () => {
|
describe('RelatedEntitiesSearchComponent', () => {
|
||||||
let comp: RelatedEntitiesSearchComponent;
|
let comp: RelatedEntitiesSearchComponent;
|
||||||
let fixture: ComponentFixture<RelatedEntitiesSearchComponent>;
|
let fixture: ComponentFixture<RelatedEntitiesSearchComponent>;
|
||||||
let fixedFilterService: SearchFixedFilterService;
|
|
||||||
|
|
||||||
const mockItem = Object.assign(new Item(), {
|
const mockItem = Object.assign(new Item(), {
|
||||||
id: 'id1'
|
id: 'id1'
|
||||||
@@ -18,17 +16,11 @@ describe('RelatedEntitiesSearchComponent', () => {
|
|||||||
const mockRelationType = 'publicationsOfAuthor';
|
const mockRelationType = 'publicationsOfAuthor';
|
||||||
const mockConfiguration = 'publication';
|
const mockConfiguration = 'publication';
|
||||||
const mockFilter= `f.${mockRelationType}=${mockItem.id}`;
|
const mockFilter= `f.${mockRelationType}=${mockItem.id}`;
|
||||||
const fixedFilterServiceStub = {
|
|
||||||
getFilterByRelation: () => mockFilter
|
|
||||||
};
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [TranslateModule.forRoot(), NoopAnimationsModule, FormsModule],
|
imports: [TranslateModule.forRoot(), NoopAnimationsModule, FormsModule],
|
||||||
declarations: [RelatedEntitiesSearchComponent],
|
declarations: [RelatedEntitiesSearchComponent],
|
||||||
providers: [
|
|
||||||
{ provide: SearchFixedFilterService, useValue: fixedFilterServiceStub }
|
|
||||||
],
|
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
}));
|
}));
|
||||||
@@ -36,7 +28,6 @@ describe('RelatedEntitiesSearchComponent', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(RelatedEntitiesSearchComponent);
|
fixture = TestBed.createComponent(RelatedEntitiesSearchComponent);
|
||||||
comp = fixture.componentInstance;
|
comp = fixture.componentInstance;
|
||||||
fixedFilterService = (comp as any).fixedFilterService;
|
|
||||||
comp.relationType = mockRelationType;
|
comp.relationType = mockRelationType;
|
||||||
comp.item = mockItem;
|
comp.item = mockItem;
|
||||||
comp.configuration = mockConfiguration;
|
comp.configuration = mockConfiguration;
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
import { Component, Input, OnInit } from '@angular/core';
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { Item } from '../../../../core/shared/item.model';
|
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 { isNotEmpty } from '../../../../shared/empty.util';
|
||||||
import { of } from 'rxjs/internal/observable/of';
|
import { of } from 'rxjs/internal/observable/of';
|
||||||
|
import { getFilterByRelation } from '../../../../shared/utils/relation-query.utils';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-related-entities-search',
|
selector: 'ds-related-entities-search',
|
||||||
@@ -47,12 +47,9 @@ export class RelatedEntitiesSearchComponent implements OnInit {
|
|||||||
fixedFilter: string;
|
fixedFilter: string;
|
||||||
configuration$: Observable<string>;
|
configuration$: Observable<string>;
|
||||||
|
|
||||||
constructor(private fixedFilterService: SearchFixedFilterService) {
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
if (isNotEmpty(this.relationType) && isNotEmpty(this.item)) {
|
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)) {
|
if (isNotEmpty(this.configuration)) {
|
||||||
this.configuration$ = of(this.configuration);
|
this.configuration$ = of(this.configuration);
|
||||||
|
@@ -1,12 +1,12 @@
|
|||||||
import { Component, HostBinding, Input, OnDestroy, OnInit } from '@angular/core';
|
import { Component, Input } from '@angular/core';
|
||||||
import { Item } from '../../../core/shared/item.model';
|
import { Item } from '../../../core/shared/item.model';
|
||||||
import { Observable } from 'rxjs/internal/Observable';
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
import { RemoteData } from '../../../core/data/remote-data';
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
import { PaginatedList } from '../../../core/data/paginated-list';
|
import { PaginatedList } from '../../../core/data/paginated-list';
|
||||||
import { RelationshipService } from '../../../core/data/relationship.service';
|
|
||||||
import { FindListOptions } from '../../../core/data/request.models';
|
import { FindListOptions } from '../../../core/data/request.models';
|
||||||
import { Subscription } from 'rxjs/internal/Subscription';
|
|
||||||
import { ViewMode } from '../../../core/shared/view-mode.model';
|
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({
|
@Component({
|
||||||
selector: 'ds-related-items',
|
selector: 'ds-related-items',
|
||||||
@@ -17,7 +17,7 @@ import { ViewMode } from '../../../core/shared/view-mode.model';
|
|||||||
* This component is used for displaying relations between items
|
* This component is used for displaying relations between items
|
||||||
* It expects a parent item and relationship type, as well as a label to display on top
|
* It expects a parent item and relationship type, as well as a label to display on top
|
||||||
*/
|
*/
|
||||||
export class RelatedItemsComponent implements OnInit, OnDestroy {
|
export class RelatedItemsComponent extends AbstractIncrementalListComponent<Observable<RemoteData<PaginatedList<Item>>>> {
|
||||||
/**
|
/**
|
||||||
* The parent of the list of related items to display
|
* The parent of the list of related items to display
|
||||||
*/
|
*/
|
||||||
@@ -30,79 +30,38 @@ export class RelatedItemsComponent implements OnInit, OnDestroy {
|
|||||||
@Input() relationType: string;
|
@Input() relationType: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default options to start a search request with
|
* The amount to increment the list by when clicking "view more"
|
||||||
* Optional input, should you wish a different page size (or other options)
|
* Defaults to 5
|
||||||
|
* The default can optionally be overridden by providing the limit as input to the component
|
||||||
*/
|
*/
|
||||||
@Input() options = Object.assign(new FindListOptions(), { elementsPerPage: 5 });
|
@Input() incrementBy = 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default options to start a search request with
|
||||||
|
* Optional input
|
||||||
|
*/
|
||||||
|
@Input() options = new FindListOptions();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An i18n label to use as a title for the list (usually describes the relation)
|
* An i18n label to use as a title for the list (usually describes the relation)
|
||||||
*/
|
*/
|
||||||
@Input() label: string;
|
@Input() label: string;
|
||||||
|
|
||||||
/**
|
|
||||||
* Completely hide the component until there's at least one item visible
|
|
||||||
*/
|
|
||||||
@HostBinding('class.d-none') hidden = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The list of related items
|
|
||||||
*/
|
|
||||||
items$: Observable<RemoteData<PaginatedList<Item>>>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Search options for displaying all elements in a list
|
|
||||||
*/
|
|
||||||
allOptions = Object.assign(new FindListOptions(), { elementsPerPage: 9999 });
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The view-mode we're currently on
|
* The view-mode we're currently on
|
||||||
* @type {ElementViewMode}
|
* @type {ViewMode}
|
||||||
*/
|
*/
|
||||||
viewMode = ViewMode.ListElement;
|
viewMode = ViewMode.ListElement;
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether or not the list is currently expanded to show all related items
|
|
||||||
*/
|
|
||||||
showingAll = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Subscription on items used to update the "hidden" property of this component
|
|
||||||
*/
|
|
||||||
itemSub: Subscription;
|
|
||||||
|
|
||||||
constructor(public relationshipService: RelationshipService) {
|
constructor(public relationshipService: RelationshipService) {
|
||||||
}
|
super();
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
this.items$ = this.relationshipService.getRelatedItemsByLabel(this.parentItem, this.relationType, this.options);
|
|
||||||
this.itemSub = this.items$.subscribe((itemsRD: RemoteData<PaginatedList<Item>>) => {
|
|
||||||
this.hidden = !(itemsRD.hasSucceeded && itemsRD.payload && itemsRD.payload.page.length > 0);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expand the list to display all related items
|
* Get a specific page
|
||||||
|
* @param page The page to fetch
|
||||||
*/
|
*/
|
||||||
viewMore() {
|
getPage(page: number): Observable<RemoteData<PaginatedList<Item>>> {
|
||||||
this.items$ = this.relationshipService.getRelatedItemsByLabel(this.parentItem, this.relationType, this.allOptions);
|
return this.relationshipService.getRelatedItemsByLabel(this.parentItem, this.relationType, Object.assign(this.options, { elementsPerPage: this.incrementBy, currentPage: page }));
|
||||||
this.showingAll = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Collapse the list to display the originally displayed items
|
|
||||||
*/
|
|
||||||
viewLess() {
|
|
||||||
this.items$ = this.relationshipService.getRelatedItemsByLabel(this.parentItem, this.relationType, this.options);
|
|
||||||
this.showingAll = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unsubscribe from the item subscription
|
|
||||||
*/
|
|
||||||
ngOnDestroy(): void {
|
|
||||||
if (this.itemSub) {
|
|
||||||
this.itemSub.unsubscribe();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,11 +1,20 @@
|
|||||||
<ds-metadata-field-wrapper *ngIf="(items$ | async)?.payload?.page?.length > 0" [label]="label">
|
<ds-metadata-field-wrapper [label]="label">
|
||||||
<ds-listable-object-component-loader *ngFor="let item of (items$ | async)?.payload?.page"
|
<ng-container *ngFor="let objectPage of objects; let i = index">
|
||||||
|
<ng-container *ngVar="(objectPage | async) as itemsRD">
|
||||||
|
<ds-listable-object-component-loader *ngFor="let item of itemsRD?.payload?.page"
|
||||||
[object]="item" [viewMode]="viewMode">
|
[object]="item" [viewMode]="viewMode">
|
||||||
</ds-listable-object-component-loader>
|
</ds-listable-object-component-loader>
|
||||||
<div *ngIf="(items$ | async)?.payload?.page?.length < (items$ | async)?.payload?.totalElements" class="mt-2" id="view-more">
|
<ds-loading *ngIf="(i + 1) === objects.length && (itemsRD || i > 0) && !(itemsRD?.hasSucceeded && itemsRD?.payload && itemsRD?.payload?.page?.length > 0)" message="{{'loading.default' | translate}}"></ds-loading>
|
||||||
<a [routerLink]="" (click)="viewMore()">{{'item.page.related-items.view-more' | translate}}</a>
|
<div class="d-inline-block w-100 mt-2" *ngIf="(i + 1) === objects.length && itemsRD?.payload?.page?.length > 0">
|
||||||
|
<div *ngIf="itemsRD?.payload?.totalPages > objects.length" class="float-left" id="view-more">
|
||||||
|
<a [routerLink]="" (click)="increase()">{{'item.page.related-items.view-more' |
|
||||||
|
translate:{ amount: (itemsRD?.payload?.totalElements - (incrementBy * objects.length) < incrementBy) ? itemsRD?.payload?.totalElements - (incrementBy * objects.length) : incrementBy } }}</a>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="showingAll" class="mt-2" id="view-less">
|
<div *ngIf="objects.length > 1" class="float-right" id="view-less">
|
||||||
<a [routerLink]="" (click)="viewLess()">{{'item.page.related-items.view-less' | translate}}</a>
|
<a [routerLink]="" (click)="decrease()">{{'item.page.related-items.view-less' |
|
||||||
|
translate:{ amount: itemsRD?.payload?.page?.length } }}</a>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
</ng-container>
|
||||||
</ds-metadata-field-wrapper>
|
</ds-metadata-field-wrapper>
|
||||||
|
@@ -9,6 +9,8 @@ import { createRelationshipsObservable } from '../item-types/shared/item.compone
|
|||||||
import { createSuccessfulRemoteDataObject$ } from '../../../shared/testing/utils';
|
import { createSuccessfulRemoteDataObject$ } from '../../../shared/testing/utils';
|
||||||
import { RelationshipService } from '../../../core/data/relationship.service';
|
import { RelationshipService } from '../../../core/data/relationship.service';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { VarDirective } from '../../../shared/utils/var.directive';
|
||||||
|
import { of as observableOf } from 'rxjs';
|
||||||
|
|
||||||
const parentItem: Item = Object.assign(new Item(), {
|
const parentItem: Item = Object.assign(new Item(), {
|
||||||
bundles: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
|
bundles: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
|
||||||
@@ -42,7 +44,7 @@ describe('RelatedItemsComponent', () => {
|
|||||||
|
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [TranslateModule.forRoot()],
|
imports: [TranslateModule.forRoot()],
|
||||||
declarations: [RelatedItemsComponent],
|
declarations: [RelatedItemsComponent, VarDirective],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: RelationshipService, useValue: relationshipService }
|
{ provide: RelationshipService, useValue: relationshipService }
|
||||||
],
|
],
|
||||||
@@ -65,31 +67,33 @@ describe('RelatedItemsComponent', () => {
|
|||||||
expect(fields.length).toBe(mockItems.length);
|
expect(fields.length).toBe(mockItems.length);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when viewMore is called', () => {
|
it('should contain one page of items', () => {
|
||||||
|
expect(comp.objects.length).toEqual(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when increase is called', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
comp.viewMore();
|
comp.increase();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call relationship-service\'s getRelatedItemsByLabel with the correct arguments', () => {
|
it('should add a new page to the list', () => {
|
||||||
expect(relationshipService.getRelatedItemsByLabel).toHaveBeenCalledWith(parentItem, relationType, comp.allOptions);
|
expect(comp.objects.length).toEqual(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set showingAll to true', () => {
|
it('should call relationship-service\'s getRelatedItemsByLabel with the correct arguments (second page)', () => {
|
||||||
expect(comp.showingAll).toEqual(true);
|
expect(relationshipService.getRelatedItemsByLabel).toHaveBeenCalledWith(parentItem, relationType, Object.assign(comp.options, { elementsPerPage: comp.incrementBy, currentPage: 2 }));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when viewLess is called', () => {
|
describe('when decrease is called', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
comp.viewLess();
|
// Add a second page
|
||||||
|
comp.objects.push(observableOf(undefined));
|
||||||
|
comp.decrease();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call relationship-service\'s getRelatedItemsByLabel with the correct arguments', () => {
|
it('should decrease the list of pages', () => {
|
||||||
expect(relationshipService.getRelatedItemsByLabel).toHaveBeenCalledWith(parentItem, relationType, comp.options);
|
expect(comp.objects.length).toEqual(1);
|
||||||
});
|
|
||||||
|
|
||||||
it('should set showingAll to false', () => {
|
|
||||||
expect(comp.showingAll).toEqual(false);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
import { of as observableOf } from 'rxjs';
|
import { of as observableOf } from 'rxjs';
|
||||||
|
|
||||||
import { MyDSpaceConfigurationService } from './my-dspace-configuration.service';
|
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 { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
|
||||||
import { SortDirection, SortOptions } from '../core/cache/models/sort-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 { ActivatedRouteStub } from '../shared/testing/active-router-stub';
|
||||||
import { MockRoleService } from '../shared/mocks/mock-role-service';
|
import { MockRoleService } from '../shared/mocks/mock-role-service';
|
||||||
import { cold, hot } from 'jasmine-marbles';
|
import { cold, hot } from 'jasmine-marbles';
|
||||||
@@ -38,12 +38,8 @@ describe('MyDSpaceConfigurationService', () => {
|
|||||||
|
|
||||||
const roleService: any = new MockRoleService();
|
const roleService: any = new MockRoleService();
|
||||||
|
|
||||||
const fixedFilterService = jasmine.createSpyObj('SearchFixedFilterService', {
|
|
||||||
getQueryByFilterName: observableOf(''),
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
service = new MyDSpaceConfigurationService(roleService, fixedFilterService, spy, activatedRoute);
|
service = new MyDSpaceConfigurationService(roleService, spy, activatedRoute);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when the scope is called', () => {
|
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 { MyDSpaceConfigurationValueType } from './my-dspace-configuration-value-type';
|
||||||
import { RoleService } from '../core/roles/role.service';
|
import { RoleService } from '../core/roles/role.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 { SearchConfigurationService } from '../+search-page/search-service/search-configuration.service';
|
import { SearchConfigurationService } from '../core/shared/search/search-configuration.service';
|
||||||
import { RouteService } from '../core/services/route.service';
|
|
||||||
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
|
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
|
||||||
import { SortDirection, SortOptions } from '../core/cache/models/sort-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
|
* 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
|
* Initialize class
|
||||||
*
|
*
|
||||||
* @param {roleService} roleService
|
* @param {roleService} roleService
|
||||||
* @param {SearchFixedFilterService} fixedFilterService
|
|
||||||
* @param {RouteService} routeService
|
* @param {RouteService} routeService
|
||||||
* @param {ActivatedRoute} route
|
* @param {ActivatedRoute} route
|
||||||
*/
|
*/
|
||||||
constructor(protected roleService: RoleService,
|
constructor(protected roleService: RoleService,
|
||||||
protected fixedFilterService: SearchFixedFilterService,
|
|
||||||
protected routeService: RouteService,
|
protected routeService: RouteService,
|
||||||
protected route: ActivatedRoute) {
|
protected route: ActivatedRoute) {
|
||||||
|
|
||||||
super(routeService, fixedFilterService, route);
|
super(routeService, route);
|
||||||
|
|
||||||
// override parent class initialization
|
// override parent class initialization
|
||||||
this._defaults = null;
|
this._defaults = null;
|
||||||
|
@@ -14,7 +14,7 @@ import { UploaderOptions } from '../../shared/uploader/uploader-options.model';
|
|||||||
import { HALEndpointService } from '../../core/shared/hal-endpoint.service';
|
import { HALEndpointService } from '../../core/shared/hal-endpoint.service';
|
||||||
import { NotificationType } from '../../shared/notifications/models/notification-type';
|
import { NotificationType } from '../../shared/notifications/models/notification-type';
|
||||||
import { hasValue } from '../../shared/empty.util';
|
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
|
* 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 { RouteService } from '../core/services/route.service';
|
||||||
import { routeServiceStub } from '../shared/testing/route-service-stub';
|
import { routeServiceStub } from '../shared/testing/route-service-stub';
|
||||||
import { SearchConfigurationServiceStub } from '../shared/testing/search-configuration-service-stub';
|
import { SearchConfigurationServiceStub } from '../shared/testing/search-configuration-service-stub';
|
||||||
import { SearchService } from '../+search-page/search-service/search.service';
|
import { SearchService } from '../core/shared/search/search.service';
|
||||||
import { SearchConfigurationService } from '../+search-page/search-service/search-configuration.service';
|
import { SearchConfigurationService } from '../core/shared/search/search-configuration.service';
|
||||||
import { PaginatedSearchOptions } from '../+search-page/paginated-search-options.model';
|
import { PaginatedSearchOptions } from '../shared/search/paginated-search-options.model';
|
||||||
import { SidebarService } from '../shared/sidebar/sidebar.service';
|
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 { RoleDirective } from '../shared/roles/role.directive';
|
||||||
import { RoleService } from '../core/roles/role.service';
|
import { RoleService } from '../core/roles/role.service';
|
||||||
import { MockRoleService } from '../shared/mocks/mock-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';
|
import { createSuccessfulRemoteDataObject$ } from '../shared/testing/utils';
|
||||||
|
|
||||||
describe('MyDSpacePageComponent', () => {
|
describe('MyDSpacePageComponent', () => {
|
||||||
@@ -82,8 +81,6 @@ describe('MyDSpacePageComponent', () => {
|
|||||||
collapse: () => this.isCollapsed = observableOf(true),
|
collapse: () => this.isCollapsed = observableOf(true),
|
||||||
expand: () => this.isCollapsed = observableOf(false)
|
expand: () => this.isCollapsed = observableOf(false)
|
||||||
};
|
};
|
||||||
const mockFixedFilterService: SearchFixedFilterService = {
|
|
||||||
} as SearchFixedFilterService;
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
@@ -123,10 +120,6 @@ describe('MyDSpacePageComponent', () => {
|
|||||||
provide: RoleService,
|
provide: RoleService,
|
||||||
useValue: new MockRoleService()
|
useValue: new MockRoleService()
|
||||||
},
|
},
|
||||||
{
|
|
||||||
provide: SearchFixedFilterService,
|
|
||||||
useValue: mockFixedFilterService
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).overrideComponent(MyDSpacePageComponent, {
|
}).overrideComponent(MyDSpacePageComponent, {
|
||||||
|
@@ -15,19 +15,19 @@ import { RemoteData } from '../core/data/remote-data';
|
|||||||
import { DSpaceObject } from '../core/shared/dspace-object.model';
|
import { DSpaceObject } from '../core/shared/dspace-object.model';
|
||||||
import { pushInOut } from '../shared/animations/push';
|
import { pushInOut } from '../shared/animations/push';
|
||||||
import { HostWindowService } from '../shared/host-window.service';
|
import { HostWindowService } from '../shared/host-window.service';
|
||||||
import { PaginatedSearchOptions } from '../+search-page/paginated-search-options.model';
|
import { PaginatedSearchOptions } from '../shared/search/paginated-search-options.model';
|
||||||
import { SearchService } from '../+search-page/search-service/search.service';
|
import { SearchService } from '../core/shared/search/search.service';
|
||||||
import { SidebarService } from '../shared/sidebar/sidebar.service';
|
import { SidebarService } from '../shared/sidebar/sidebar.service';
|
||||||
import { hasValue } from '../shared/empty.util';
|
import { hasValue } from '../shared/empty.util';
|
||||||
import { getSucceededRemoteData } from '../core/shared/operators';
|
import { getSucceededRemoteData } from '../core/shared/operators';
|
||||||
import { MyDSpaceResponseParsingService } from '../core/data/mydspace-response-parsing.service';
|
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 { 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 { MyDSpaceConfigurationService } from './my-dspace-configuration.service';
|
||||||
import { ViewMode } from '../core/shared/view-mode.model';
|
import { ViewMode } from '../core/shared/view-mode.model';
|
||||||
import { MyDSpaceRequest } from '../core/data/request.models';
|
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';
|
import { Context } from '../core/shared/context.model';
|
||||||
|
|
||||||
export const MYDSPACE_ROUTE = '/mydspace';
|
export const MYDSPACE_ROUTE = '/mydspace';
|
||||||
|
@@ -5,7 +5,6 @@ import { SharedModule } from '../shared/shared.module';
|
|||||||
|
|
||||||
import { MyDspacePageRoutingModule } from './my-dspace-page-routing.module';
|
import { MyDspacePageRoutingModule } from './my-dspace-page-routing.module';
|
||||||
import { MyDSpacePageComponent } from './my-dspace-page.component';
|
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 { 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 { 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';
|
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,
|
CommonModule,
|
||||||
SharedModule,
|
SharedModule,
|
||||||
MyDspacePageRoutingModule,
|
MyDspacePageRoutingModule,
|
||||||
SearchPageModule
|
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
MyDSpacePageComponent,
|
MyDSpacePageComponent,
|
||||||
|
@@ -2,12 +2,12 @@ import { Component, Input } from '@angular/core';
|
|||||||
import { RemoteData } from '../../core/data/remote-data';
|
import { RemoteData } from '../../core/data/remote-data';
|
||||||
import { DSpaceObject } from '../../core/shared/dspace-object.model';
|
import { DSpaceObject } from '../../core/shared/dspace-object.model';
|
||||||
import { fadeIn, fadeInOut } from '../../shared/animations/fade';
|
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 { PaginatedList } from '../../core/data/paginated-list';
|
||||||
import { ViewMode } from '../../core/shared/view-mode.model';
|
import { ViewMode } from '../../core/shared/view-mode.model';
|
||||||
import { isEmpty } from '../../shared/empty.util';
|
import { isEmpty } from '../../shared/empty.util';
|
||||||
import { SearchResult } from '../../+search-page/search-result.model';
|
|
||||||
import { Context } from '../../core/shared/context.model';
|
import { Context } from '../../core/shared/context.model';
|
||||||
|
import { SearchResult } from '../../shared/search/search-result.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that represents all results for mydspace page
|
* Component that represents all results for mydspace page
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { configureSearchComponentTestingModule } from './search.component.spec';
|
import { configureSearchComponentTestingModule } from './search.component.spec';
|
||||||
import { SearchConfigurationService } from './search-service/search-configuration.service';
|
|
||||||
import { ConfigurationSearchPageComponent } from './configuration-search-page.component';
|
import { ConfigurationSearchPageComponent } from './configuration-search-page.component';
|
||||||
|
import { SearchConfigurationService } from '../core/shared/search/search-configuration.service';
|
||||||
|
|
||||||
describe('ConfigurationSearchPageComponent', () => {
|
describe('ConfigurationSearchPageComponent', () => {
|
||||||
let comp: ConfigurationSearchPageComponent;
|
let comp: ConfigurationSearchPageComponent;
|
||||||
|
@@ -1,15 +1,14 @@
|
|||||||
import { HostWindowService } from '../shared/host-window.service';
|
import { HostWindowService } from '../shared/host-window.service';
|
||||||
import { SearchService } from './search-service/search.service';
|
|
||||||
import { SidebarService } from '../shared/sidebar/sidebar.service';
|
import { SidebarService } from '../shared/sidebar/sidebar.service';
|
||||||
import { SearchComponent } from './search.component';
|
import { SearchComponent } from './search.component';
|
||||||
import { ChangeDetectionStrategy, Component, Inject, Input, OnInit } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, Inject, Input, OnInit } from '@angular/core';
|
||||||
import { pushInOut } from '../shared/animations/push';
|
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 { 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 { 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.
|
* 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 sidebarService: SidebarService,
|
||||||
protected windowService: HostWindowService,
|
protected windowService: HostWindowService,
|
||||||
@Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService,
|
@Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService,
|
||||||
protected routeService: RouteService) {
|
protected routeService: RouteService,
|
||||||
super(service, sidebarService, windowService, searchConfigService, routeService);
|
protected router: Router) {
|
||||||
|
super(service, sidebarService, windowService, searchConfigService, routeService, router);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -58,24 +58,8 @@ export class ConfigurationSearchPageComponent extends SearchComponent implements
|
|||||||
*/
|
*/
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
super.ngOnInit();
|
super.ngOnInit();
|
||||||
}
|
if (hasValue(this.configuration)) {
|
||||||
|
this.routeService.setParameter('configuration', this.configuration);
|
||||||
/**
|
}
|
||||||
* 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
|
|
||||||
});
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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 { NgModule } from '@angular/core';
|
||||||
import { RouterModule } from '@angular/router';
|
import { RouterModule } from '@angular/router';
|
||||||
|
|
||||||
import { SearchComponent } from './search.component';
|
|
||||||
import { ConfigurationSearchPageGuard } from './configuration-search-page.guard';
|
import { ConfigurationSearchPageGuard } from './configuration-search-page.guard';
|
||||||
import { ConfigurationSearchPageComponent } from './configuration-search-page.component';
|
import { ConfigurationSearchPageComponent } from './configuration-search-page.component';
|
||||||
import { SearchPageComponent } from './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 { CoreModule } from '../core/core.module';
|
||||||
import { SharedModule } from '../shared/shared.module';
|
import { SharedModule } from '../shared/shared.module';
|
||||||
import { SearchPageRoutingModule } from './search-page-routing.module';
|
import { SearchPageRoutingModule } from './search-page-routing.module';
|
||||||
import { SearchComponent } from './search.component';
|
import { SearchPageComponent } from './search-page.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 { ConfigurationSearchPageComponent } from './configuration-search-page.component';
|
import { ConfigurationSearchPageComponent } from './configuration-search-page.component';
|
||||||
import { ConfigurationSearchPageGuard } from './configuration-search-page.guard';
|
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';
|
import { SearchTrackerComponent } from './search-tracker.component';
|
||||||
|
import { StatisticsModule } from '../statistics/statistics.module';
|
||||||
const effects = [
|
import { SearchComponent } from './search.component';
|
||||||
SidebarEffects
|
|
||||||
];
|
|
||||||
|
|
||||||
const components = [
|
const components = [
|
||||||
SearchPageComponent,
|
SearchPageComponent,
|
||||||
SearchComponent,
|
SearchComponent,
|
||||||
SearchResultsComponent,
|
|
||||||
SearchSidebarComponent,
|
|
||||||
SearchSettingsComponent,
|
|
||||||
SearchFiltersComponent,
|
|
||||||
SearchFilterComponent,
|
|
||||||
SearchFacetFilterComponent,
|
|
||||||
SearchLabelsComponent,
|
|
||||||
SearchLabelComponent,
|
|
||||||
SearchFacetFilterComponent,
|
|
||||||
SearchFacetFilterWrapperComponent,
|
|
||||||
SearchRangeFilterComponent,
|
|
||||||
SearchTextFilterComponent,
|
|
||||||
SearchHierarchyFilterComponent,
|
|
||||||
SearchBooleanFilterComponent,
|
|
||||||
SearchFacetOptionComponent,
|
|
||||||
SearchFacetSelectedOptionComponent,
|
|
||||||
SearchFacetRangeOptionComponent,
|
|
||||||
SearchSwitchConfigurationComponent,
|
|
||||||
SearchAuthorityFilterComponent,
|
|
||||||
ConfigurationSearchPageComponent,
|
ConfigurationSearchPageComponent,
|
||||||
SearchTrackerComponent,
|
SearchTrackerComponent
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@@ -72,30 +22,11 @@ const components = [
|
|||||||
SearchPageRoutingModule,
|
SearchPageRoutingModule,
|
||||||
CommonModule,
|
CommonModule,
|
||||||
SharedModule,
|
SharedModule,
|
||||||
EffectsModule.forFeature(effects),
|
|
||||||
CoreModule.forRoot(),
|
CoreModule.forRoot(),
|
||||||
StatisticsModule.forRoot(),
|
StatisticsModule.forRoot(),
|
||||||
],
|
],
|
||||||
|
providers: [ConfigurationSearchPageGuard],
|
||||||
declarations: components,
|
declarations: components,
|
||||||
providers: [
|
|
||||||
SidebarService,
|
|
||||||
SidebarFilterService,
|
|
||||||
SearchFilterService,
|
|
||||||
SearchFixedFilterService,
|
|
||||||
ConfigurationSearchPageGuard,
|
|
||||||
SearchConfigurationService
|
|
||||||
],
|
|
||||||
entryComponents: [
|
|
||||||
SearchFacetFilterComponent,
|
|
||||||
SearchRangeFilterComponent,
|
|
||||||
SearchTextFilterComponent,
|
|
||||||
SearchHierarchyFilterComponent,
|
|
||||||
SearchBooleanFilterComponent,
|
|
||||||
SearchFacetOptionComponent,
|
|
||||||
SearchFacetSelectedOptionComponent,
|
|
||||||
SearchFacetRangeOptionComponent,
|
|
||||||
SearchAuthorityFilterComponent
|
|
||||||
],
|
|
||||||
exports: components
|
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 { Angulartics2 } from 'angulartics2';
|
||||||
import { filter, map, switchMap } from 'rxjs/operators';
|
import { filter, map, switchMap } from 'rxjs/operators';
|
||||||
import { SearchComponent } from './search.component';
|
import { SearchComponent } from './search.component';
|
||||||
import { SearchService } from './search-service/search.service';
|
|
||||||
import { SidebarService } from '../shared/sidebar/sidebar.service';
|
import { SidebarService } from '../shared/sidebar/sidebar.service';
|
||||||
import { SearchConfigurationService } from './search-service/search-configuration.service';
|
|
||||||
import { HostWindowService } from '../shared/host-window.service';
|
import { HostWindowService } from '../shared/host-window.service';
|
||||||
import { SEARCH_CONFIG_SERVICE } from '../+my-dspace-page/my-dspace-page.component';
|
import { SEARCH_CONFIG_SERVICE } from '../+my-dspace-page/my-dspace-page.component';
|
||||||
import { RouteService } from '../core/services/route.service';
|
import { RouteService } from '../core/services/route.service';
|
||||||
import { hasValue } from '../shared/empty.util';
|
import { hasValue } from '../shared/empty.util';
|
||||||
import { SearchQueryResponse } from './search-service/search-query-response.model';
|
|
||||||
import { SearchSuccessResponse } from '../core/cache/response.models';
|
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
|
* This component triggers a page view statistic
|
||||||
@@ -35,9 +36,10 @@ export class SearchTrackerComponent extends SearchComponent implements OnInit {
|
|||||||
protected windowService: HostWindowService,
|
protected windowService: HostWindowService,
|
||||||
@Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService,
|
@Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService,
|
||||||
protected routeService: RouteService,
|
protected routeService: RouteService,
|
||||||
public angulartics2:Angulartics2
|
public angulartics2: Angulartics2,
|
||||||
|
protected router: Router
|
||||||
) {
|
) {
|
||||||
super(service, sidebarService, windowService, searchConfigService, routeService);
|
super(service, sidebarService, windowService, searchConfigService, routeService, router);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit():void {
|
ngOnInit():void {
|
||||||
|
@@ -46,5 +46,9 @@
|
|||||||
[scopes]="(scopeListRD$ | async)"
|
[scopes]="(scopeListRD$ | async)"
|
||||||
[inPlaceSearch]="inPlaceSearch">
|
[inPlaceSearch]="inPlaceSearch">
|
||||||
</ds-search-form>
|
</ds-search-form>
|
||||||
|
<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>
|
<ds-search-labels *ngIf="searchEnabled" [inPlaceSearch]="inPlaceSearch"></ds-search-labels>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
@@ -11,21 +11,19 @@ import { CommunityDataService } from '../core/data/community-data.service';
|
|||||||
import { HostWindowService } from '../shared/host-window.service';
|
import { HostWindowService } from '../shared/host-window.service';
|
||||||
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
|
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
|
||||||
import { SearchComponent } from './search.component';
|
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 { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
import { NgbCollapseModule } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbCollapseModule } from '@ng-bootstrap/ng-bootstrap';
|
||||||
import { SidebarService } from '../shared/sidebar/sidebar.service';
|
import { SidebarService } from '../shared/sidebar/sidebar.service';
|
||||||
import { SearchFilterService } from './search-filters/search-filter/search-filter.service';
|
import { SearchFilterService } from '../core/shared/search/search-filter.service';
|
||||||
import { SearchConfigurationService } from './search-service/search-configuration.service';
|
import { SearchConfigurationService } from '../core/shared/search/search-configuration.service';
|
||||||
import { RemoteData } from '../core/data/remote-data';
|
|
||||||
import { SEARCH_CONFIG_SERVICE } from '../+my-dspace-page/my-dspace-page.component';
|
import { SEARCH_CONFIG_SERVICE } from '../+my-dspace-page/my-dspace-page.component';
|
||||||
import { RouteService } from '../core/services/route.service';
|
import { RouteService } from '../core/services/route.service';
|
||||||
import { SearchConfigurationServiceStub } from '../shared/testing/search-configuration-service-stub';
|
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 { createSuccessfulRemoteDataObject$ } from '../shared/testing/utils';
|
||||||
|
import { PaginatedSearchOptions } from '../shared/search/paginated-search-options.model';
|
||||||
|
|
||||||
let comp: SearchComponent;
|
let comp: SearchComponent;
|
||||||
let fixture: ComponentFixture<SearchComponent>;
|
let fixture: ComponentFixture<SearchComponent>;
|
||||||
@@ -89,7 +87,6 @@ const routeServiceStub = {
|
|||||||
return observableOf('')
|
return observableOf('')
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const mockFixedFilterService: SearchFixedFilterService = {} as SearchFixedFilterService;
|
|
||||||
|
|
||||||
export function configureSearchComponentTestingModule(compType) {
|
export function configureSearchComponentTestingModule(compType) {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
@@ -122,10 +119,6 @@ export function configureSearchComponentTestingModule(compType) {
|
|||||||
provide: SearchFilterService,
|
provide: SearchFilterService,
|
||||||
useValue: {}
|
useValue: {}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
provide: SearchFixedFilterService,
|
|
||||||
useValue: mockFixedFilterService
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
provide: SearchConfigurationService,
|
provide: SearchConfigurationService,
|
||||||
useValue: {
|
useValue: {
|
||||||
@@ -158,6 +151,7 @@ describe('SearchComponent', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(SearchComponent);
|
fixture = TestBed.createComponent(SearchComponent);
|
||||||
comp = fixture.componentInstance; // SearchComponent test instance
|
comp = fixture.componentInstance; // SearchComponent test instance
|
||||||
|
comp.inPlaceSearch = false;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
searchServiceObject = (comp as any).service;
|
searchServiceObject = (comp as any).service;
|
||||||
searchConfigurationServiceObject = (comp as any).searchConfigService;
|
searchConfigurationServiceObject = (comp as any).searchConfigService;
|
||||||
|
@@ -1,20 +1,22 @@
|
|||||||
import { ChangeDetectionStrategy, Component, Inject, Input, OnInit } from '@angular/core';
|
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 { startWith, switchMap, } from 'rxjs/operators';
|
||||||
import { PaginatedList } from '../core/data/paginated-list';
|
import { PaginatedList } from '../core/data/paginated-list';
|
||||||
import { RemoteData } from '../core/data/remote-data';
|
import { RemoteData } from '../core/data/remote-data';
|
||||||
import { DSpaceObject } from '../core/shared/dspace-object.model';
|
import { DSpaceObject } from '../core/shared/dspace-object.model';
|
||||||
import { pushInOut } from '../shared/animations/push';
|
import { pushInOut } from '../shared/animations/push';
|
||||||
import { HostWindowService } from '../shared/host-window.service';
|
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 { SidebarService } from '../shared/sidebar/sidebar.service';
|
||||||
import { hasValue, isNotEmpty } from '../shared/empty.util';
|
import { hasValue, isNotEmpty } from '../shared/empty.util';
|
||||||
import { SearchConfigurationService } from './search-service/search-configuration.service';
|
|
||||||
import { getSucceededRemoteData } from '../core/shared/operators';
|
import { getSucceededRemoteData } from '../core/shared/operators';
|
||||||
import { RouteService } from '../core/services/route.service';
|
import { RouteService } from '../core/services/route.service';
|
||||||
import { SEARCH_CONFIG_SERVICE } from '../+my-dspace-page/my-dspace-page.component';
|
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({
|
@Component({
|
||||||
selector: 'ds-search',
|
selector: 'ds-search',
|
||||||
@@ -96,7 +98,8 @@ export class SearchComponent implements OnInit {
|
|||||||
protected sidebarService: SidebarService,
|
protected sidebarService: SidebarService,
|
||||||
protected windowService: HostWindowService,
|
protected windowService: HostWindowService,
|
||||||
@Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService,
|
@Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService,
|
||||||
protected routeService: RouteService) {
|
protected routeService: RouteService,
|
||||||
|
protected router: Router) {
|
||||||
this.isXsOrSm$ = this.windowService.isXsOrSm();
|
this.isXsOrSm$ = this.windowService.isXsOrSm();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,7 +162,7 @@ export class SearchComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
private getSearchLink(): string {
|
private getSearchLink(): string {
|
||||||
if (this.inPlaceSearch) {
|
if (this.inPlaceSearch) {
|
||||||
return './';
|
return currentPath(this.router);
|
||||||
}
|
}
|
||||||
return this.service.getSearchLink();
|
return this.service.getSearchLink();
|
||||||
}
|
}
|
||||||
|
@@ -20,7 +20,7 @@ import { Store, StoreModule } from '@ngrx/store';
|
|||||||
// Load the implementations that should be tested
|
// Load the implementations that should be tested
|
||||||
import { AppComponent } from './app.component';
|
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 { HostWindowResizeAction } from './shared/host-window.actions';
|
||||||
|
|
||||||
import { MetadataService } from './core/metadata/metadata.service';
|
import { MetadataService } from './core/metadata/metadata.service';
|
||||||
|
@@ -1,13 +1,5 @@
|
|||||||
import { filter, map, take } from 'rxjs/operators';
|
import { filter, map, take } from 'rxjs/operators';
|
||||||
import {
|
import { AfterViewInit, ChangeDetectionStrategy, Component, HostListener, Inject, OnInit, ViewEncapsulation } from '@angular/core';
|
||||||
AfterViewInit,
|
|
||||||
ChangeDetectionStrategy,
|
|
||||||
Component,
|
|
||||||
HostListener,
|
|
||||||
Inject,
|
|
||||||
OnInit,
|
|
||||||
ViewEncapsulation
|
|
||||||
} from '@angular/core';
|
|
||||||
import { NavigationCancel, NavigationEnd, NavigationStart, Router } from '@angular/router';
|
import { NavigationCancel, NavigationEnd, NavigationStart, Router } from '@angular/router';
|
||||||
|
|
||||||
import { select, Store } from '@ngrx/store';
|
import { select, Store } from '@ngrx/store';
|
||||||
@@ -18,12 +10,11 @@ import { GLOBAL_CONFIG, GlobalConfig } from '../config';
|
|||||||
|
|
||||||
import { MetadataService } from './core/metadata/metadata.service';
|
import { MetadataService } from './core/metadata/metadata.service';
|
||||||
import { HostWindowResizeAction } from './shared/host-window.actions';
|
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 { NativeWindowRef, NativeWindowService } from './core/services/window.service';
|
||||||
import { isAuthenticated } from './core/auth/selectors';
|
import { isAuthenticated } from './core/auth/selectors';
|
||||||
import { AuthService } from './core/auth/auth.service';
|
import { AuthService } from './core/auth/auth.service';
|
||||||
import { Angulartics2GoogleAnalytics } from 'angulartics2/ga';
|
import { Angulartics2GoogleAnalytics } from 'angulartics2/ga';
|
||||||
import { RouteService } from './core/services/route.service';
|
|
||||||
import variables from '../styles/_exposed_variables.scss';
|
import variables from '../styles/_exposed_variables.scss';
|
||||||
import { CSSVariableService } from './shared/sass-helper/sass-helper.service';
|
import { CSSVariableService } from './shared/sass-helper/sass-helper.service';
|
||||||
import { MenuService } from './shared/menu/menu.service';
|
import { MenuService } from './shared/menu/menu.service';
|
||||||
|
@@ -1,9 +1,13 @@
|
|||||||
import { StoreEffects } from './store.effects';
|
import { StoreEffects } from './store.effects';
|
||||||
import { NotificationsEffects } from './shared/notifications/notifications.effects';
|
import { NotificationsEffects } from './shared/notifications/notifications.effects';
|
||||||
import { NavbarEffects } from './navbar/navbar.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 = [
|
export const appEffects = [
|
||||||
StoreEffects,
|
StoreEffects,
|
||||||
NavbarEffects,
|
NavbarEffects,
|
||||||
NotificationsEffects,
|
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 { 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 { ExpandableAdminSidebarSectionComponent } from './+admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component';
|
||||||
import { NavbarModule } from './navbar/navbar.module';
|
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 { JournalEntitiesModule } from './entity-groups/journal-entities/journal-entities.module';
|
||||||
import { ResearchEntitiesModule } from './entity-groups/research-entities/research-entities.module';
|
import { ResearchEntitiesModule } from './entity-groups/research-entities/research-entities.module';
|
||||||
import { ClientCookieService } from './core/services/client-cookie.service';
|
|
||||||
|
|
||||||
export function getConfig() {
|
export function getConfig() {
|
||||||
return ENV_CONFIG;
|
return ENV_CONFIG;
|
||||||
@@ -76,7 +76,7 @@ const ENTITY_IMPORTS = [
|
|||||||
|
|
||||||
IMPORTS.push(
|
IMPORTS.push(
|
||||||
StoreDevtoolsModule.instrument({
|
StoreDevtoolsModule.instrument({
|
||||||
maxAge: 100,
|
maxAge: 1000,
|
||||||
logOnly: ENV_CONFIG.production,
|
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 * 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 { 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 { formReducer, FormState } from './shared/form/form.reducer';
|
||||||
import {
|
import { sidebarReducer, SidebarState } from './shared/sidebar/sidebar.reducer';
|
||||||
SidebarState,
|
import { sidebarFilterReducer, SidebarFiltersState } from './shared/sidebar/filter/sidebar-filter.reducer';
|
||||||
sidebarReducer
|
import { filterReducer, SearchFiltersState } from './shared/search/search-filters/search-filter/search-filter.reducer';
|
||||||
} from './shared/sidebar/sidebar.reducer';
|
import { notificationsReducer, NotificationsState } from './shared/notifications/notifications.reducers';
|
||||||
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 { truncatableReducer, TruncatablesState } from './shared/truncatable/truncatable.reducer';
|
import { truncatableReducer, TruncatablesState } from './shared/truncatable/truncatable.reducer';
|
||||||
import {
|
import { metadataRegistryReducer, MetadataRegistryState } from './+admin/admin-registries/metadata-registry/metadata-registry.reducers';
|
||||||
metadataRegistryReducer,
|
|
||||||
MetadataRegistryState
|
|
||||||
} from './+admin/admin-registries/metadata-registry/metadata-registry.reducers';
|
|
||||||
import { hasValue } from './shared/empty.util';
|
import { hasValue } from './shared/empty.util';
|
||||||
import { cssVariablesReducer, CSSVariablesState } from './shared/sass-helper/sass-helper.reducer';
|
import { cssVariablesReducer, CSSVariablesState } from './shared/sass-helper/sass-helper.reducer';
|
||||||
import { menusReducer, MenusState } from './shared/menu/menu.reducer';
|
import { menusReducer, MenusState } from './shared/menu/menu.reducer';
|
||||||
import { historyReducer, HistoryState } from './shared/history/history.reducer';
|
import { historyReducer, HistoryState } from './shared/history/history.reducer';
|
||||||
import {
|
import { selectableListReducer, SelectableListsState } from './shared/object-list/selectable-list/selectable-list.reducer';
|
||||||
bitstreamFormatReducer,
|
import { bitstreamFormatReducer, BitstreamFormatRegistryState } from './+admin/admin-registries/bitstream-formats/bitstream-format.reducers';
|
||||||
BitstreamFormatRegistryState
|
|
||||||
} from './+admin/admin-registries/bitstream-formats/bitstream-format.reducers';
|
|
||||||
import { ObjectSelectionListState, objectSelectionReducer } from './shared/object-select/object-select.reducer';
|
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 {
|
export interface AppState {
|
||||||
router: fromRouter.RouterReducerState;
|
router: fromRouter.RouterReducerState;
|
||||||
@@ -49,6 +33,8 @@ export interface AppState {
|
|||||||
cssVariables: CSSVariablesState;
|
cssVariables: CSSVariablesState;
|
||||||
menus: MenusState;
|
menus: MenusState;
|
||||||
objectSelection: ObjectSelectionListState;
|
objectSelection: ObjectSelectionListState;
|
||||||
|
selectableLists: SelectableListsState;
|
||||||
|
relationshipLists: NameVariantListsState;
|
||||||
communityList: CommunityListState;
|
communityList: CommunityListState;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,6 +53,8 @@ export const appReducers: ActionReducerMap<AppState> = {
|
|||||||
cssVariables: cssVariablesReducer,
|
cssVariables: cssVariablesReducer,
|
||||||
menus: menusReducer,
|
menus: menusReducer,
|
||||||
objectSelection: objectSelectionReducer,
|
objectSelection: objectSelectionReducer,
|
||||||
|
selectableLists: selectableListReducer,
|
||||||
|
relationshipLists: nameVariantReducer,
|
||||||
communityList: CommunityListReducer,
|
communityList: CommunityListReducer,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
|
|||||||
import { NormalizedObject } from '../models/normalized-object.model';
|
import { NormalizedObject } from '../models/normalized-object.model';
|
||||||
import { getMapsToType, getRelationships } from './build-decorators';
|
import { getMapsToType, getRelationships } from './build-decorators';
|
||||||
import { hasValue, isNotEmpty } from '../../../shared/empty.util';
|
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`
|
* Return true if halObj has a value for `_links.self`
|
||||||
@@ -34,14 +34,13 @@ export class NormalizedObjectBuildService {
|
|||||||
*
|
*
|
||||||
* @param {TDomain} domainModel a domain model
|
* @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 normalizedConstructor = getMapsToType((domainModel as any).type);
|
||||||
const relationships = getRelationships(normalizedConstructor) || [];
|
const relationships = getRelationships(normalizedConstructor) || [];
|
||||||
|
|
||||||
const normalizedModel = Object.assign({}, domainModel) as any;
|
const normalizedModel = Object.assign({}, domainModel) as any;
|
||||||
relationships.forEach((key: string) => {
|
relationships.forEach((key: string) => {
|
||||||
if (hasValue(domainModel[key])) {
|
if (hasValue(normalizedModel[key])) {
|
||||||
domainModel[key] = undefined;
|
normalizedModel[key] = normalizedModel._links[key];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return normalizedModel;
|
return normalizedModel;
|
||||||
|
@@ -116,7 +116,7 @@ export class RemoteDataBuildService {
|
|||||||
const requestEntry$ = href$.pipe(getRequestFromRequestHref(this.requestService));
|
const requestEntry$ = href$.pipe(getRequestFromRequestHref(this.requestService));
|
||||||
const tDomainList$ = requestEntry$.pipe(
|
const tDomainList$ = requestEntry$.pipe(
|
||||||
getResourceLinksFromResponse(),
|
getResourceLinksFromResponse(),
|
||||||
flatMap((resourceUUIDs: string[]) => {
|
switchMap((resourceUUIDs: string[]) => {
|
||||||
return this.objectCache.getList(resourceUUIDs).pipe(
|
return this.objectCache.getList(resourceUUIDs).pipe(
|
||||||
map((normList: Array<NormalizedObject<T>>) => {
|
map((normList: Array<NormalizedObject<T>>) => {
|
||||||
return normList.map((normalized: 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>>> {
|
private toPaginatedList<T>(input: Observable<RemoteData<T[] | PaginatedList<T>>>, pageInfo: PageInfo): Observable<RemoteData<PaginatedList<T>>> {
|
||||||
return input.pipe(
|
return input.pipe(
|
||||||
map((rd: RemoteData<T[] | PaginatedList<T>>) => {
|
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)) {
|
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)) {
|
} 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 {
|
} 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 { Relationship } from '../../../shared/item-relationships/relationship.model';
|
||||||
import { mapsTo, relationship } from '../../builders/build-decorators';
|
import { mapsTo, relationship } from '../../builders/build-decorators';
|
||||||
import { NormalizedObject } from '../normalized-object.model';
|
import { NormalizedObject } from '../normalized-object.model';
|
||||||
@@ -16,20 +16,20 @@ export class NormalizedRelationship extends NormalizedObject<Relationship> {
|
|||||||
/**
|
/**
|
||||||
* The identifier of this Relationship
|
* The identifier of this Relationship
|
||||||
*/
|
*/
|
||||||
@autoserialize
|
@deserialize
|
||||||
id: string;
|
id: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The item to the left of this relationship
|
* The item to the left of this relationship
|
||||||
*/
|
*/
|
||||||
@autoserialize
|
@deserialize
|
||||||
@relationship(Item, false)
|
@relationship(Item, false)
|
||||||
leftItem: string;
|
leftItem: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The item to the right of this relationship
|
* The item to the right of this relationship
|
||||||
*/
|
*/
|
||||||
@autoserialize
|
@deserialize
|
||||||
@relationship(Item, false)
|
@relationship(Item, false)
|
||||||
rightItem: string;
|
rightItem: string;
|
||||||
|
|
||||||
@@ -46,15 +46,27 @@ export class NormalizedRelationship extends NormalizedObject<Relationship> {
|
|||||||
rightPlace: number;
|
rightPlace: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of Relationship
|
* The name variant of the Item to the left side of this Relationship
|
||||||
*/
|
*/
|
||||||
@autoserialize
|
@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)
|
@relationship(RelationshipType, false)
|
||||||
relationshipType: string;
|
relationshipType: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The universally unique identifier of this Relationship
|
* The universally unique identifier of this Relationship
|
||||||
*/
|
*/
|
||||||
@autoserializeAs(new IDToUUIDSerializer(Relationship.type.value), 'id')
|
@deserializeAs(new IDToUUIDSerializer(Relationship.type.value), 'id')
|
||||||
uuid: string;
|
uuid: string;
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { CacheableObject, TypedObject } from '../object-cache.reducer';
|
import { CacheableObject, TypedObject } from '../object-cache.reducer';
|
||||||
import { autoserialize } from 'cerialize';
|
import { autoserialize, deserialize } from 'cerialize';
|
||||||
import { ResourceType } from '../../shared/resource-type';
|
import { ResourceType } from '../../shared/resource-type';
|
||||||
/**
|
/**
|
||||||
* An abstract model class for a NormalizedObject.
|
* 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
|
* The link to the rest endpoint where this object can be found
|
||||||
*/
|
*/
|
||||||
@autoserialize
|
@deserialize
|
||||||
self: string;
|
self: string;
|
||||||
|
|
||||||
@autoserialize
|
@deserialize
|
||||||
_links: {
|
_links: {
|
||||||
[name: string]: string
|
[name: string]: string
|
||||||
};
|
};
|
||||||
@@ -19,6 +19,6 @@ export abstract class NormalizedObject<T extends TypedObject> implements Cacheab
|
|||||||
/**
|
/**
|
||||||
* A string representing the kind of object
|
* A string representing the kind of object
|
||||||
*/
|
*/
|
||||||
@autoserialize
|
@deserialize
|
||||||
type: string;
|
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
|
* false otherwise
|
||||||
*/
|
*/
|
||||||
hasByUUID(uuid: string): boolean {
|
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(
|
this.store.pipe(
|
||||||
select(selfLinkFromUuidSelector(uuid)),
|
select(selfLinkFromUuidSelector(uuid)),
|
||||||
take(1)
|
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 { RequestError } from '../data/request.models';
|
||||||
import { PageInfo } from '../shared/page-info.model';
|
import { PageInfo } from '../shared/page-info.model';
|
||||||
import { ConfigObject } from '../config/models/config.model';
|
import { ConfigObject } from '../config/models/config.model';
|
||||||
import { FacetValue } from '../../+search-page/search-service/facet-value.model';
|
import { FacetValue } from '../../shared/search/facet-value.model';
|
||||||
import { SearchFilterConfig } from '../../+search-page/search-service/search-filter-config.model';
|
import { SearchFilterConfig } from '../../shared/search/search-filter-config.model';
|
||||||
import { IntegrationModel } from '../integration/models/integration.model';
|
import { IntegrationModel } from '../integration/models/integration.model';
|
||||||
import { RegistryMetadataschemasResponse } from '../registry/registry-metadataschemas-response.model';
|
import { RegistryMetadataschemasResponse } from '../registry/registry-metadataschemas-response.model';
|
||||||
import { RegistryMetadatafieldsResponse } from '../registry/registry-metadatafields-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 { ServerSyncBufferEffects } from './cache/server-sync-buffer.effects';
|
||||||
import { ObjectUpdatesEffects } from './data/object-updates/object-updates.effects';
|
import { ObjectUpdatesEffects } from './data/object-updates/object-updates.effects';
|
||||||
import { RouteEffects } from './services/route.effects';
|
import { RouteEffects } from './services/route.effects';
|
||||||
|
import { RouterEffects } from './router/router.effects';
|
||||||
|
|
||||||
export const coreEffects = [
|
export const coreEffects = [
|
||||||
RequestEffects,
|
RequestEffects,
|
||||||
@@ -15,5 +16,6 @@ export const coreEffects = [
|
|||||||
JsonPatchOperationsEffects,
|
JsonPatchOperationsEffects,
|
||||||
ServerSyncBufferEffects,
|
ServerSyncBufferEffects,
|
||||||
ObjectUpdatesEffects,
|
ObjectUpdatesEffects,
|
||||||
RouteEffects
|
RouteEffects,
|
||||||
|
RouterEffects
|
||||||
];
|
];
|
||||||
|
@@ -53,7 +53,7 @@ import { UUIDService } from './shared/uuid.service';
|
|||||||
import { AuthenticatedGuard } from './auth/authenticated.guard';
|
import { AuthenticatedGuard } from './auth/authenticated.guard';
|
||||||
import { AuthRequestService } from './auth/auth-request.service';
|
import { AuthRequestService } from './auth/auth-request.service';
|
||||||
import { AuthResponseParsingService } from './auth/auth-response-parsing.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 { AuthInterceptor } from './auth/auth.interceptor';
|
||||||
import { HALEndpointService } from './shared/hal-endpoint.service';
|
import { HALEndpointService } from './shared/hal-endpoint.service';
|
||||||
import { FacetValueResponseParsingService } from './data/facet-value-response-parsing.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 { DSOChangeAnalyzer } from './data/dso-change-analyzer.service';
|
||||||
import { ObjectUpdatesService } from './data/object-updates/object-updates.service';
|
import { ObjectUpdatesService } from './data/object-updates/object-updates.service';
|
||||||
import { DefaultChangeAnalyzer } from './data/default-change-analyzer.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 { NormalizedCollection } from './cache/models/normalized-collection.model';
|
||||||
import { NormalizedCommunity } from './cache/models/normalized-community.model';
|
import { NormalizedCommunity } from './cache/models/normalized-community.model';
|
||||||
import { NormalizedDSpaceObject } from './cache/models/normalized-dspace-object.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 { NormalizedSubmissionSectionModel } from './config/models/normalized-config-submission-section.model';
|
||||||
import { NormalizedAuthStatus } from './auth/models/normalized-auth-status.model';
|
import { NormalizedAuthStatus } from './auth/models/normalized-auth-status.model';
|
||||||
import { NormalizedAuthorityValue } from './integration/models/normalized-authority-value.model';
|
import { NormalizedAuthorityValue } from './integration/models/normalized-authority-value.model';
|
||||||
import { RelationshipService } from './data/relationship.service';
|
|
||||||
import { RoleService } from './roles/role.service';
|
import { RoleService } from './roles/role.service';
|
||||||
import { MyDSpaceGuard } from '../+my-dspace-page/my-dspace.guard';
|
import { MyDSpaceGuard } from '../+my-dspace-page/my-dspace.guard';
|
||||||
import { MyDSpaceResponseParsingService } from './data/mydspace-response-parsing.service';
|
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 { SiteDataService } from './data/site-data.service';
|
||||||
import { NormalizedSite } from './cache/models/normalized-site.model';
|
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 = [
|
const IMPORTS = [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
StoreModule.forFeature('core', coreReducers, {}),
|
StoreModule.forFeature('core', coreReducers, {}),
|
||||||
@@ -143,7 +168,8 @@ const PROVIDERS = [
|
|||||||
CollectionDataService,
|
CollectionDataService,
|
||||||
SiteDataService,
|
SiteDataService,
|
||||||
DSOResponseParsingService,
|
DSOResponseParsingService,
|
||||||
DSpaceRESTv2Service,
|
{ provide: MOCK_RESPONSE_MAP, useValue: mockResponseMap },
|
||||||
|
{ provide: DSpaceRESTv2Service, useFactory: restServiceFactory, deps: [GLOBAL_CONFIG, MOCK_RESPONSE_MAP, HttpClient]},
|
||||||
DynamicFormLayoutService,
|
DynamicFormLayoutService,
|
||||||
DynamicFormService,
|
DynamicFormService,
|
||||||
DynamicFormValidationService,
|
DynamicFormValidationService,
|
||||||
@@ -214,6 +240,13 @@ const PROVIDERS = [
|
|||||||
TaskResponseParsingService,
|
TaskResponseParsingService,
|
||||||
ClaimedTaskDataService,
|
ClaimedTaskDataService,
|
||||||
PoolTaskDataService,
|
PoolTaskDataService,
|
||||||
|
SearchService,
|
||||||
|
SidebarService,
|
||||||
|
SearchFilterService,
|
||||||
|
SearchFilterService,
|
||||||
|
SearchConfigurationService,
|
||||||
|
SelectableListService,
|
||||||
|
RelationshipTypeService,
|
||||||
// register AuthInterceptor as HttpInterceptor
|
// register AuthInterceptor as HttpInterceptor
|
||||||
{
|
{
|
||||||
provide: HTTP_INTERCEPTORS,
|
provide: HTTP_INTERCEPTORS,
|
||||||
|
@@ -25,8 +25,8 @@ import { ResponseParsingService } from './parsing.service';
|
|||||||
import { GenericConstructor } from '../shared/generic-constructor';
|
import { GenericConstructor } from '../shared/generic-constructor';
|
||||||
import { hasValue, isNotEmptyOperator } from '../../shared/empty.util';
|
import { hasValue, isNotEmptyOperator } from '../../shared/empty.util';
|
||||||
import { DSpaceObject } from '../shared/dspace-object.model';
|
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 { SearchParam } from '../cache/models/search-param.model';
|
||||||
|
import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CollectionDataService extends ComColDataService<Collection> {
|
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>>> {
|
getAuthorizedCollectionByCommunity(communityId: string, options: FindListOptions = {}): Observable<RemoteData<PaginatedList<Collection>>> {
|
||||||
const searchHref = 'findAuthorizedByCommunity';
|
const searchHref = 'findAuthorizedByCommunity';
|
||||||
options.searchParams = [new SearchParam('uuid', communityId)];
|
options = Object.assign({}, options, {
|
||||||
|
searchParams: [new SearchParam('uuid', communityId)]
|
||||||
|
});
|
||||||
|
|
||||||
return this.searchBy(searchHref, options).pipe(
|
return this.searchBy(searchHref, options).pipe(
|
||||||
filter((collections: RemoteData<PaginatedList<Collection>>) => !collections.isResponsePending));
|
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 { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
|
||||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||||
import { Item } from '../shared/item.model';
|
import { Item } from '../shared/item.model';
|
||||||
|
import * as uuidv4 from 'uuid/v4';
|
||||||
|
|
||||||
const endpoint = 'https://rest.api/core';
|
const endpoint = 'https://rest.api/core';
|
||||||
|
|
||||||
@@ -51,10 +52,11 @@ class DummyChangeAnalyzer implements ChangeAnalyzer<NormalizedTestObject> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('DataService', () => {
|
describe('DataService', () => {
|
||||||
let service: TestService;
|
let service: TestService;
|
||||||
let options: FindListOptions;
|
let options: FindListOptions;
|
||||||
const requestService = {} as RequestService;
|
const requestService = {generateRequestId: () => uuidv4()} as RequestService;
|
||||||
const halService = {} as HALEndpointService;
|
const halService = {} as HALEndpointService;
|
||||||
const rdbService = {} as RemoteDataBuildService;
|
const rdbService = {} as RemoteDataBuildService;
|
||||||
const notificationsService = {} as NotificationsService;
|
const notificationsService = {} as NotificationsService;
|
||||||
@@ -87,6 +89,7 @@ describe('DataService', () => {
|
|||||||
comparator,
|
comparator,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
service = initTestService();
|
service = initTestService();
|
||||||
|
|
||||||
describe('getFindAllHref', () => {
|
describe('getFindAllHref', () => {
|
||||||
@@ -188,7 +191,7 @@ describe('DataService', () => {
|
|||||||
dso2.self = selfLink;
|
dso2.self = selfLink;
|
||||||
dso2.metadata = [{ key: 'dc.title', value: name2 }];
|
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, 'getObjectBySelfLink').and.returnValues(observableOf(dso));
|
||||||
spyOn(objectCache, 'addPatch');
|
spyOn(objectCache, 'addPatch');
|
||||||
});
|
});
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
|
||||||
import { Observable } from 'rxjs';
|
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 { Store } from '@ngrx/store';
|
||||||
|
|
||||||
import { hasValue, isNotEmpty, isNotEmptyOperator } from '../../shared/empty.util';
|
import { hasValue, isNotEmpty, isNotEmptyOperator } from '../../shared/empty.util';
|
||||||
@@ -204,15 +204,22 @@ export abstract class DataService<T extends CacheableObject> {
|
|||||||
|
|
||||||
const hrefObs = this.getSearchByHref(searchMethod, options);
|
const hrefObs = this.getSearchByHref(searchMethod, options);
|
||||||
|
|
||||||
hrefObs.pipe(
|
return hrefObs.pipe(
|
||||||
first((href: string) => hasValue(href)))
|
find((href: string) => hasValue(href)),
|
||||||
.subscribe((href: string) => {
|
tap((href: string) => {
|
||||||
|
this.requestService.removeByHrefSubstring(href);
|
||||||
const request = new FindListRequest(this.requestService.generateRequestId(), href, options);
|
const request = new FindListRequest(this.requestService.generateRequestId(), href, options);
|
||||||
request.responseMsToLive = 10 * 1000;
|
request.responseMsToLive = 10 * 1000;
|
||||||
this.requestService.configure(request);
|
|
||||||
});
|
|
||||||
|
|
||||||
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)) {
|
if (isNotEmpty(operations)) {
|
||||||
this.objectCache.addPatch(object.self, 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 { Injectable } from '@angular/core';
|
||||||
import { CacheableObject } from '../cache/object-cache.reducer';
|
import { CacheableObject } from '../cache/object-cache.reducer';
|
||||||
import { NormalizedObject } from '../cache/models/normalized-object.model';
|
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
|
* A class to determine what differs between two
|
||||||
@@ -11,6 +12,8 @@ import { NormalizedObject } from '../cache/models/normalized-object.model';
|
|||||||
*/
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DefaultChangeAnalyzer<T extends CacheableObject> implements ChangeAnalyzer<T> {
|
export class DefaultChangeAnalyzer<T extends CacheableObject> implements ChangeAnalyzer<T> {
|
||||||
|
constructor(private normalizeService: NormalizedObjectBuildService) {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compare the metadata of two CacheableObject and return the differences as
|
* 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
|
* The second object to compare
|
||||||
*/
|
*/
|
||||||
diff(object1: T | NormalizedObject<T>, object2: T | NormalizedObject<T>): Operation[] {
|
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 { RestRequest } from './request.models';
|
||||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||||
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
|
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 { BaseResponseParsingService } from './base-response-parsing.service';
|
||||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
import { GlobalConfig } from '../../../config/global-config.interface';
|
import { GlobalConfig } from '../../../config/global-config.interface';
|
||||||
|
@@ -9,7 +9,7 @@ import { ResponseParsingService } from './parsing.service';
|
|||||||
import { RestRequest } from './request.models';
|
import { RestRequest } from './request.models';
|
||||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||||
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
|
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 { BaseResponseParsingService } from './base-response-parsing.service';
|
||||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
import { GlobalConfig } from '../../../config/global-config.interface';
|
import { GlobalConfig } from '../../../config/global-config.interface';
|
||||||
|
@@ -4,7 +4,7 @@ import { ResponseParsingService } from './parsing.service';
|
|||||||
import { RestRequest } from './request.models';
|
import { RestRequest } from './request.models';
|
||||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||||
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
|
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 { BaseResponseParsingService } from './base-response-parsing.service';
|
||||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
import { GLOBAL_CONFIG } from '../../../config';
|
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 { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||||
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
|
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
|
||||||
import { hasValue } from '../../shared/empty.util';
|
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';
|
import { MetadataMap, MetadataValue } from '../shared/metadata.models';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@@ -3,7 +3,7 @@ import { hasValue } from '../../shared/empty.util';
|
|||||||
|
|
||||||
export class PaginatedList<T> {
|
export class PaginatedList<T> {
|
||||||
|
|
||||||
constructor(private pageInfo: PageInfo,
|
constructor(public pageInfo: PageInfo,
|
||||||
public page: T[]) {
|
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 rdbService = getMockRemoteDataBuildService(undefined, buildList$);
|
||||||
const objectCache = Object.assign({
|
const objectCache = Object.assign({
|
||||||
/* tslint:disable:no-empty */
|
/* tslint:disable:no-empty */
|
||||||
remove: () => {}
|
remove: () => {},
|
||||||
|
hasBySelfLinkObservable: () => observableOf(false)
|
||||||
/* tslint:enable:no-empty */
|
/* tslint:enable:no-empty */
|
||||||
}) as ObjectCacheService;
|
}) as ObjectCacheService;
|
||||||
|
|
||||||
const itemService = jasmine.createSpyObj('itemService', {
|
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() {
|
function initTestService() {
|
||||||
@@ -90,6 +92,7 @@ describe('RelationshipService', () => {
|
|||||||
objectCache,
|
objectCache,
|
||||||
null,
|
null,
|
||||||
null,
|
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', () => {
|
describe('getRelatedItems', () => {
|
||||||
it('should return the related items', () => {
|
it('should return the related items', () => {
|
||||||
service.getRelatedItems(item).subscribe((result) => {
|
service.getRelatedItems(item).subscribe((result) => {
|
||||||
|
@@ -2,38 +2,43 @@ import { Injectable } from '@angular/core';
|
|||||||
import { RequestService } from './request.service';
|
import { RequestService } from './request.service';
|
||||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||||
import { hasValue, hasValueOperator, isNotEmptyOperator } from '../../shared/empty.util';
|
import { hasValue, hasValueOperator, isNotEmpty, isNotEmptyOperator } from '../../shared/empty.util';
|
||||||
import { distinctUntilChanged, filter, flatMap, map, switchMap, take, tap } from 'rxjs/operators';
|
import { distinctUntilChanged, filter, map, mergeMap, skipWhile, startWith, switchMap, take, tap } from 'rxjs/operators';
|
||||||
import {
|
import { configureRequest, getRemoteDataPayload, getResponseFromEntry, getSucceededRemoteData } from '../shared/operators';
|
||||||
configureRequest,
|
import { DeleteRequest, FindListOptions, PostRequest, RestRequest } from './request.models';
|
||||||
filterSuccessfulResponses,
|
|
||||||
getRemoteDataPayload, getResponseFromEntry,
|
|
||||||
getSucceededRemoteData
|
|
||||||
} from '../shared/operators';
|
|
||||||
import { DeleteRequest, FindListOptions, RestRequest } from './request.models';
|
|
||||||
import { Observable } from 'rxjs/internal/Observable';
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
import { RestResponse } from '../cache/response.models';
|
import { RestResponse } from '../cache/response.models';
|
||||||
import { Item } from '../shared/item.model';
|
import { Item } from '../shared/item.model';
|
||||||
import { Relationship } from '../shared/item-relationships/relationship.model';
|
import { Relationship } from '../shared/item-relationships/relationship.model';
|
||||||
import { RelationshipType } from '../shared/item-relationships/relationship-type.model';
|
import { RelationshipType } from '../shared/item-relationships/relationship-type.model';
|
||||||
import { RemoteData } from './remote-data';
|
import { RemoteData } from './remote-data';
|
||||||
import { combineLatest as observableCombineLatest } from 'rxjs/internal/observable/combineLatest';
|
import { combineLatest, combineLatest as observableCombineLatest } from 'rxjs';
|
||||||
import { zip as observableZip } from 'rxjs';
|
|
||||||
import { PaginatedList } from './paginated-list';
|
import { PaginatedList } from './paginated-list';
|
||||||
import { ItemDataService } from './item-data.service';
|
import { ItemDataService } from './item-data.service';
|
||||||
import {
|
import { compareArraysUsingIds, paginatedRelationsToItems, relationsToItems } from '../../+item-page/simple/item-types/shared/item-relationships-utils';
|
||||||
compareArraysUsingIds, filterRelationsByTypeLabel, paginatedRelationsToItems,
|
|
||||||
relationsToItems
|
|
||||||
} from '../../+item-page/simple/item-types/shared/item-relationships-utils';
|
|
||||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
import { DataService } from './data.service';
|
import { DataService } from './data.service';
|
||||||
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.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 { CoreState } from '../core.reducers';
|
||||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
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 { DefaultChangeAnalyzer } from './default-change-analyzer.service';
|
||||||
import { SearchParam } from '../cache/models/search-param.model';
|
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
|
* The service handling all relationship requests
|
||||||
@@ -52,7 +57,8 @@ export class RelationshipService extends DataService<Relationship> {
|
|||||||
protected objectCache: ObjectCacheService,
|
protected objectCache: ObjectCacheService,
|
||||||
protected notificationsService: NotificationsService,
|
protected notificationsService: NotificationsService,
|
||||||
protected http: HttpClient,
|
protected http: HttpClient,
|
||||||
protected comparator: DefaultChangeAnalyzer<Relationship>) {
|
protected comparator: DefaultChangeAnalyzer<Relationship>,
|
||||||
|
protected appStore: Store<AppState>) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,76 +71,94 @@ export class RelationshipService extends DataService<Relationship> {
|
|||||||
* @param uuid
|
* @param uuid
|
||||||
*/
|
*/
|
||||||
getRelationshipEndpoint(uuid: string) {
|
getRelationshipEndpoint(uuid: string) {
|
||||||
return this.halService.getEndpoint(this.linkPath).pipe(
|
return this.getBrowseEndpoint().pipe(
|
||||||
map((href: string) => `${href}/${uuid}`)
|
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
|
* Send a delete request for a relationship by ID
|
||||||
* @param uuid
|
* @param id
|
||||||
*/
|
*/
|
||||||
deleteRelationship(uuid: string): Observable<RestResponse> {
|
deleteRelationship(id: string): Observable<RestResponse> {
|
||||||
return this.getRelationshipEndpoint(uuid).pipe(
|
return this.getRelationshipEndpoint(id).pipe(
|
||||||
isNotEmptyOperator(),
|
isNotEmptyOperator(),
|
||||||
distinctUntilChanged(),
|
take(1),
|
||||||
map((endpointURL: string) => new DeleteRequest(this.requestService.generateRequestId(), endpointURL)),
|
map((endpointURL: string) => new DeleteRequest(this.requestService.generateRequestId(), endpointURL)),
|
||||||
configureRequest(this.requestService),
|
configureRequest(this.requestService),
|
||||||
switchMap((restRequest: RestRequest) => this.requestService.getByUUID(restRequest.uuid)),
|
switchMap((restRequest: RestRequest) => this.requestService.getByUUID(restRequest.uuid)),
|
||||||
getResponseFromEntry(),
|
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
|
* Method to create a new relationship
|
||||||
* This is used for easier access of a relationship's type because they exist as observables
|
* @param typeId The identifier of the relationship type
|
||||||
* @param item
|
* @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[]]> {
|
addRelationship(typeId: string, item1: Item, item2: Item, leftwardValue?: string, rightwardValue?: string): Observable<RestResponse> {
|
||||||
return observableCombineLatest(
|
const options: HttpOptions = Object.create({});
|
||||||
this.getItemRelationshipsArray(item),
|
let headers = new HttpHeaders();
|
||||||
this.getItemRelationshipTypesArray(item)
|
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
|
* Method to remove two items of a relationship from the cache using the identifier of the relationship
|
||||||
* This is used for easier access of a relationship's type and left and right items because they exist as observables
|
* @param relationshipId The identifier of the relationship
|
||||||
* @param item
|
|
||||||
*/
|
*/
|
||||||
getItemResolvedRelatedItemsAndTypes(item: Item): Observable<[Item[], Item[], RelationshipType[]]> {
|
private removeRelationshipItemsFromCacheByRelationship(relationshipId: string) {
|
||||||
return observableCombineLatest(
|
this.findById(relationshipId).pipe(
|
||||||
this.getItemLeftRelatedItemArray(item),
|
getSucceededRemoteData(),
|
||||||
this.getItemRightRelatedItemArray(item),
|
getRemoteDataPayload(),
|
||||||
this.getItemRelationshipTypesArray(item)
|
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
|
* Method to remove an item that's part of a relationship from the cache
|
||||||
* This is used for easier access of the relationship and their left and right items because they exist as observables
|
* @param item The item to remove from the cache
|
||||||
* @param item
|
|
||||||
*/
|
*/
|
||||||
getItemResolvedRelatedItemsAndRelationships(item: Item): Observable<[Item[], Item[], Relationship[]]> {
|
private removeRelationshipItemsFromCache(item) {
|
||||||
return observableCombineLatest(
|
this.objectCache.remove(item.self);
|
||||||
this.getItemLeftRelatedItemArray(item),
|
this.requestService.removeByHrefSubstring(item.self);
|
||||||
this.getItemRightRelatedItemArray(item),
|
combineLatest(
|
||||||
this.getItemRelationshipsArray(item)
|
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
|
* @param item
|
||||||
*/
|
*/
|
||||||
getItemRelationshipsArray(item: Item): Observable<Relationship[]> {
|
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
|
* Get an array of the labels of an item’s unique relationship types
|
||||||
* @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
|
|
||||||
* The array doesn't contain any duplicate labels
|
* The array doesn't contain any duplicate labels
|
||||||
* @param item
|
* @param item
|
||||||
*/
|
*/
|
||||||
getItemRelationshipLabels(item: Item): Observable<string[]> {
|
getRelationshipTypeLabelsByItem(item: Item): Observable<string[]> {
|
||||||
return this.getItemResolvedRelatedItemsAndTypes(item).pipe(
|
return this.getItemRelationshipsArray(item).pipe(
|
||||||
map(([leftItems, rightItems, relTypesCurrentPage]) => {
|
switchMap((relationships: Relationship[]) => observableCombineLatest(relationships.map((relationship: Relationship) => this.getRelationshipTypeLabelByRelationshipAndItem(relationship, item)))),
|
||||||
return relTypesCurrentPage.map((type, index) => {
|
|
||||||
if (leftItems[index].uuid === item.uuid) {
|
|
||||||
return type.leftwardType;
|
|
||||||
} else {
|
|
||||||
return type.rightwardType;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
map((labels: string[]) => Array.from(new Set(labels)))
|
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;
|
||||||
|
}
|
||||||
|
})
|
||||||
)
|
)
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -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)
|
* Method for fetching an item's relationships, but filtered by related item IDs (essentially performing a reverse lookup)
|
||||||
* @param uuid
|
* Only relationships where leftItem or rightItem's ID is present in the list provided will be returned
|
||||||
|
* @param item
|
||||||
|
* @param uuids
|
||||||
*/
|
*/
|
||||||
clearRelatedCache(uuid: string) {
|
getRelationshipsByRelatedItemIds(item: Item, uuids: string[]): Observable<Relationship[]> {
|
||||||
this.findById(uuid).pipe(
|
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(),
|
getSucceededRemoteData(),
|
||||||
flatMap((rd: RemoteData<Relationship>) => observableCombineLatest(rd.payload.leftItem.pipe(getSucceededRemoteData()), rd.payload.rightItem.pipe(getSucceededRemoteData()))),
|
map((itemRD: RemoteData<Item>) => itemRD.payload),
|
||||||
take(1)
|
map((item: Item) => uuids.includes(item.uuid))
|
||||||
).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);
|
* 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> {
|
export class RemoteData<T> {
|
||||||
constructor(
|
constructor(
|
||||||
private requestPending: boolean,
|
private requestPending?: boolean,
|
||||||
private responsePending: boolean,
|
private responsePending?: boolean,
|
||||||
private isSuccessful: boolean,
|
private isSuccessful?: boolean,
|
||||||
public error: RemoteDataError,
|
public error?: RemoteDataError,
|
||||||
public payload: T
|
public payload?: T
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -3,7 +3,7 @@ import { HttpHeaders } from '@angular/common/http';
|
|||||||
|
|
||||||
import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store';
|
import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store';
|
||||||
import { Observable, race as observableRace } from 'rxjs';
|
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 { cloneDeep, remove } from 'lodash';
|
||||||
|
|
||||||
import { AppState } from '../../app.reducer';
|
import { AppState } from '../../app.reducer';
|
||||||
@@ -304,6 +304,7 @@ export class RequestService {
|
|||||||
*/
|
*/
|
||||||
hasByHref(href: string): boolean {
|
hasByHref(href: string): boolean {
|
||||||
let result = false;
|
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(
|
this.getByHref(href).pipe(
|
||||||
take(1)
|
take(1)
|
||||||
).subscribe((requestEntry: RequestEntry) => result = this.isValid(requestEntry));
|
).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 { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||||
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
|
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
|
||||||
import { hasValue } from '../../shared/empty.util';
|
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';
|
import { MetadataMap, MetadataValue } from '../shared/metadata.models';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@@ -0,0 +1,93 @@
|
|||||||
|
import { StatusCodeOnlyResponseParsingService } from './status-code-only-response-parsing.service';
|
||||||
|
|
||||||
|
describe('StatusCodeOnlyResponseParsingService', () => {
|
||||||
|
let service;
|
||||||
|
let statusCode;
|
||||||
|
let statusText;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
service = new StatusCodeOnlyResponseParsingService();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('parse', () => {
|
||||||
|
|
||||||
|
it('should return a RestResponse that doesn\'t contain the response body', () => {
|
||||||
|
const payload = 'd9128e44-183b-479d-aa2e-d39435838bf6';
|
||||||
|
const result = service.parse(undefined, {
|
||||||
|
payload,
|
||||||
|
statusCode: 201,
|
||||||
|
statusText: '201'
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(JSON.stringify(result).indexOf(payload)).toBe(-1);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the response is successful', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
statusCode = 201;
|
||||||
|
statusText = `${statusCode}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return a success RestResponse', () => {
|
||||||
|
const result = service.parse(undefined, {
|
||||||
|
statusCode,
|
||||||
|
statusText
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.isSuccessful).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return a RestResponse with the correct status code', () => {
|
||||||
|
const result = service.parse(undefined, {
|
||||||
|
statusCode,
|
||||||
|
statusText
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.statusCode).toBe(statusCode);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return a RestResponse with the correct status text', () => {
|
||||||
|
const result = service.parse(undefined, {
|
||||||
|
statusCode,
|
||||||
|
statusText
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.statusText).toBe(statusText);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the response is unsuccessful', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
statusCode = 400;
|
||||||
|
statusText = `${statusCode}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return an error RestResponse', () => {
|
||||||
|
const result = service.parse(undefined, {
|
||||||
|
statusCode,
|
||||||
|
statusText
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.isSuccessful).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return a RestResponse with the correct status code', () => {
|
||||||
|
const result = service.parse(undefined, {
|
||||||
|
statusCode,
|
||||||
|
statusText
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.statusCode).toBe(statusCode);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return a RestResponse with the correct status text', () => {
|
||||||
|
const result = service.parse(undefined, {
|
||||||
|
statusCode,
|
||||||
|
statusText
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.statusText).toBe(statusText);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,26 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { RestResponse } from '../cache/response.models';
|
||||||
|
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||||
|
import { ResponseParsingService } from './parsing.service';
|
||||||
|
import { RestRequest } from './request.models';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A responseparser that will only look at the status code and status
|
||||||
|
* text of the response, and ignore anything else that might be there
|
||||||
|
*/
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class StatusCodeOnlyResponseParsingService implements ResponseParsingService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the response and only extract the status code and status text
|
||||||
|
*
|
||||||
|
* @param request The request that was sent to the server
|
||||||
|
* @param data The response to parse
|
||||||
|
*/
|
||||||
|
parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse {
|
||||||
|
const isSuccessful = data.statusCode >= 200 && data.statusCode < 300;
|
||||||
|
return new RestResponse(isSuccessful, data.statusCode, data.statusText);
|
||||||
|
}
|
||||||
|
}
|
@@ -26,7 +26,7 @@ export interface HttpOptions {
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class DSpaceRESTv2Service {
|
export class DSpaceRESTv2Service {
|
||||||
|
|
||||||
constructor(private http: HttpClient) {
|
constructor(protected http: HttpClient) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -9,6 +9,7 @@ import { Group } from './group.model';
|
|||||||
@mapsTo(EPerson)
|
@mapsTo(EPerson)
|
||||||
@inheritSerialization(NormalizedDSpaceObject)
|
@inheritSerialization(NormalizedDSpaceObject)
|
||||||
export class NormalizedEPerson extends NormalizedDSpaceObject<EPerson> implements CacheableObject {
|
export class NormalizedEPerson extends NormalizedDSpaceObject<EPerson> implements CacheableObject {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A string representing the unique handle of this EPerson
|
* 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
|
* Class the represents a metadata field
|
||||||
*/
|
*/
|
||||||
export class MetadataField implements ListableObject {
|
export class MetadataField extends ListableObject {
|
||||||
static type = new ResourceType('metadatafield');
|
static type = new ResourceType('metadatafield');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -5,7 +5,7 @@ import { GenericConstructor } from '../shared/generic-constructor';
|
|||||||
/**
|
/**
|
||||||
* Class that represents a metadata schema
|
* Class that represents a metadata schema
|
||||||
*/
|
*/
|
||||||
export class MetadataSchema implements ListableObject {
|
export class MetadataSchema extends ListableObject {
|
||||||
static type = new ResourceType('metadataschema');
|
static type = new ResourceType('metadataschema');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -2,7 +2,6 @@ import { autoserialize, deserialize, inheritSerialization } from 'cerialize';
|
|||||||
import { mapsTo, relationship } from '../cache/builders/build-decorators';
|
import { mapsTo, relationship } from '../cache/builders/build-decorators';
|
||||||
import { MetadataField } from './metadata-field.model';
|
import { MetadataField } from './metadata-field.model';
|
||||||
import { NormalizedObject } from '../cache/models/normalized-object.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';
|
import { MetadataSchema } from './metadata-schema.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
import { autoserialize, inheritSerialization } from 'cerialize';
|
import { autoserialize, inheritSerialization } from 'cerialize';
|
||||||
import { NormalizedObject } from '../cache/models/normalized-object.model';
|
import { NormalizedObject } from '../cache/models/normalized-object.model';
|
||||||
import { mapsTo } from '../cache/builders/build-decorators';
|
import { mapsTo } from '../cache/builders/build-decorators';
|
||||||
import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
|
|
||||||
import { MetadataSchema } from './metadata-schema.model';
|
import { MetadataSchema } from './metadata-schema.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,4 +32,5 @@ export class NormalizedMetadataSchema extends NormalizedObject<MetadataSchema> {
|
|||||||
*/
|
*/
|
||||||
@autoserialize
|
@autoserialize
|
||||||
namespace: string;
|
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'),
|
SET_PARAMETERS: type('dspace/core/route/SET_PARAMETERS'),
|
||||||
ADD_QUERY_PARAMETER: type('dspace/core/route/ADD_QUERY_PARAMETER'),
|
ADD_QUERY_PARAMETER: type('dspace/core/route/ADD_QUERY_PARAMETER'),
|
||||||
ADD_PARAMETER: type('dspace/core/route/ADD_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'),
|
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
|
* An ngrx action to reset the route state
|
||||||
*/
|
*/
|
||||||
@@ -113,4 +161,5 @@ export type RouteActions =
|
|||||||
| SetParametersAction
|
| SetParametersAction
|
||||||
| AddQueryParameterAction
|
| AddQueryParameterAction
|
||||||
| AddParameterAction
|
| 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 { Injectable } from '@angular/core';
|
||||||
import { Actions, Effect, ofType } from '@ngrx/effects'
|
import { Actions, Effect, ofType } from '@ngrx/effects'
|
||||||
import * as fromRouter from '@ngrx/router-store';
|
import { ResetRouteStateAction, RouteActionTypes } from './route.actions';
|
||||||
import { ResetRouteStateAction } from './route.actions';
|
import { RouterActionTypes } from '../../core/router/router.actions';
|
||||||
|
import { RouteService } from './route.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RouteEffects {
|
export class RouteEffects {
|
||||||
@@ -12,12 +13,16 @@ export class RouteEffects {
|
|||||||
*/
|
*/
|
||||||
@Effect() routeChange$ = this.actions$
|
@Effect() routeChange$ = this.actions$
|
||||||
.pipe(
|
.pipe(
|
||||||
ofType(fromRouter.ROUTER_NAVIGATION),
|
ofType(RouterActionTypes.ROUTE_UPDATE),
|
||||||
map(() => new ResetRouteStateAction())
|
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,
|
AddParameterAction,
|
||||||
AddQueryParameterAction,
|
AddQueryParameterAction,
|
||||||
RouteActions,
|
RouteActions,
|
||||||
RouteActionTypes, SetParametersAction, SetQueryParametersAction
|
RouteActionTypes,
|
||||||
|
SetParameterAction,
|
||||||
|
SetParametersAction,
|
||||||
|
SetQueryParameterAction,
|
||||||
|
SetQueryParametersAction
|
||||||
} from './route.actions';
|
} from './route.actions';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -44,6 +48,12 @@ export function routeReducer(state = initialState, action: RouteActions): RouteS
|
|||||||
case RouteActionTypes.ADD_QUERY_PARAMETER: {
|
case RouteActionTypes.ADD_QUERY_PARAMETER: {
|
||||||
return addParameter(state, action as AddQueryParameterAction, 'queryParams');
|
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: {
|
default: {
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
@@ -60,9 +70,10 @@ function addParameter(state: RouteState, action: AddParameterAction | AddQueryPa
|
|||||||
const subState = state[paramType];
|
const subState = state[paramType];
|
||||||
const existingValues = subState[action.payload.key] || [];
|
const existingValues = subState[action.payload.key] || [];
|
||||||
const newValues = [...existingValues, action.payload.value];
|
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 });
|
return Object.assign({}, state, { [paramType]: newSubstate });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a route or query parameter in the store
|
* Set a route or query parameter in the store
|
||||||
* @param state The current state
|
* @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
|
* @param paramType The type of parameter to set: route or query parameter
|
||||||
*/
|
*/
|
||||||
function setParameters(state: RouteState, action: SetParametersAction | SetQueryParametersAction, paramType: string): RouteState {
|
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 { Injectable } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
ActivatedRoute,
|
ActivatedRoute,
|
||||||
@@ -12,12 +12,17 @@ import { combineLatest, Observable } from 'rxjs';
|
|||||||
import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store';
|
import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash';
|
||||||
|
|
||||||
import { AddUrlToHistoryAction } from '../../shared/history/history.actions';
|
import {
|
||||||
import { historySelector } from '../../shared/history/selectors';
|
AddParameterAction,
|
||||||
import { SetParametersAction, SetQueryParametersAction } from './route.actions';
|
SetParameterAction,
|
||||||
import { CoreState } from '../core.reducers';
|
SetParametersAction,
|
||||||
|
SetQueryParametersAction
|
||||||
|
} from './route.actions';
|
||||||
|
import { CoreState } from '../../core/core.reducers';
|
||||||
|
import { coreSelector } from '../../core/core.selectors';
|
||||||
import { hasValue } from '../../shared/empty.util';
|
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
|
* Selector to select all route parameters from the store
|
||||||
@@ -121,7 +126,7 @@ export class RouteService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getRouteDataValue(datafield: string): Observable<any> {
|
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 {
|
public saveRouting(): void {
|
||||||
combineLatest(this.router.events, this.getRouteParams(), this.route.queryParams)
|
this.router.events
|
||||||
.pipe(filter(([event, params, queryParams]) => event instanceof NavigationEnd))
|
.pipe(filter((event) => event instanceof NavigationEnd))
|
||||||
.subscribe(([event, params, queryParams]: [NavigationEnd, Params, Params]) => {
|
.subscribe((event: NavigationEnd) => {
|
||||||
this.store.dispatch(new SetParametersAction(params));
|
|
||||||
this.store.dispatch(new SetQueryParametersAction(queryParams));
|
|
||||||
this.store.dispatch(new AddUrlToHistoryAction(event.urlAfterRedirects));
|
this.store.dispatch(new AddUrlToHistoryAction(event.urlAfterRedirects));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -183,4 +186,26 @@ export class RouteService {
|
|||||||
map((history: string[]) => history[history.length - 2] || '')
|
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 { TypedObject } from '../cache/object-cache.reducer';
|
||||||
import { ResourceType } from './resource-type';
|
import { ResourceType } from './resource-type';
|
||||||
import { GenericConstructor } from './generic-constructor';
|
import { GenericConstructor } from './generic-constructor';
|
||||||
|
import { excludeFromEquals } from '../utilities/equals.decorators';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class object representing a browse entry
|
* Class object representing a browse entry
|
||||||
* This class is not normalized because browse entries do not have self links
|
* 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');
|
static type = new ResourceType('browseEntry');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -28,6 +29,7 @@ export class BrowseEntry implements ListableObject {
|
|||||||
/**
|
/**
|
||||||
* The count of this browse entry
|
* The count of this browse entry
|
||||||
*/
|
*/
|
||||||
|
@excludeFromEquals
|
||||||
count: number;
|
count: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -11,25 +11,29 @@ import { hasNoValue, isUndefined } from '../../shared/empty.util';
|
|||||||
import { CacheableObject } from '../cache/object-cache.reducer';
|
import { CacheableObject } from '../cache/object-cache.reducer';
|
||||||
import { RemoteData } from '../data/remote-data';
|
import { RemoteData } from '../data/remote-data';
|
||||||
import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
|
import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
|
||||||
|
import { excludeFromEquals } from '../utilities/equals.decorators';
|
||||||
import { ResourceType } from './resource-type';
|
import { ResourceType } from './resource-type';
|
||||||
import { GenericConstructor } from './generic-constructor';
|
import { GenericConstructor } from './generic-constructor';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An abstract model class for a DSpaceObject.
|
* 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, …
|
* A string representing the kind of DSpaceObject, e.g. community, item, …
|
||||||
*/
|
*/
|
||||||
static type = new ResourceType('dspaceobject');
|
static type = new ResourceType('dspaceobject');
|
||||||
|
|
||||||
|
@excludeFromEquals
|
||||||
private _name: string;
|
private _name: string;
|
||||||
|
|
||||||
|
@excludeFromEquals
|
||||||
self: string;
|
self: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The human-readable identifier of this DSpaceObject
|
* The human-readable identifier of this DSpaceObject
|
||||||
*/
|
*/
|
||||||
|
@excludeFromEquals
|
||||||
id: string;
|
id: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -37,6 +41,12 @@ export class DSpaceObject implements CacheableObject, ListableObject {
|
|||||||
*/
|
*/
|
||||||
uuid: string;
|
uuid: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A string representing the kind of DSpaceObject, e.g. community, item, …
|
||||||
|
*/
|
||||||
|
@excludeFromEquals
|
||||||
|
type: ResourceType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The name for this DSpaceObject
|
* The name for this DSpaceObject
|
||||||
*/
|
*/
|
||||||
@@ -54,6 +64,7 @@ export class DSpaceObject implements CacheableObject, ListableObject {
|
|||||||
/**
|
/**
|
||||||
* All metadata of this DSpaceObject
|
* All metadata of this DSpaceObject
|
||||||
*/
|
*/
|
||||||
|
@excludeFromEquals
|
||||||
metadata: MetadataMap;
|
metadata: MetadataMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -66,11 +77,13 @@ export class DSpaceObject implements CacheableObject, ListableObject {
|
|||||||
/**
|
/**
|
||||||
* An array of DSpaceObjects that are direct parents of this DSpaceObject
|
* An array of DSpaceObjects that are direct parents of this DSpaceObject
|
||||||
*/
|
*/
|
||||||
|
@excludeFromEquals
|
||||||
parents: Observable<RemoteData<DSpaceObject[]>>;
|
parents: Observable<RemoteData<DSpaceObject[]>>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The DSpaceObject that owns this DSpaceObject
|
* The DSpaceObject that owns this DSpaceObject
|
||||||
*/
|
*/
|
||||||
|
@excludeFromEquals
|
||||||
owner: Observable<RemoteData<DSpaceObject>>;
|
owner: Observable<RemoteData<DSpaceObject>>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -12,6 +12,8 @@ export class ItemType implements CacheableObject {
|
|||||||
*/
|
*/
|
||||||
id: string;
|
id: string;
|
||||||
|
|
||||||
|
label: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The link to the rest endpoint where this object can be found
|
* The link to the rest endpoint where this object can be found
|
||||||
*/
|
*/
|
||||||
|
@@ -46,6 +46,16 @@ export class Relationship implements CacheableObject {
|
|||||||
*/
|
*/
|
||||||
rightPlace: number;
|
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
|
* The type of Relationship
|
||||||
*/
|
*/
|
||||||
|
@@ -81,6 +81,9 @@ export interface MetadataValueFilter {
|
|||||||
/** The value constraint. */
|
/** The value constraint. */
|
||||||
value?: string;
|
value?: string;
|
||||||
|
|
||||||
|
/** The authority constraint. */
|
||||||
|
authority?: string;
|
||||||
|
|
||||||
/** Whether the value constraint should match without regard to case. */
|
/** Whether the value constraint should match without regard to case. */
|
||||||
ignoreCase?: boolean;
|
ignoreCase?: boolean;
|
||||||
|
|
||||||
|
@@ -8,8 +8,8 @@ import {
|
|||||||
} from './metadata.models';
|
} from './metadata.models';
|
||||||
import { Metadata } from './metadata.utils';
|
import { Metadata } from './metadata.utils';
|
||||||
|
|
||||||
const mdValue = (value: string, language?: string): MetadataValue => {
|
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: undefined, confidence: undefined });
|
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');
|
const dcDescription = mdValue('Some description');
|
||||||
@@ -184,6 +184,8 @@ describe('Metadata', () => {
|
|||||||
testValueMatches(mdValue('a'), true, { language: null });
|
testValueMatches(mdValue('a'), true, { language: null });
|
||||||
testValueMatches(mdValue('a'), false, { language: 'en_US' });
|
testValueMatches(mdValue('a'), false, { language: 'en_US' });
|
||||||
testValueMatches(mdValue('a', 'en_US'), true, { 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', () => {
|
describe('toViewModelList method', () => {
|
||||||
|
@@ -127,6 +127,8 @@ export class Metadata {
|
|||||||
return true;
|
return true;
|
||||||
} else if (filter.language && filter.language !== mdValue.language) {
|
} else if (filter.language && filter.language !== mdValue.language) {
|
||||||
return false;
|
return false;
|
||||||
|
} else if (filter.authority && filter.authority !== mdValue.authority) {
|
||||||
|
return false;
|
||||||
} else if (filter.value) {
|
} else if (filter.value) {
|
||||||
let fValue = filter.value;
|
let fValue = filter.value;
|
||||||
let mValue = mdValue.value;
|
let mValue = mdValue.value;
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user