Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 22 additions & 4 deletions resources/i18n/en.json5
Original file line number Diff line number Diff line change
Expand Up @@ -1566,6 +1566,8 @@

"search.results.no-results-link": "quotes around it",

"search.results.empty": "Your search returned no results.",



"search.sidebar.close": "Back to results",
Expand Down Expand Up @@ -1639,13 +1641,21 @@

"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.Author": "Local Authors ({{ count }})",

"submission.sections.describe.relationship-lookup.search-tab.tab-title.Journal": "Local Journals ({{ count }})",

"submission.sections.describe.relationship-lookup.search-tab.tab-title.Journal Issue": "Local Journal Issues ({{ count }})",

"submission.sections.describe.relationship-lookup.search-tab.tab-title.Journal Volume": "Local Journal Volumes ({{ count }})",

"submission.sections.describe.relationship-lookup.search-tab.tab-title.sherpaJournal": "Sherpa Journals ({{ count }})",

"submission.sections.describe.relationship-lookup.search-tab.tab-title.Journal": "Search for Journals",
"submission.sections.describe.relationship-lookup.search-tab.tab-title.sherpaPublisher": "Sherpa Publishers ({{ count }})",

"submission.sections.describe.relationship-lookup.search-tab.tab-title.Journal Issue": "Search for Journal Issues",
"submission.sections.describe.relationship-lookup.search-tab.tab-title.orcidV2": "ORCID ({{ count }})",

"submission.sections.describe.relationship-lookup.search-tab.tab-title.Journal Volume": "Search for Journal Volumes",
"submission.sections.describe.relationship-lookup.search-tab.tab-title.lcname": "LC Names ({{ count }})",

"submission.sections.describe.relationship-lookup.search-tab.tab-title.Funding Agency": "Search for Funding Agencies",

Expand Down Expand Up @@ -1679,6 +1689,14 @@

"submission.sections.describe.relationship-lookup.selection-tab.title.Journal Issue": "Selected Issue",

"submission.sections.describe.relationship-lookup.selection-tab.title.sherpaJournal": "Search Results",

"submission.sections.describe.relationship-lookup.selection-tab.title.sherpaPublisher": "Search Results",

"submission.sections.describe.relationship-lookup.selection-tab.title.orcidV2": "Search Results",

"submission.sections.describe.relationship-lookup.selection-tab.title.lcname": "Search Results",

"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",
Expand Down
5 changes: 4 additions & 1 deletion src/app/+search-page/configuration-search-page.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import { ChangeDetectionStrategy, Component, Inject, Input, OnInit } from '@angu
import { pushInOut } from '../shared/animations/push';
import { SEARCH_CONFIG_SERVICE } from '../+my-dspace-page/my-dspace-page.component';
import { SearchConfigurationService } from '../core/shared/search/search-configuration.service';
import { Router } from '@angular/router';
import { hasValue } from '../shared/empty.util';
import { RouteService } from '../core/services/route.service';
import { SearchService } from '../core/shared/search/search.service';
import { Router } from '@angular/router';

