[CST-4591] Use search collection dropdown when importing from external source with lookup modal

This commit is contained in:
Giuseppe Digilio
2021-09-24 13:20:36 +02:00
parent c6a73f2dcb
commit d29c27b400
9 changed files with 90 additions and 32 deletions

View File

@@ -1,10 +1,15 @@
import { autoserialize, deserialize } from 'cerialize'; import { autoserialize, deserialize } from 'cerialize';
import { typedObject } from '../cache/builders/build-decorators'; import { link, typedObject } from '../cache/builders/build-decorators';
import { CacheableObject } from '../cache/object-cache.reducer'; import { CacheableObject } from '../cache/object-cache.reducer';
import { excludeFromEquals } from '../utilities/equals.decorators'; import { excludeFromEquals } from '../utilities/equals.decorators';
import { EXTERNAL_SOURCE } from './external-source.resource-type'; import { EXTERNAL_SOURCE } from './external-source.resource-type';
import { HALLink } from './hal-link.model'; import { HALLink } from './hal-link.model';
import { ResourceType } from './resource-type'; import { ResourceType } from './resource-type';
import { RemoteData } from '../data/remote-data';
import { PaginatedList } from '../data/paginated-list.model';
import { Observable } from 'rxjs/internal/Observable';
import { ITEM_TYPE } from './item-relationships/item-type.resource-type';
import { ItemType } from './item-relationships/item-type.model';
/** /**
* Model class for an external source * Model class for an external source
@@ -38,6 +43,13 @@ export class ExternalSource extends CacheableObject {
@autoserialize @autoserialize
hierarchical: boolean; hierarchical: boolean;
/**
* The list of entity types that are compatible with this external source
* Will be undefined unless the entityTypes {@link HALLink} has been resolved.
*/
@link(ITEM_TYPE, true)
entityTypes?: Observable<RemoteData<PaginatedList<ItemType>>>;
/** /**
* The {@link HALLink}s for this ExternalSource * The {@link HALLink}s for this ExternalSource
*/ */
@@ -45,5 +57,6 @@ export class ExternalSource extends CacheableObject {
_links: { _links: {
self: HALLink; self: HALLink;
entries: HALLink; entries: HALLink;
entityTypes: HALLink;
}; };
} }

View File