/**
* This component renders a search page using a configuration as input.
Expand Down Expand Up @@ -61,5 +61,8 @@ export class ConfigurationSearchPageComponent extends SearchComponent implements
if (hasValue(this.configuration)) {
this.routeService.setParameter('configuration', this.configuration);
}
if (hasValue(this.fixedFilterQuery)) {
this.routeService.setParameter('fixedFilter', this.fixedFilterQuery);
}
}
}
27 changes: 22 additions & 5 deletions src/app/+search-page/search-page.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,47 @@ import { CommonModule } from '@angular/common';
import { CoreModule } from '../core/core.module';
import { SharedModule } from '../shared/shared.module';
import { SearchPageRoutingModule } from './search-page-routing.module';
import { SearchPageComponent } from './search-page.component';
import { SearchComponent } from './search.component';
import { SidebarService } from '../shared/sidebar/sidebar.service';
import { SidebarEffects } from '../shared/sidebar/sidebar-effects.service';
import { EffectsModule } from '@ngrx/effects';
import { ConfigurationSearchPageComponent } from './configuration-search-page.component';
import { ConfigurationSearchPageGuard } from './configuration-search-page.guard';
import { SearchTrackerComponent } from './search-tracker.component';
import { SearchPageComponent } from './search-page.component';
import { SidebarFilterService } from '../shared/sidebar/filter/sidebar-filter.service';
import { StatisticsModule } from '../statistics/statistics.module';
import { SearchComponent } from './search.component';
import { SearchTrackerComponent } from './search-tracker.component';
import { SearchFilterService } from '../core/shared/search/search-filter.service';
import { SearchConfigurationService } from '../core/shared/search/search-configuration.service';

const effects = [
SidebarEffects
];

const components = [
SearchPageComponent,
SearchComponent,
ConfigurationSearchPageComponent,
SearchTrackerComponent
SearchTrackerComponent,
];

@NgModule({
imports: [
SearchPageRoutingModule,
CommonModule,
SharedModule,
EffectsModule.forFeature(effects),
CoreModule.forRoot(),
StatisticsModule.forRoot(),
],
providers: [ConfigurationSearchPageGuard],
declarations: components,
providers: [
SidebarService,
SidebarFilterService,
SearchFilterService,
ConfigurationSearchPageGuard,
SearchConfigurationService
],
exports: components
})

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize';
import { NormalizedObject } from './normalized-object.model';
import { ExternalSourceEntry } from '../../shared/external-source-entry.model';
import { mapsTo } from '../builders/build-decorators';
import { MetadataMap, MetadataMapSerializer } from '../../shared/metadata.models';

/**
* Normalized model class for an external source entry
*/
@mapsTo(ExternalSourceEntry)
@inheritSerialization(NormalizedObject)
export class NormalizedExternalSourceEntry extends NormalizedObject<ExternalSourceEntry> {
/**
* Unique identifier
*/
@autoserialize
id: string;

/**
* The value to display
*/
@autoserialize
display: string;

/**
* The value to store the entry with
*/
@autoserialize
value: string;

/**
* Metadata of the entry
*/
@autoserializeAs(MetadataMapSerializer)
metadata: MetadataMap;
}
29 changes: 29 additions & 0 deletions src/app/core/cache/models/normalized-external-source.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { autoserialize, inheritSerialization } from 'cerialize';
import { NormalizedObject } from './normalized-object.model';
import { ExternalSource } from '../../shared/external-source.model';
import { mapsTo } from '../builders/build-decorators';

/**
* Normalized model class for an external source
*/
@mapsTo(ExternalSource)
@inheritSerialization(NormalizedObject)
export class NormalizedExternalSource extends NormalizedObject<ExternalSource> {
/**
* Unique identifier
*/
@autoserialize
id: string;

/**
* The name of this external source
*/
@autoserialize
name: string;

/**
* Is the source hierarchical?
*/
@autoserialize
hierarchical: boolean;
}
10 changes: 9 additions & 1 deletion src/app/core/core.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ import { SearchConfigurationService } from './shared/search/search-configuration
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';
import { NormalizedExternalSource } from './cache/models/normalized-external-source.model';
import { NormalizedExternalSourceEntry } from './cache/models/normalized-external-source-entry.model';
import { ExternalSourceService } from './data/external-source.service';
import { LookupRelationService } from './data/lookup-relation.service';

/**
* When not in production, endpoint responses can be mocked for testing purposes
Expand Down Expand Up @@ -247,6 +251,8 @@ const PROVIDERS = [
SearchConfigurationService,
SelectableListService,
RelationshipTypeService,
ExternalSourceService,
LookupRelationService,
// register AuthInterceptor as HttpInterceptor
{
provide: HTTP_INTERCEPTORS,
Expand Down Expand Up @@ -292,7 +298,9 @@ export const normalizedModels =
NormalizedPoolTask,
NormalizedRelationship,
NormalizedRelationshipType,
NormalizedItemType
NormalizedItemType,
NormalizedExternalSource,
NormalizedExternalSourceEntry
];

@NgModule({
Expand Down
76 changes: 76 additions & 0 deletions src/app/core/data/external-source.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { ExternalSourceService } from './external-source.service';
import { createPaginatedList, createSuccessfulRemoteDataObject$ } from '../../shared/testing/utils';
import { ExternalSourceEntry } from '../shared/external-source-entry.model';
import { of as observableOf } from 'rxjs';
import { GetRequest } from './request.models';

describe('ExternalSourceService', () => {
let service: ExternalSourceService;

let requestService;
let rdbService;
let halService;

const entries = [
Object.assign(new ExternalSourceEntry(), {
id: '0001-0001-0001-0001',
display: 'John Doe',
value: 'John, Doe',
metadata: {
'dc.identifier.uri': [
{
value: 'https://orcid.org/0001-0001-0001-0001'
}
]
}
}),
Object.assign(new ExternalSourceEntry(), {
id: '0001-0001-0001-0002',
display: 'Sampson Megan',
value: 'Sampson, Megan',
metadata: {
'dc.identifier.uri': [
{
value: 'https://orcid.org/0001-0001-0001-0002'
}
]
}
})
];

function init() {
requestService = jasmine.createSpyObj('requestService', {
generateRequestId: 'request-uuid',
configure: {}
});
rdbService = jasmine.createSpyObj('rdbService', {
buildList: createSuccessfulRemoteDataObject$(createPaginatedList(entries))
});
halService = jasmine.createSpyObj('halService', {
getEndpoint: observableOf('external-sources-REST-endpoint')
});
service = new ExternalSourceService(requestService, rdbService, undefined, undefined, undefined, halService, undefined, undefined, undefined);
}

beforeEach(() => {
init();
});

describe('getExternalSourceEntries', () => {
let result;

beforeEach(() => {
result = service.getExternalSourceEntries('test');
});

it('should configure a GetRequest', () => {
expect(requestService.configure).toHaveBeenCalledWith(jasmine.any(GetRequest));
});

it('should return the entries', () => {
result.subscribe((resultRD) => {
expect(resultRD.payload.page).toBe(entries);
});
});
});
});
85 changes: 85 additions & 0 deletions src/app/core/data/external-source.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { Injectable } from '@angular/core';
import { DataService } from './data.service';
import { ExternalSource } from '../shared/external-source.model';
import { RequestService } from './request.service';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
import { Store } from '@ngrx/store';
import { CoreState } from '../core.reducers';
import { ObjectCacheService } from '../cache/object-cache.service';
import { HALEndpointService } from '../shared/hal-endpoint.service';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { HttpClient } from '@angular/common/http';
import { FindListOptions, GetRequest } from './request.models';
import { Observable } from 'rxjs/internal/Observable';
import { distinctUntilChanged, map, switchMap } from 'rxjs/operators';
import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model';
import { hasValue, isNotEmptyOperator } from '../../shared/empty.util';
import { configureRequest } from '../shared/operators';
import { RemoteData } from './remote-data';
import { PaginatedList } from './paginated-list';
import { ExternalSourceEntry } from '../shared/external-source-entry.model';
import { DefaultChangeAnalyzer } from './default-change-analyzer.service';

/**
* A service handling all external source requests
*/
@Injectable()
export class ExternalSourceService extends DataService<ExternalSource> {
protected linkPath = 'externalsources';

constructor(
protected requestService: RequestService,
protected rdbService: RemoteDataBuildService,
protected dataBuildService: NormalizedObjectBuildService,
protected store: Store<CoreState>,
protected objectCache: ObjectCacheService,
protected halService: HALEndpointService,
protected notificationsService: NotificationsService,
protected http: HttpClient,
protected comparator: DefaultChangeAnalyzer<ExternalSource>) {
super();
}

/**
* Get the endpoint to browse external sources
* @param options
* @param linkPath
*/
getBrowseEndpoint(options: FindListOptions = {}, linkPath: string = this.linkPath): Observable<string> {
return this.halService.getEndpoint(linkPath);
}

/**
* Get the endpoint for an external source's entries
* @param externalSourceId The id of the external source to fetch entries for
*/
getEntriesEndpoint(externalSourceId: string): Observable<string> {
return this.getBrowseEndpoint().pipe(
map((href) => this.getIDHref(href, externalSourceId)),
switchMap((href) => this.halService.getEndpoint('entries', href))
);
}

/**
* Get the entries for an external source
* @param externalSourceId The id of the external source to fetch entries for
* @param searchOptions The search options to limit results to
*/
getExternalSourceEntries(externalSourceId: string, searchOptions?: PaginatedSearchOptions): Observable<RemoteData<PaginatedList<ExternalSourceEntry>>> {
const requestUuid = this.requestService.generateRequestId();

const href$ = this.getEntriesEndpoint(externalSourceId).pipe(
isNotEmptyOperator(),
distinctUntilChanged(),
map((endpoint: string) => hasValue(searchOptions) ? searchOptions.toRestUrl(endpoint) : endpoint)
);

href$.pipe(
map((endpoint: string) => new GetRequest(requestUuid, endpoint)),
configureRequest(this.requestService)
).subscribe();

return this.rdbService.buildList(href$);
}
}
Loading