@@ -12,7 +12,9 @@ import { RelationshipOptions } from '../../models/relationship-options.model';
import { SearchResult } from '../../../../search/search-result.model'; import { SearchResult } from '../../../../search/search-result.model';
import { Item } from '../../../../../core/shared/item.model'; import { Item } from '../../../../../core/shared/item.model';
import { import {
AddRelationshipAction, RemoveRelationshipAction, UpdateRelationshipNameVariantAction, AddRelationshipAction,
RemoveRelationshipAction,
UpdateRelationshipNameVariantAction,
} from './relationship.actions'; } from './relationship.actions';
import { RelationshipService } from '../../../../../core/data/relationship.service'; import { RelationshipService } from '../../../../../core/data/relationship.service';
import { RelationshipTypeService } from '../../../../../core/data/relationship-type.service'; import { RelationshipTypeService } from '../../../../../core/data/relationship-type.service';
@@ -25,6 +27,7 @@ import { ExternalSourceService } from '../../../../../core/data/external-source.
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { RemoteDataBuildService } from '../../../../../core/cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../../../../../core/cache/builders/remote-data-build.service';
import { getAllSucceededRemoteDataPayload } from '../../../../../core/shared/operators'; import { getAllSucceededRemoteDataPayload } from '../../../../../core/shared/operators';
import { followLink } from '../../../../utils/follow-link-config.model';
@Component({ @Component({
selector: 'ds-dynamic-lookup-relation-modal', selector: 'ds-dynamic-lookup-relation-modal',
@@ -146,7 +149,14 @@ export class DsDynamicLookupRelationModalComponent implements OnInit, OnDestroy
if (isNotEmpty(this.relationshipOptions.externalSources)) { if (isNotEmpty(this.relationshipOptions.externalSources)) {
this.externalSourcesRD$ = this.rdbService.aggregate( this.externalSourcesRD$ = this.rdbService.aggregate(
this.relationshipOptions.externalSources.map((source) => this.externalSourceService.findById(source)) this.relationshipOptions.externalSources.map((source) => {
return this.externalSourceService.findById(
source,
true,
true,
followLink('entityTypes')
);
})
).pipe( ).pipe(
getAllSucceededRemoteDataPayload() getAllSucceededRemoteDataPayload()
); );

View File

@@ -26,6 +26,7 @@ import { ExternalSourceEntryImportModalComponent } from './external-source-entry
import { createPaginatedList } from '../../../../../testing/utils.test'; import { createPaginatedList } from '../../../../../testing/utils.test';
import { PaginationService } from '../../../../../../core/pagination/pagination.service'; import { PaginationService } from '../../../../../../core/pagination/pagination.service';
import { PaginationServiceStub } from '../../../../../testing/pagination-service.stub'; import { PaginationServiceStub } from '../../../../../testing/pagination-service.stub';
import { ItemType } from '../../../../../../core/shared/item-relationships/item-type.model';
describe('DsDynamicLookupRelationExternalSourceTabComponent', () => { describe('DsDynamicLookupRelationExternalSourceTabComponent', () => {
let component: DsDynamicLookupRelationExternalSourceTabComponent; let component: DsDynamicLookupRelationExternalSourceTabComponent;
@@ -35,10 +36,12 @@ describe('DsDynamicLookupRelationExternalSourceTabComponent', () => {
let selectableListService; let selectableListService;
let modalService; let modalService;
const itemType = Object.assign(new ItemType(), { label: 'Person' });
const externalSource = { const externalSource = {
id: 'orcidV2', id: 'orcidV2',
name: 'orcidV2', name: 'orcidV2',
hierarchical: false hierarchical: false,
entityTypes: createSuccessfulRemoteDataObject$(createPaginatedList([itemType]))
} as ExternalSource; } as ExternalSource;
const externalEntries = [ const externalEntries = [
Object.assign({ Object.assign({

View File

@@ -7,7 +7,7 @@ import { RemoteData } from '../../../../../../core/data/remote-data';
import { PaginatedList } from '../../../../../../core/data/paginated-list.model'; import { PaginatedList } from '../../../../../../core/data/paginated-list.model';
import { ExternalSourceEntry } from '../../../../../../core/shared/external-source-entry.model'; import { ExternalSourceEntry } from '../../../../../../core/shared/external-source-entry.model';
import { ExternalSource } from '../../../../../../core/shared/external-source.model'; import { ExternalSource } from '../../../../../../core/shared/external-source.model';
import { startWith, switchMap } from 'rxjs/operators'; import { map, startWith, switchMap } from 'rxjs/operators';
import { PaginatedSearchOptions } from '../../../../../search/paginated-search-options.model'; import { PaginatedSearchOptions } from '../../../../../search/paginated-search-options.model';
import { Context } from '../../../../../../core/shared/context.model'; import { Context } from '../../../../../../core/shared/context.model';
import { ListableObject } from '../../../../../object-collection/shared/listable-object.model'; import { ListableObject } from '../../../../../object-collection/shared/listable-object.model';
@@ -22,6 +22,8 @@ import { Item } from '../../../../../../core/shared/item.model';
import { Collection } from '../../../../../../core/shared/collection.model'; import { Collection } from '../../../../../../core/shared/collection.model';
import { PaginationService } from '../../../../../../core/pagination/pagination.service'; import { PaginationService } from '../../../../../../core/pagination/pagination.service';
import { Observable, Subscription } from 'rxjs'; import { Observable, Subscription } from 'rxjs';
import { ItemType } from '../../../../../../core/shared/item-relationships/item-type.model';
import { getFirstCompletedRemoteData } from '../../../../../../core/shared/operators';
@Component({ @Component({
selector: 'ds-dynamic-lookup-relation-external-source-tab', selector: 'ds-dynamic-lookup-relation-external-source-tab',
@@ -116,6 +118,11 @@ export class DsDynamicLookupRelationExternalSourceTabComponent implements OnInit
*/ */
importObjectSub: Subscription; importObjectSub: Subscription;
/**
* The entity types compatible with the given external source
*/
relatedEntityType: ItemType;
constructor(private router: Router, constructor(private router: Router,
public searchConfigService: SearchConfigurationService, public searchConfigService: SearchConfigurationService,
private externalSourceService: ExternalSourceService, private externalSourceService: ExternalSourceService,
@@ -129,6 +136,15 @@ export class DsDynamicLookupRelationExternalSourceTabComponent implements OnInit
* Get the entries for the selected external source * Get the entries for the selected external source
*/ */
ngOnInit(): void { ngOnInit(): void {
this.externalSource.entityTypes.pipe(
getFirstCompletedRemoteData(),
map((entityTypesRD: RemoteData<PaginatedList<ItemType>>) => {
return (entityTypesRD.hasSucceeded && entityTypesRD.payload.totalElements > 0) ? entityTypesRD.payload.page[0] : null;
})
).subscribe((entityType: ItemType) => {
this.relatedEntityType = entityType;
});
this.resetRoute(); this.resetRoute();
this.entriesRD$ = this.searchConfigService.paginatedSearchOptions.pipe( this.entriesRD$ = this.searchConfigService.paginatedSearchOptions.pipe(
switchMap((searchOptions: PaginatedSearchOptions) => switchMap((searchOptions: PaginatedSearchOptions) =>
@@ -155,6 +171,7 @@ export class DsDynamicLookupRelationExternalSourceTabComponent implements OnInit
modalComp.collection = this.collection; modalComp.collection = this.collection;
modalComp.relationship = this.relationship; modalComp.relationship = this.relationship;
modalComp.label = this.label; modalComp.label = this.label;
modalComp.relatedEntityType = this.relatedEntityType;
this.importObjectSub = modalComp.importedObject.subscribe((object) => { this.importObjectSub = modalComp.importedObject.subscribe((object) => {
this.selectableListService.selectSingle(this.listId, object); this.selectableListService.selectSingle(this.listId, object);
this.importedObject.emit(object); this.importedObject.emit(object);

View File

@@ -17,13 +17,6 @@
<div id="external-source-entry-entities" class="mb-3"> <div id="external-source-entry-entities" class="mb-3">
<h5 class="font-weight-bold">{{ (labelPrefix + 'entities' | translate) }}</h5> <h5 class="font-weight-bold">{{ (labelPrefix + 'entities' | translate) }}</h5>
<div id="external-source-entry-collection" class="mb-3">
<div class="form-group">
<label for="collection">{{ (labelPrefix + 'collection' | translate) }}</label>
<input type="text" class="form-control" id="collection" placeholder="Enter collection ID" [(ngModel)]="collectionId">
</div>
</div>
<ds-search-results *ngIf="(localEntitiesRD$ | async)?.payload?.page?.length > 0" <ds-search-results *ngIf="(localEntitiesRD$ | async)?.payload?.page?.length > 0"
[searchResults]="(localEntitiesRD$ | async)" [searchResults]="(localEntitiesRD$ | async)"
[sortConfig]="this.lookupRelationService.searchConfig?.sort" [sortConfig]="this.lookupRelationService.searchConfig?.sort"

View File

@@ -86,7 +86,6 @@ describe('DsDynamicLookupRelationExternalSourceTabComponent', () => {
component.externalSourceEntry = entry; component.externalSourceEntry = entry;
component.label = label; component.label = label;
component.relationship = relationship; component.relationship = relationship;
component.collection = submissionCollection;
component.item = submissionItem; component.item = submissionItem;
fixture.detectChanges(); fixture.detectChanges();
}); });

View File

@@ -1,5 +1,5 @@
import { Component, EventEmitter, OnInit } from '@angular/core'; import { Component, EventEmitter, OnInit } from '@angular/core';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { NgbActiveModal, NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
import { ExternalSourceEntry } from '../../../../../../../core/shared/external-source-entry.model'; import { ExternalSourceEntry } from '../../../../../../../core/shared/external-source-entry.model';
import { MetadataValue } from '../../../../../../../core/shared/metadata.models'; import { MetadataValue } from '../../../../../../../core/shared/metadata.models';
import { Metadata } from '../../../../../../../core/shared/metadata.utils'; import { Metadata } from '../../../../../../../core/shared/metadata.utils';
@@ -15,14 +15,16 @@ import { CollectionElementLinkType } from '../../../../../../object-collection/c
import { Context } from '../../../../../../../core/shared/context.model'; import { Context } from '../../../../../../../core/shared/context.model';
import { SelectableListService } from '../../../../../../object-list/selectable-list/selectable-list.service'; import { SelectableListService } from '../../../../../../object-list/selectable-list/selectable-list.service';
import { ListableObject } from '../../../../../../object-collection/shared/listable-object.model'; import { ListableObject } from '../../../../../../object-collection/shared/listable-object.model';
import { Collection } from '../../../../../../../core/shared/collection.model';
import { ItemDataService } from '../../../../../../../core/data/item-data.service'; import { ItemDataService } from '../../../../../../../core/data/item-data.service';
import { PaginationComponentOptions } from '../../../../../../pagination/pagination-component-options.model'; import { PaginationComponentOptions } from '../../../../../../pagination/pagination-component-options.model';
import { getRemoteDataPayload, getFirstSucceededRemoteData } from '../../../../../../../core/shared/operators'; import { getFirstSucceededRemoteData, getRemoteDataPayload } from '../../../../../../../core/shared/operators';
import { take } from 'rxjs/operators'; import { mergeMap, take } from 'rxjs/operators';
import { ItemSearchResult } from '../../../../../../object-collection/shared/item-search-result.model'; import { ItemSearchResult } from '../../../../../../object-collection/shared/item-search-result.model';
import { NotificationsService } from '../../../../../../notifications/notifications.service'; import { NotificationsService } from '../../../../../../notifications/notifications.service';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { ItemType } from '../../../../../../../core/shared/item-relationships/item-type.model';
import { SubmissionImportExternalCollectionComponent } from '../../../../../../../submission/import-external/import-external-collection/submission-import-external-collection.component';
import { CollectionListEntry } from '../../../../../../collection-dropdown/collection-dropdown.component';
/** /**
* The possible types of import for the external entry * The possible types of import for the external entry
@@ -67,16 +69,6 @@ export class ExternalSourceEntryImportModalComponent implements OnInit {
*/ */
item: Item; item: Item;
/**
* The collection the user is submitting in
*/
collection: Collection;
/**
* The ID of the collection to import entries to
*/
collectionId: string;
/** /**
* The current relationship-options used for filtering results * The current relationship-options used for filtering results
*/ */
@@ -147,8 +139,19 @@ export class ExternalSourceEntryImportModalComponent implements OnInit {
*/ */
authorityEnabled = false; authorityEnabled = false;
/**
* The entity types compatible with the given external source
*/
relatedEntityType: ItemType;
/**
* The modal for the collection selection
*/
modalRef: NgbModalRef;
constructor(public modal: NgbActiveModal, constructor(public modal: NgbActiveModal,
public lookupRelationService: LookupRelationService, public lookupRelationService: LookupRelationService,
private modalService: NgbModal,
private selectService: SelectableListService, private selectService: SelectableListService,
private itemService: ItemDataService, private itemService: ItemDataService,
private notificationsService: NotificationsService, private notificationsService: NotificationsService,
@@ -160,7 +163,6 @@ export class ExternalSourceEntryImportModalComponent implements OnInit {
const pagination = Object.assign(new PaginationComponentOptions(), { id: 'external-entry-import', pageSize: 5 }); const pagination = Object.assign(new PaginationComponentOptions(), { id: 'external-entry-import', pageSize: 5 });
this.searchOptions = Object.assign(new PaginatedSearchOptions({ query: this.externalSourceEntry.value, pagination: pagination })); this.searchOptions = Object.assign(new PaginatedSearchOptions({ query: this.externalSourceEntry.value, pagination: pagination }));
this.localEntitiesRD$ = this.lookupRelationService.getLocalResults(this.relationship, this.searchOptions); this.localEntitiesRD$ = this.lookupRelationService.getLocalResults(this.relationship, this.searchOptions);
this.collectionId = this.collection.id;
} }
/** /**
@@ -211,10 +213,19 @@ export class ExternalSourceEntryImportModalComponent implements OnInit {
* Create and import a new entity from the external entry * Create and import a new entity from the external entry
*/ */
importNewEntity() { importNewEntity() {
this.itemService.importExternalSourceEntry(this.externalSourceEntry, this.collectionId).pipe( this.modalRef = this.modalService.open(SubmissionImportExternalCollectionComponent, {
getFirstSucceededRemoteData(), size: 'lg',
getRemoteDataPayload(), });
take(1) this.modalRef.componentInstance.entityType = this.relatedEntityType.label;
this.modalRef.componentInstance.selectedEvent.pipe(
mergeMap((collectionListEntry: CollectionListEntry) => {
return this.itemService.importExternalSourceEntry(this.externalSourceEntry, collectionListEntry.collection.id).pipe(
getFirstSucceededRemoteData(),
getRemoteDataPayload(),
take(1)
);
})
).subscribe((item: Item) => { ).subscribe((item: Item) => {
this.lookupRelationService.removeLocalResultsCache(); this.lookupRelationService.removeLocalResultsCache();
const searchResult = Object.assign(new ItemSearchResult(), { const searchResult = Object.assign(new ItemSearchResult(), {

View File

@@ -11,6 +11,9 @@ export const externalSourceOrcid: ExternalSource = {
entries: { entries: {
href: 'https://dspace7.4science.cloud/server/api/integration/externalsources/orcid/entries' href: 'https://dspace7.4science.cloud/server/api/integration/externalsources/orcid/entries'
}, },
entityTypes: {
href: 'https://dspace7.4science.cloud/server/api/integration/externalsources/my_staff_db/entityTypes'
},
self: { self: {
href: 'https://dspace7.4science.cloud/server/api/integration/externalsources/orcid' href: 'https://dspace7.4science.cloud/server/api/integration/externalsources/orcid'
} }
@@ -26,6 +29,9 @@ export const externalSourceCiencia: ExternalSource = {
entries: { entries: {
href: 'https://dspace7.4science.cloud/server/api/integration/externalsources/ciencia/entries' href: 'https://dspace7.4science.cloud/server/api/integration/externalsources/ciencia/entries'
}, },
entityTypes: {
href: 'https://dspace7.4science.cloud/server/api/integration/externalsources/my_staff_db/entityTypes'
},
self: { self: {
href: 'https://dspace7.4science.cloud/server/api/integration/externalsources/ciencia' href: 'https://dspace7.4science.cloud/server/api/integration/externalsources/ciencia'
} }
@@ -41,6 +47,9 @@ export const externalSourceMyStaffDb: ExternalSource = {
entries: { entries: {
href: 'https://dspace7.4science.cloud/server/api/integration/externalsources/my_staff_db/entries' href: 'https://dspace7.4science.cloud/server/api/integration/externalsources/my_staff_db/entries'
}, },
entityTypes: {
href: 'https://dspace7.4science.cloud/server/api/integration/externalsources/my_staff_db/entityTypes'
},
self: { self: {
href: 'https://dspace7.4science.cloud/server/api/integration/externalsources/my_staff_db' href: 'https://dspace7.4science.cloud/server/api/integration/externalsources/my_staff_db'
} }

View File

@@ -1193,6 +1193,8 @@
"dso-selector.placeholder": "Search for a {{ type }}", "dso-selector.placeholder": "Search for a {{ type }}",
"dso-selector.select.collection.head": "Select a collection",
"confirmation-modal.export-metadata.header": "Export metadata for {{ dsoName }}", "confirmation-modal.export-metadata.header": "Export metadata for {{ dsoName }}",
@@ -3730,6 +3732,7 @@
"vocabulary-treeview.tree.description.srsc": "Research Subject Categories", "vocabulary-treeview.tree.description.srsc": "Research Subject Categories",
"unset.listelement.badge": "Untyped",
"uploader.browse": "browse", "uploader.browse": "browse",